2024-03-14 00:51:05 +01:00
# define _XOPEN_SOURCE 600
2024-03-14 11:52:01 +01:00
2024-03-09 18:37:04 +01:00
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
2024-03-09 19:24:38 +01:00
# include <dirent.h> /* directories etc. */
2024-03-11 22:54:14 +01:00
# include <sys/stat.h>
2024-03-13 20:37:26 +01:00
# include <errno.h>
2024-03-14 00:51:05 +01:00
# include <ftw.h>
2024-03-11 22:54:14 +01:00
# include <time.h>
2024-03-09 18:37:04 +01:00
# include <ncurses.h>
2024-03-09 20:14:26 +01:00
# include "file.h"
2024-03-09 18:37:04 +01:00
# include "util.h"
2024-03-13 20:19:22 +01:00
# include "config.h"
2024-03-09 19:24:38 +01:00
/* functions' definitions */
2024-03-20 17:03:54 +01:00
void show_help ( ) ;
2024-03-20 13:28:08 +01:00
void start_ccc ( ) ;
2024-03-16 22:46:21 +01:00
void change_dir ( const char * buf , int selection ) ;
2024-03-14 15:46:06 +01:00
int mkdir_p ( const char * destdir ) ;
2024-03-16 22:09:47 +01:00
void populate_files ( const char * path , int ftype ) ;
2024-03-14 00:51:05 +01:00
int get_directory_size ( const char * fpath , const struct stat * sb , int typeflag , struct FTW * ftwbuf ) ;
2024-03-20 21:29:04 +01:00
void add_file_stat ( char * filepath , int ftype ) ;
2024-03-09 18:37:04 +01:00
void highlight_current_line ( ) ;
void show_file_content ( ) ;
2024-03-11 16:57:41 +01:00
void edit_file ( ) ;
2024-03-19 23:07:05 +01:00
int write_last_d ( ) ;
2024-03-14 15:46:06 +01:00
void wpprintw ( const char * line ) ;
2024-03-09 18:37:04 +01:00
void init_windows ( ) ;
2024-03-17 23:58:31 +01:00
void draw_border_title ( WINDOW * window , bool active ) ;
2024-03-09 18:37:04 +01:00
2024-03-09 19:24:38 +01:00
/* global variables */
2024-03-09 18:37:04 +01:00
unsigned int focus = 0 ;
2024-03-10 21:23:15 +01:00
long current_selection = 0 ;
2024-03-16 21:37:56 +01:00
bool dirs_size = DIRS_SIZE ;
2024-03-20 19:21:02 +01:00
bool show_hidden = SHOW_HIDDEN ;
2024-03-20 21:29:04 +01:00
bool file_details = SHOW_DETAILS ;
2024-03-12 21:59:36 +01:00
char * cwd ;
2024-03-20 15:45:01 +01:00
char * p_cwd ; /* previous cwd */
2024-03-09 19:39:29 +01:00
int half_width ;
2024-03-20 21:29:04 +01:00
ArrayList * files ;
ArrayList * marked ;
2024-03-12 21:59:36 +01:00
WINDOW * directory_border ;
WINDOW * directory_content ;
WINDOW * preview_border ;
WINDOW * preview_content ;
WINDOW * panel ;
2024-03-09 18:37:04 +01:00
2024-03-14 00:51:05 +01:00
unsigned long total_dir_size ;
2024-03-09 18:37:04 +01:00
int main ( int argc , char * * argv )
{
2024-03-10 22:38:26 +01:00
if ( argc > 1 & & strcmp ( argv [ 1 ] , " -h " ) = = 0 )
2024-03-10 21:23:15 +01:00
die ( " Usage: ccc filename " ) ;
2024-03-10 11:54:08 +01:00
/* check if it is interactive shell */
2024-03-09 18:37:04 +01:00
if ( ! isatty ( STDIN_FILENO ) )
die ( " ccc: No tty detected. ccc requires an interactive shell to run. \n " ) ;
2024-03-09 19:24:38 +01:00
2024-03-10 21:23:15 +01:00
/* initialize screen, don't print special chars,
2024-03-14 13:46:38 +01:00
* make ctrl + c work , don ' t show cursor
* enable arrow keys */
2024-03-09 18:37:04 +01:00
initscr ( ) ;
noecho ( ) ;
2024-03-10 21:23:15 +01:00
cbreak ( ) ;
2024-03-09 18:37:04 +01:00
curs_set ( 0 ) ;
2024-03-14 11:52:01 +01:00
keypad ( stdscr , TRUE ) ;
2024-03-09 18:37:04 +01:00
2024-03-10 11:54:08 +01:00
/* check terminal has colors */
if ( ! has_colors ( ) ) {
2024-03-09 18:37:04 +01:00
endwin ( ) ;
die ( " ccc: Color is not supported in your terminal. \n " ) ;
2024-03-10 11:54:08 +01:00
} else {
2024-03-13 11:18:35 +01:00
use_default_colors ( ) ;
2024-03-09 19:24:38 +01:00
start_color ( ) ;
}
2024-03-14 15:58:03 +01:00
/* colors */
init_pair ( 1 , COLOR_BLACK , - 1 ) ; /* */
init_pair ( 2 , COLOR_RED , - 1 ) ; /* */
init_pair ( 3 , COLOR_GREEN , - 1 ) ; /* LNK */
init_pair ( 4 , COLOR_YELLOW , - 1 ) ; /* BLK */
init_pair ( 5 , COLOR_BLUE , - 1 ) ; /* DIR */
init_pair ( 6 , COLOR_MAGENTA , - 1 ) ; /* */
2024-03-15 19:28:37 +01:00
init_pair ( 7 , COLOR_CYAN , - 1 ) ; /* MARKED FILES */
2024-03-14 15:58:03 +01:00
init_pair ( 8 , COLOR_WHITE , - 1 ) ; /* REG */
2024-03-09 19:24:38 +01:00
2024-03-20 21:29:04 +01:00
/* init files and marked arrays */
files = arraylist_init ( 100 ) ;
marked = arraylist_init ( 100 ) ;
2024-03-12 21:59:36 +01:00
cwd = memalloc ( PATH_MAX * sizeof ( char ) ) ;
2024-03-20 15:45:01 +01:00
p_cwd = memalloc ( PATH_MAX * sizeof ( char ) ) ;
2024-03-12 21:59:36 +01:00
getcwd ( cwd , PATH_MAX ) ;
2024-03-20 13:28:08 +01:00
start_ccc ( ) ;
2024-03-09 19:24:38 +01:00
2024-03-14 11:52:01 +01:00
int ch , ch2 ;
2024-03-10 11:54:08 +01:00
while ( 1 ) {
if ( COLS < 80 | | LINES < 24 ) {
2024-03-09 18:37:04 +01:00
endwin ( ) ;
die ( " ccc: Terminal size needs to be at least 80x24 \n " ) ;
}
ch = getch ( ) ;
2024-03-14 20:56:36 +01:00
/* printf("%d ",ch); */
2024-03-10 11:54:08 +01:00
switch ( ch ) {
2024-03-12 21:59:36 +01:00
/* quit */
2024-03-10 21:23:15 +01:00
case ' q ' :
2024-03-19 23:07:05 +01:00
if ( write_last_d ( ) = = - 1 ) {
/* prompt user so error message can be shown to user */
getch ( ) ;
}
2024-03-10 21:23:15 +01:00
endwin ( ) ;
return 0 ;
2024-03-12 21:59:36 +01:00
2024-03-14 15:46:06 +01:00
/* reload using z */
case ' z ' :
2024-03-16 22:46:21 +01:00
change_dir ( cwd , 0 ) ;
2024-03-09 18:37:04 +01:00
break ;
2024-03-12 21:59:36 +01:00
2024-03-14 11:52:01 +01:00
/* go back by backspace or h or left arrow */
2024-03-14 20:47:46 +01:00
case BACKSPACE :
case LEFT :
2024-03-14 13:46:38 +01:00
case ' h ' : ;
2024-03-12 21:59:36 +01:00
/* get parent directory */
2024-03-20 15:45:01 +01:00
strcpy ( p_cwd , cwd ) ;
2024-03-12 21:59:36 +01:00
char * last_slash = strrchr ( cwd , ' / ' ) ;
if ( last_slash ! = NULL ) {
* last_slash = ' \0 ' ;
2024-03-16 22:46:21 +01:00
change_dir ( cwd , 0 ) ;
2024-03-12 21:59:36 +01:00
}
2024-03-10 21:23:15 +01:00
break ;
2024-03-12 21:59:36 +01:00
2024-03-14 11:52:01 +01:00
/* enter directory/open a file using enter or l or right arrow */
2024-03-14 20:47:46 +01:00
case ENTER :
case RIGHT :
2024-03-12 21:59:36 +01:00
case ' l ' : ;
2024-03-20 15:45:01 +01:00
strcpy ( p_cwd , cwd ) ;
2024-03-20 21:29:04 +01:00
file c_file = files - > items [ current_selection ] ;
/* check if it is directory or a regular file */
if ( strncmp ( c_file . type , " DIR " , 3 ) = = 0 ) {
/* change cwd to directory */
change_dir ( c_file . path , 0 ) ;
} else if ( strncmp ( c_file . type , " REG " , 3 ) = = 0 ) {
edit_file ( ) ;
2024-03-12 21:59:36 +01:00
}
break ;
2024-03-12 22:01:11 +01:00
2024-03-16 21:37:56 +01:00
/* jump up (ctrl u) */
2024-03-14 20:47:46 +01:00
case CTRLU :
2024-03-11 21:24:54 +01:00
if ( ( current_selection - JUMP_NUM ) > 0 )
current_selection - = JUMP_NUM ;
else
current_selection = 0 ;
highlight_current_line ( ) ;
break ;
2024-03-12 22:01:11 +01:00
2024-03-14 11:52:01 +01:00
/* go up by k or up arrow */
2024-03-14 20:47:46 +01:00
case UP :
2024-03-09 18:37:04 +01:00
case ' k ' :
2024-03-10 21:23:15 +01:00
if ( current_selection > 0 )
2024-03-09 18:37:04 +01:00
current_selection - - ;
2024-03-10 21:23:15 +01:00
2024-03-11 21:24:54 +01:00
highlight_current_line ( ) ;
break ;
2024-03-13 16:51:52 +01:00
2024-03-16 21:37:56 +01:00
/* jump down (ctrl d) */
2024-03-14 20:47:46 +01:00
case CTRLD :
2024-03-20 21:29:04 +01:00
if ( ( current_selection + JUMP_NUM ) < ( files - > length - 1 ) )
2024-03-11 21:24:54 +01:00
current_selection + = JUMP_NUM ;
else
2024-03-20 21:29:04 +01:00
current_selection = ( files - > length - 1 ) ;
2024-03-11 21:24:54 +01:00
2024-03-09 18:37:04 +01:00
highlight_current_line ( ) ;
break ;
2024-03-13 16:51:52 +01:00
2024-03-14 11:52:01 +01:00
/* go down by j or down arrow */
2024-03-14 20:47:46 +01:00
case DOWN :
2024-03-09 18:37:04 +01:00
case ' j ' :
2024-03-20 21:29:04 +01:00
if ( current_selection < ( files - > length - 1 ) )
2024-03-09 18:37:04 +01:00
current_selection + + ;
2024-03-10 21:23:15 +01:00
2024-03-09 18:37:04 +01:00
highlight_current_line ( ) ;
break ;
2024-03-13 16:51:52 +01:00
2024-03-10 21:23:15 +01:00
/* jump to the bottom */
2024-03-09 20:52:22 +01:00
case ' G ' :
2024-03-20 21:29:04 +01:00
current_selection = ( files - > length - 1 ) ;
2024-03-09 20:52:22 +01:00
highlight_current_line ( ) ;
break ;
2024-03-13 16:51:52 +01:00
2024-03-10 21:23:15 +01:00
/* jump to the top */
case ' g ' :
2024-03-14 11:52:01 +01:00
ch2 = getch ( ) ;
switch ( ch2 ) {
2024-03-10 21:23:15 +01:00
case ' g ' :
current_selection = 0 ;
highlight_current_line ( ) ;
break ;
default :
break ;
}
break ;
2024-03-14 11:52:01 +01:00
2024-03-17 11:09:22 +01:00
/* '~' go to $HOME */
2024-03-14 20:47:46 +01:00
case TILDE : ;
2024-03-14 13:46:38 +01:00
char * home = getenv ( " HOME " ) ;
if ( home = = NULL ) {
2024-03-14 15:46:06 +01:00
wpprintw ( " $HOME is not defined " ) ;
2024-03-14 13:46:38 +01:00
} else {
2024-03-16 22:46:21 +01:00
change_dir ( home , 0 ) ;
2024-03-14 15:46:06 +01:00
}
break ;
2024-03-16 21:59:49 +01:00
/* go to the trash dir */
2024-03-14 15:46:06 +01:00
case ' t ' : ;
2024-03-16 22:46:21 +01:00
# ifdef TRASH_DIR
char * trash_dir = TRASH_DIR ;
# else
char * trash_dir = getenv ( " CCC_TRASH " ) ;
# endif
2024-03-14 15:46:06 +01:00
if ( trash_dir = = NULL ) {
wpprintw ( " $CCC_TRASH is not defined " ) ;
} else {
if ( access ( trash_dir , F_OK ) ! = 0 ) {
/* create the directory with 755 perm if it doesn't exit */
if ( mkdir_p ( trash_dir ) = = - 1 ) {
switch ( errno ) {
case EACCES :
2024-03-16 22:46:21 +01:00
wpprintw ( " Parent directory does not allow write permission or one of directories does not allow search access " ) ;
2024-03-14 15:46:06 +01:00
}
}
}
2024-03-16 22:46:21 +01:00
change_dir ( trash_dir , 0 ) ;
2024-03-14 13:46:38 +01:00
}
break ;
2024-03-16 21:59:49 +01:00
/* show directories' sizes */
2024-03-16 22:46:21 +01:00
case ' A ' :
2024-03-16 21:37:56 +01:00
dirs_size = ! dirs_size ;
2024-03-20 21:29:04 +01:00
change_dir ( cwd , 0 ) ;
2024-03-16 20:26:35 +01:00
break ;
2024-03-20 15:45:01 +01:00
/* go to previous dir */
case ' - ' :
change_dir ( p_cwd , 0 ) ;
break ;
2024-03-20 19:21:02 +01:00
/* show help */
2024-03-20 17:03:54 +01:00
case ' ? ' :
show_help ( ) ;
break ;
2024-03-20 19:21:02 +01:00
2024-03-20 21:29:04 +01:00
/* toggle hidden files */
2024-03-20 19:21:02 +01:00
case ' . ' :
show_hidden = ! show_hidden ;
2024-03-20 21:29:04 +01:00
change_dir ( cwd , 0 ) ;
2024-03-20 19:21:02 +01:00
break ;
2024-03-20 17:03:54 +01:00
2024-03-20 21:29:04 +01:00
/* toggle file details */
case ' i ' :
file_details = ! file_details ;
change_dir ( cwd , 0 ) ;
2024-03-16 22:09:47 +01:00
/* mark one file */
2024-03-18 00:21:58 +01:00
case SPACE :
2024-03-20 21:29:04 +01:00
add_file_stat ( files - > items [ current_selection ] . path , 1 ) ;
2024-03-14 20:56:36 +01:00
highlight_current_line ( ) ;
2024-03-11 16:57:41 +01:00
break ;
2024-03-16 22:09:47 +01:00
/* mark all files in directory */
2024-03-18 00:21:58 +01:00
case ' a ' :
populate_files ( cwd , 2 ) ;
change_dir ( cwd , current_selection ) ; /* reload current dir */
2024-03-16 22:09:47 +01:00
break ;
2024-03-17 13:28:27 +01:00
/* mark actions: */
/* delete */
case ' d ' : ;
2024-03-20 21:29:04 +01:00
if ( marked - > length ) {
2024-03-17 13:28:27 +01:00
;
}
break ;
/* move */
case ' m ' : ;
2024-03-20 21:29:04 +01:00
if ( marked - > length ) {
2024-03-17 13:28:27 +01:00
;
}
break ;
/* copy */
case ' c ' : ;
2024-03-20 21:29:04 +01:00
if ( marked - > length ) {
2024-03-17 13:28:27 +01:00
;
}
break ;
/* symbolic link */
case ' s ' : ;
2024-03-20 21:29:04 +01:00
if ( marked - > length ) {
2024-03-17 13:28:27 +01:00
;
}
break ;
/* bulk rename */
case ' b ' : ;
2024-03-20 21:29:04 +01:00
if ( marked - > length ) {
2024-03-17 13:28:27 +01:00
;
}
break ;
2024-03-14 11:52:01 +01:00
/* escape */
2024-03-14 20:47:46 +01:00
case ESC :
2024-03-09 18:37:04 +01:00
break ;
2024-03-14 11:52:01 +01:00
2024-03-09 18:37:04 +01:00
case KEY_RESIZE :
2024-03-17 23:58:31 +01:00
delwin ( directory_border ) ;
delwin ( directory_content ) ;
delwin ( preview_border ) ;
delwin ( preview_content ) ;
delwin ( panel ) ;
2024-03-13 11:18:35 +01:00
endwin ( ) ;
2024-03-20 13:28:08 +01:00
start_ccc ( ) ;
2024-03-09 18:37:04 +01:00
break ;
2024-03-10 21:23:15 +01:00
default :
break ;
2024-03-09 18:37:04 +01:00
}
}
2024-03-20 21:29:04 +01:00
arraylist_free ( files ) ;
arraylist_free ( marked ) ;
2024-03-10 21:23:15 +01:00
endwin ( ) ;
2024-03-09 18:37:04 +01:00
return 0 ;
}
2024-03-20 21:29:04 +01:00
void show_help ( )
{
2024-03-20 17:03:54 +01:00
wclear ( directory_content ) ;
wclear ( preview_content ) ;
2024-03-20 21:29:04 +01:00
wprintw ( directory_content , " h: go to parent dir \n j: scroll down \n k: scroll up \n l: go to child dir \n \n left: go to parent dir \n down: scroll down \n up: scroll up \n right: go to child dir \n \n enter: go to child dir/open file \n backspace: go to parent dir \n \n gg: go to top \n G: go to bottom \n \n ctrl+u: jump up \n ctrl+d: jump down \n \n t: go to trash dir \n ~: go to home dir \n -: go to previous dir \n z: refresh current dir \n \n A: show directory disk usage/block size \n space: mark file \n a: mark all files in directory \n \n ?: show help \n q: exit with last dir written to file \n ctrl+c exit without writing last dir " ) ;
2024-03-20 17:03:54 +01:00
wpprintw ( " Visit https://github.com/piotr-marendowski/ccc or use 'man ccc' for help " ) ;
wrefresh ( directory_content ) ;
wrefresh ( preview_content ) ;
}
2024-03-20 13:28:08 +01:00
void start_ccc ( )
{
half_width = COLS / 2 ;
init_windows ( ) ;
refresh ( ) ;
populate_files ( cwd , 0 ) ;
highlight_current_line ( ) ;
}
2024-03-14 13:46:38 +01:00
/*
2024-03-16 22:46:21 +01:00
* Change directory in window with selection
2024-03-14 13:46:38 +01:00
*/
2024-03-16 22:46:21 +01:00
void change_dir ( const char * buf , int selection )
2024-03-14 13:46:38 +01:00
{
2024-03-20 15:45:01 +01:00
char * buf_dup ;
if ( buf = = p_cwd ) {
buf_dup = strdup ( p_cwd ) ;
if ( buf_dup = = NULL ) {
return ;
}
} else {
buf_dup = ( char * ) buf ;
}
strcpy ( cwd , buf_dup ) ;
2024-03-20 21:29:04 +01:00
arraylist_free ( files ) ;
files = arraylist_init ( 100 ) ;
2024-03-16 22:09:47 +01:00
populate_files ( cwd , 0 ) ;
2024-03-16 22:46:21 +01:00
current_selection = selection ;
2024-03-14 13:46:38 +01:00
highlight_current_line ( ) ;
}
2024-03-14 15:46:06 +01:00
/*
* Recursively create directory by creating each subdirectory
2024-03-19 23:07:05 +01:00
* like mkdir - p
2024-03-14 15:46:06 +01:00
*/
int mkdir_p ( const char * destdir )
{
char * path = memalloc ( PATH_MAX * sizeof ( char ) ) ;
2024-03-16 21:37:56 +01:00
char dir_path [ PATH_MAX ] = " " ;
2024-03-14 15:46:06 +01:00
if ( destdir [ 0 ] = = ' ~ ' ) {
char * home = getenv ( " HOME " ) ;
if ( home = = NULL ) {
wpprintw ( " $HOME is not defined " ) ;
return - 1 ;
}
/* replace ~ with home */
2024-03-19 23:07:05 +01:00
snprintf ( path , PATH_MAX , " %s%s " , home , destdir + 1 ) ;
} else {
strcpy ( path , destdir ) ;
}
2024-03-16 21:37:56 +01:00
/* fix first / not appearing in the string */
if ( path [ 0 ] = = ' / ' )
2024-03-14 15:46:06 +01:00
strcat ( dir_path , " / " ) ;
2024-03-19 23:07:05 +01:00
char * token = strtok ( path , " / " ) ;
2024-03-14 15:46:06 +01:00
while ( token ! = NULL ) {
strcat ( dir_path , token ) ;
strcat ( dir_path , " / " ) ;
if ( mkdir ( dir_path , 0755 ) = = - 1 ) {
struct stat st ;
if ( stat ( dir_path , & st ) = = 0 & & S_ISDIR ( st . st_mode ) ) {
/* Directory already exists, continue to the next dir */
token = strtok ( NULL , " / " ) ;
continue ;
}
perror ( " ccc " ) ;
free ( path ) ;
return - 1 ;
}
token = strtok ( NULL , " / " ) ;
}
free ( path ) ;
return 0 ;
}
2024-03-09 20:14:26 +01:00
/*
2024-03-14 16:29:03 +01:00
* Read the provided directory and add all files in directory to linked list
2024-03-18 00:21:58 +01:00
* ftype : normal files = 0 , marked = 1 , marking ALL = 2
2024-03-10 21:23:15 +01:00
* ep - > d_name - > filename
2024-03-09 20:14:26 +01:00
*/
2024-03-16 22:09:47 +01:00
void populate_files ( const char * path , int ftype )
2024-03-09 18:37:04 +01:00
{
DIR * dp ;
struct dirent * ep ;
2024-03-09 19:24:38 +01:00
2024-03-17 13:28:27 +01:00
# if DRAW_BORDERS
2024-03-17 23:58:31 +01:00
draw_border_title ( directory_border , true ) ;
2024-03-17 13:28:27 +01:00
# endif
2024-03-13 11:18:35 +01:00
if ( ( dp = opendir ( path ) ) ! = NULL ) {
2024-03-12 21:59:36 +01:00
/* clear directory window to ready for printing */
wclear ( directory_content ) ;
2024-03-10 21:23:15 +01:00
2024-03-10 11:54:08 +01:00
while ( ( ep = readdir ( dp ) ) ! = NULL ) {
2024-03-12 21:59:36 +01:00
char * filename = memalloc ( PATH_MAX * sizeof ( char ) ) ;
/* make filename be basename of selected item just to pass check */
filename [ 0 ] = ' \0 ' ;
strcat ( filename , ep - > d_name ) ;
2024-03-20 19:21:02 +01:00
/* use strncmp to filter out dotfiles */
if ( ( show_hidden & & strncmp ( filename , " . " , 1 ) & & strncmp ( filename , " .. " , 2 ) ) | | ( ! show_hidden & & strcmp ( filename , " . " ) & & strcmp ( filename , " .. " ) ) ) {
2024-03-12 21:59:36 +01:00
/* construct full file path */
filename [ 0 ] = ' \0 ' ;
strcat ( filename , cwd ) ;
strcat ( filename , " / " ) ;
2024-03-16 21:37:56 +01:00
strcat ( filename , ep - > d_name ) ; /* add filename */
2024-03-12 21:59:36 +01:00
2024-03-16 22:09:47 +01:00
add_file_stat ( filename , ftype ) ;
2024-03-09 18:37:04 +01:00
}
2024-03-11 22:54:14 +01:00
free ( filename ) ;
2024-03-09 18:37:04 +01:00
}
closedir ( dp ) ;
2024-03-12 21:59:36 +01:00
wrefresh ( directory_content ) ;
2024-03-10 11:54:08 +01:00
} else {
2024-03-09 18:37:04 +01:00
perror ( " ccc " ) ;
}
}
2024-03-14 00:51:05 +01:00
int get_directory_size ( const char * fpath , const struct stat * sb , int typeflag , struct FTW * ftwbuf )
2024-03-13 11:18:35 +01:00
{
2024-03-14 00:51:05 +01:00
total_dir_size + = sb - > st_size ;
return 0 ;
2024-03-13 11:18:35 +01:00
}
2024-03-11 22:54:14 +01:00
/*
2024-03-13 11:18:35 +01:00
* Get file ' s last modified time , size , type
* Add that file into list
2024-03-18 00:21:58 +01:00
* ftype : normal file = 0 , normal marked = 1 , marking ALL = 2
2024-03-11 22:54:14 +01:00
*/
2024-03-20 21:29:04 +01:00
void add_file_stat ( char * filepath , int ftype )
2024-03-12 21:59:36 +01:00
{
2024-03-11 22:54:14 +01:00
struct stat file_stat ;
2024-03-13 20:37:26 +01:00
if ( stat ( filepath , & file_stat ) = = - 1 ) {
2024-03-16 21:59:49 +01:00
/* can't be triggered? */
if ( errno = = EACCES )
2024-03-20 21:29:04 +01:00
arraylist_add ( files , filepath , " " , " " , 8 , false , false ) ;
2024-03-16 21:59:49 +01:00
}
2024-03-16 22:46:21 +01:00
/* get file type and color, 4 chars for the type */
char * type = memalloc ( 4 * sizeof ( char ) ) ;
2024-03-16 21:59:49 +01:00
int color ;
if ( S_ISDIR ( file_stat . st_mode ) ) {
strcpy ( type , " DIR " ) ; /* directory type */
color = 5 ; /* blue color */
} else if ( S_ISREG ( file_stat . st_mode ) ) {
strcpy ( type , " REG " ) ; /* regular file */
color = 8 ; /* white color */
} else if ( S_ISLNK ( file_stat . st_mode ) ) {
strcpy ( type , " LNK " ) ; /* symbolic link */
color = 3 ; /* green color */
} else if ( S_ISCHR ( file_stat . st_mode ) ) {
strcpy ( type , " CHR " ) ; /* character device */
color = 8 ; /* white color */
} else if ( S_ISSOCK ( file_stat . st_mode ) ) {
strcpy ( type , " SOC " ) ; /* socket */
color = 8 ; /* white color */
} else if ( S_ISBLK ( file_stat . st_mode ) ) {
strcpy ( type , " BLK " ) ; /* block device */
color = 4 ; /* yellow color */
} else if ( S_ISFIFO ( file_stat . st_mode ) ) {
strcpy ( type , " FIF " ) ; /* FIFO */
color = 8 ; /* white color */
} else {
color = 8 ; /* white color */
}
/* if file is to be marked */
2024-03-18 00:21:58 +01:00
if ( ftype = = 1 | | ftype = = 2 ) {
/* force if user is marking all files */
bool force = ftype = = 2 ? true : false ;
2024-03-20 21:29:04 +01:00
arraylist_add ( marked , filepath , NULL , type , 8 , true , force ) ;
2024-03-17 11:09:22 +01:00
/* free type and return without allocating more stuff */
free ( type ) ;
2024-03-20 21:29:04 +01:00
return ;
2024-03-13 20:37:26 +01:00
}
2024-03-11 22:54:14 +01:00
2024-03-13 11:18:35 +01:00
/* get last modified time */
2024-03-11 22:54:14 +01:00
char * time = memalloc ( 20 * sizeof ( char ) ) ;
2024-03-12 21:59:36 +01:00
/* format last modified time to a string */
strftime ( time , 20 , " %Y-%m-%d %H:%M " , localtime ( & file_stat . st_mtime ) ) ;
2024-03-11 22:54:14 +01:00
2024-03-13 11:18:35 +01:00
/* get file size */
2024-03-11 22:54:14 +01:00
double bytes = file_stat . st_size ;
2024-03-16 20:26:35 +01:00
2024-03-16 21:37:56 +01:00
if ( dirs_size ) {
/* dirs_size is true, so calculate disk usage */
2024-03-16 20:26:35 +01:00
if ( S_ISDIR ( file_stat . st_mode ) ) {
/* at most 15 fd opened */
total_dir_size = 0 ;
nftw ( filepath , & get_directory_size , 15 , FTW_PHYS ) ;
bytes = total_dir_size ;
}
2024-03-13 11:18:35 +01:00
}
2024-03-14 15:58:03 +01:00
/* max 25 chars due to long, space, suffix and null */
2024-03-13 11:18:35 +01:00
char * size = memalloc ( 25 * sizeof ( char ) ) ;
2024-03-11 22:54:14 +01:00
int unit = 0 ;
2024-03-17 23:19:19 +01:00
const char * units [ ] = { " B " , " KiB " , " MiB " , " GiB " , " TiB " , " PiB " } ;
2024-03-11 22:54:14 +01:00
while ( bytes > 1024 ) {
bytes / = 1024 ;
unit + + ;
}
2024-03-17 11:09:22 +01:00
/* display sizes */
sprintf ( size , " %.3g%s " , bytes , units [ unit ] ) ;
2024-03-15 19:28:37 +01:00
2024-03-11 22:54:14 +01:00
char * total_stat = memalloc ( 45 * sizeof ( char ) ) ;
2024-03-17 11:09:22 +01:00
snprintf ( total_stat , 45 , " %-18s %-8s " , time , size ) ;
2024-03-12 21:59:36 +01:00
total_stat [ strlen ( total_stat ) ] = ' \0 ' ;
2024-03-11 22:54:14 +01:00
2024-03-20 21:29:04 +01:00
arraylist_add ( files , filepath , total_stat , type , color , false , false ) ;
2024-03-13 11:18:35 +01:00
2024-03-17 11:09:22 +01:00
free ( time ) ;
free ( size ) ;
2024-03-13 11:18:35 +01:00
free ( total_stat ) ;
free ( type ) ;
2024-03-11 22:54:14 +01:00
}
2024-03-14 16:29:03 +01:00
2024-03-09 20:14:26 +01:00
/*
2024-03-10 21:23:15 +01:00
* Highlight current line by reversing the color
2024-03-09 20:14:26 +01:00
*/
2024-03-09 18:37:04 +01:00
void highlight_current_line ( )
{
2024-03-13 16:51:52 +01:00
long overflow = 0 ;
if ( current_selection > LINES - 4 ) {
/* overflown */
overflow = current_selection - ( LINES - 4 ) ;
}
/* calculate range of files to show */
2024-03-20 21:29:04 +01:00
long range = files - > length ;
2024-03-14 15:46:06 +01:00
/* not highlight if no files in directory */
if ( range = = 0 ) {
2024-03-17 23:58:31 +01:00
# if DRAW_PREVIEW
wprintw ( preview_content , " empty directory " ) ;
wrefresh ( preview_content ) ;
# endif
2024-03-14 15:46:06 +01:00
return ;
}
2024-03-13 16:51:52 +01:00
if ( range > LINES - 3 ) {
2024-03-16 21:37:56 +01:00
/* if there are more files than lines available to display
* shrink range to avaiable lines to display with
* overflow to keep the number of iterations to be constant */
2024-03-13 16:51:52 +01:00
range = LINES - 3 + overflow ;
}
wclear ( directory_content ) ;
long line_count = 0 ;
for ( long i = overflow ; i < range ; i + + ) {
if ( ( overflow = = 0 & & i = = current_selection ) | | ( overflow ! = 0 & & i = = current_selection ) ) {
2024-03-12 21:59:36 +01:00
wattron ( directory_content , A_REVERSE ) ;
2024-03-10 21:23:15 +01:00
/* update the panel */
2024-03-12 21:59:36 +01:00
wclear ( panel ) ;
2024-03-16 20:26:35 +01:00
/* check for marked files */
2024-03-20 21:29:04 +01:00
long num_marked = marked - > length ;
2024-03-14 20:47:46 +01:00
if ( num_marked > 0 ) {
2024-03-14 20:56:36 +01:00
/* Determine length of formatted string */
2024-03-16 22:09:47 +01:00
int m_len = snprintf ( NULL , 0 , " [%ld] selected " , num_marked ) ;
2024-03-14 20:56:36 +01:00
char * selected = memalloc ( ( m_len + 1 ) * sizeof ( char ) ) ;
2024-03-16 22:09:47 +01:00
snprintf ( selected , m_len + 1 , " [%ld] selected " , num_marked ) ;
2024-03-20 21:29:04 +01:00
wprintw ( panel , " (%ld/%ld) %s %s " , current_selection + 1 , files - > length , selected , cwd ) ;
2024-03-14 20:56:36 +01:00
} else {
2024-03-20 21:29:04 +01:00
wprintw ( panel , " (%ld/%ld) %s " , current_selection + 1 , files - > length , cwd ) ;
2024-03-14 20:47:46 +01:00
}
2024-03-09 18:37:04 +01:00
}
2024-03-11 22:54:14 +01:00
/* print the actual filename and stats */
2024-03-20 21:29:04 +01:00
char * line = get_line ( files , i , file_details ) ;
int color = files - > items [ i ] . color ;
2024-03-15 19:28:37 +01:00
/* check is file marked for action */
2024-03-20 21:29:04 +01:00
bool is_marked = arraylist_includes ( marked , files - > items [ i ] . path ) ;
if ( is_marked ) {
2024-03-15 19:28:37 +01:00
/* show file is selected */
wattron ( directory_content , COLOR_PAIR ( 7 ) ) ;
} else {
/* print the whole directory with default colors */
wattron ( directory_content , COLOR_PAIR ( color ) ) ;
}
2024-03-13 16:51:52 +01:00
if ( overflow > 0 )
mvwprintw ( directory_content , line_count , 0 , " %s " , line ) ;
else
mvwprintw ( directory_content , i , 0 , " %s " , line ) ;
2024-03-14 15:58:03 +01:00
2024-03-20 21:29:04 +01:00
if ( is_marked ) {
2024-03-15 19:28:37 +01:00
wattroff ( directory_content , COLOR_PAIR ( 7 ) ) ;
} else {
wattroff ( directory_content , COLOR_PAIR ( color ) ) ;
}
2024-03-12 21:59:36 +01:00
wattroff ( directory_content , A_REVERSE ) ;
2024-03-11 22:54:14 +01:00
free ( line ) ;
2024-03-13 16:51:52 +01:00
line_count + + ;
2024-03-09 18:37:04 +01:00
}
2024-03-09 19:24:38 +01:00
2024-03-12 21:59:36 +01:00
wrefresh ( directory_content ) ;
wrefresh ( panel ) ;
2024-03-10 21:23:15 +01:00
/* show file content every time cursor changes */
2024-03-17 13:28:27 +01:00
# if DRAW_PREVIEW
show_file_content ( ) ;
# endif
2024-03-13 20:37:26 +01:00
wrefresh ( preview_content ) ;
2024-03-09 18:37:04 +01:00
}
2024-03-09 20:14:26 +01:00
/*
2024-03-10 21:23:15 +01:00
* Get file content into buffer and show it to preview window
2024-03-09 20:14:26 +01:00
*/
2024-03-09 18:37:04 +01:00
void show_file_content ( )
{
2024-03-13 20:37:26 +01:00
wclear ( preview_content ) ;
2024-03-20 21:29:04 +01:00
file current_file = files - > items [ current_selection ] ;
2024-03-16 21:37:56 +01:00
2024-03-20 21:29:04 +01:00
if ( strncmp ( current_file . type , " DIR " , 3 ) = = 0 )
2024-03-16 21:37:56 +01:00
return ;
2024-03-20 21:29:04 +01:00
FILE * file = fopen ( current_file . path , " r " ) ;
2024-03-15 15:02:52 +01:00
if ( file = = NULL ) {
2024-03-20 21:29:04 +01:00
mvwprintw ( preview_content , 0 , 0 , " Unable to read %s " , current_file . path ) ;
2024-03-15 15:02:52 +01:00
return ;
}
2024-03-17 13:28:27 +01:00
# if DRAW_BORDERS
2024-03-17 23:58:31 +01:00
draw_border_title ( preview_border , true ) ;
2024-03-17 13:28:27 +01:00
# endif
2024-03-15 15:02:52 +01:00
int c ;
2024-03-16 21:37:56 +01:00
/* check if its binary */
while ( ( c = fgetc ( file ) ) ! = EOF ) {
2024-03-15 15:02:52 +01:00
if ( c = = ' \0 ' ) {
mvwprintw ( preview_content , 0 , 0 , " binary " ) ;
return ;
2024-03-10 21:23:15 +01:00
}
2024-03-09 18:37:04 +01:00
}
2024-03-15 15:02:52 +01:00
fseek ( file , 0 , SEEK_END ) ;
long length = ftell ( file ) ;
/* check if file isn't empty */
if ( length ! = 0 ) {
fseek ( file , 0 , SEEK_SET ) ; /* set cursor back to start of file */
int max_length = ( LINES - 3 ) * ( COLS / 2 - 2 ) ;
char * buffer = memalloc ( max_length * sizeof ( char ) ) ;
fread ( buffer , 1 , max_length , file ) ;
mvwprintw ( preview_content , 0 , 0 , " %s " , buffer ) ;
free ( buffer ) ;
} else {
wclear ( preview_content ) ;
}
fclose ( file ) ;
2024-03-09 18:37:04 +01:00
}
2024-03-11 16:57:41 +01:00
/*
* Opens $ EDITOR to edit the file
*/
2024-03-12 21:59:36 +01:00
void edit_file ( )
{
2024-03-16 21:37:56 +01:00
# ifdef EDITOR
char * editor = EDITOR ;
# else
char * editor = getenv ( " EDITOR " ) ;
# endif
2024-03-11 16:57:41 +01:00
if ( editor = = NULL ) {
2024-03-14 15:46:06 +01:00
wpprintw ( " Cannot get EDITOR variable, is it defined? " ) ;
2024-03-11 16:57:41 +01:00
return ;
} else {
2024-03-16 21:37:56 +01:00
def_prog_mode ( ) ; /* save the tty modes */
endwin ( ) ; /* end curses mode temporarily */
2024-03-20 21:29:04 +01:00
char * filename = files - > items [ current_selection ] . path ;
2024-03-16 21:37:56 +01:00
int length = strlen ( editor ) + strlen ( filename ) + 2 ; /* one for space one for null */
2024-03-11 16:57:41 +01:00
char command [ length ] ;
2024-03-16 21:37:56 +01:00
2024-03-11 16:57:41 +01:00
snprintf ( command , length , " %s %s " , editor , filename ) ;
system ( command ) ;
2024-03-16 21:37:56 +01:00
reset_prog_mode ( ) ; /* return to previous tty mode */
refresh ( ) ; /* store the screen contents */
2024-03-11 22:54:14 +01:00
free ( filename ) ;
2024-03-11 16:57:41 +01:00
}
}
2024-03-20 21:29:04 +01:00
int write_last_d ( )
{
2024-03-19 23:07:05 +01:00
# ifdef LAST_D
char * last_d = memalloc ( PATH_MAX * sizeof ( char ) ) ;
strcpy ( last_d , LAST_D ) ;
# else
char * last_d = getenv ( " CCC_LAST_D " ) ;
# endif
if ( last_d = = NULL ) {
wpprintw ( " Cannot get CCC_LAST_D variable, is it defined? " ) ;
return - 1 ;
} else {
char * last_ddup = memalloc ( PATH_MAX * sizeof ( char ) ) ;
char * home = getenv ( " HOME " ) ;
if ( home = = NULL ) {
wpprintw ( " $HOME is not defined " ) ;
return - 1 ;
}
/* replace ~ with home */
snprintf ( last_ddup , PATH_MAX , " %s%s " , home , last_d + 1 ) ;
char * last_d_dir = strrchr ( last_d , ' / ' ) ;
if ( last_d_dir ! = NULL ) {
* last_d_dir = ' \0 ' ; /* truncate string */
}
mkdir_p ( last_d ) ;
FILE * last_d_file = fopen ( last_ddup , " w " ) ;
if ( last_d_file = = NULL ) {
wpprintw ( " Cannot open last directory file " ) ;
return - 1 ;
}
fwrite ( cwd , strlen ( cwd ) , sizeof ( char ) , last_d_file ) ;
fclose ( last_d_file ) ;
}
return 0 ;
}
2024-03-14 15:46:06 +01:00
/*
2024-03-17 11:09:22 +01:00
* Print line to the panel
2024-03-14 15:46:06 +01:00
*/
void wpprintw ( const char * line )
{
wclear ( panel ) ;
wprintw ( panel , " %s " , line ) ;
wrefresh ( panel ) ;
}
2024-03-09 18:37:04 +01:00
void init_windows ( )
{
2024-03-17 13:28:27 +01:00
/* offset for width of the content 1 and 2 */
int width_left = half_width ;
int width_right = half_width ;
# ifdef WINDOW_OFFSET
width_left + = WINDOW_OFFSET ;
width_right - = WINDOW_OFFSET ;
# endif
2024-03-11 16:57:41 +01:00
/*------------------------------+
2024-03-12 21:59:36 +01:00
| - - - - border ( 0 ) - - | | - - border ( 2 ) - - | |
| | | | | |
| | content ( 1 ) | | content ( 3 ) | |
2024-03-17 13:28:27 +01:00
| | ( directory ) | | ( preview ) | |
2024-03-12 21:59:36 +01:00
| | | | | |
| - - - - - - - - - - - - - - - | | - - - - - - - - - - - - - | |
+ = = = = = = = = = = panel ( 4 ) = = = = = = = = = = = */
2024-03-09 18:37:04 +01:00
2024-03-17 23:58:31 +01:00
/* lines, cols, y, x */
panel = newwin ( PH , COLS , LINES - PH , 0 ) ;
2024-03-17 13:28:27 +01:00
/* draw border around windows */
# if DRAW_BORDERS
2024-03-17 23:58:31 +01:00
directory_border = newwin ( LINES - PH , width_left , 0 , 0 ) ;
directory_content = newwin ( LINES - PH - 2 , width_left - 2 , 1 , 1 ) ;
2024-03-17 13:28:27 +01:00
preview_border = newwin ( LINES - PH , width_right , 0 , width_left ) ;
preview_content = newwin ( LINES - PH - 2 , width_right - 2 , 1 , width_left + 1 ) ;
2024-03-09 18:37:04 +01:00
2024-03-17 23:58:31 +01:00
draw_border_title ( directory_border , true ) ;
draw_border_title ( preview_border , true ) ;
2024-03-17 13:28:27 +01:00
# else
/* if there are no borders, then draw content in their places */
2024-03-17 23:58:31 +01:00
directory_border = newwin ( 0 , 0 , COLS , LINES ) ;
preview_border = newwin ( 0 , 0 , COLS , LINES ) ;
2024-03-17 13:28:27 +01:00
/* -1 for the one space to the left */
2024-03-17 23:58:31 +01:00
directory_content = newwin ( LINES - PH - 1 , width_left , 0 , 1 ) ;
preview_content = newwin ( LINES - PH , width_right , 0 , width_left ) ;
2024-03-17 13:28:27 +01:00
# endif
2024-03-09 18:37:04 +01:00
2024-03-13 16:51:52 +01:00
scrollok ( directory_content , true ) ;
2024-03-09 18:37:04 +01:00
}
/*
2024-03-17 13:28:27 +01:00
* Draw the border of the window depending if it ' s active or not ,
2024-03-09 18:37:04 +01:00
*/
2024-03-17 23:58:31 +01:00
void draw_border_title ( WINDOW * window , bool active )
2024-03-09 18:37:04 +01:00
{
2024-03-17 13:28:27 +01:00
/* check if the window is directory of preview */
int width = half_width ;
2024-03-17 23:58:31 +01:00
if ( window = = directory_border ) { /* left */
2024-03-17 23:17:46 +01:00
width + = WINDOW_OFFSET ;
2024-03-17 23:58:31 +01:00
} else if ( window = = preview_border ) { /* right */
2024-03-17 13:28:27 +01:00
width - = WINDOW_OFFSET ;
}
2024-03-10 11:54:08 +01:00
/* turn on color depends on active */
2024-03-09 18:37:04 +01:00
if ( active ) {
2024-03-14 15:58:03 +01:00
wattron ( window , COLOR_PAIR ( 7 ) ) ;
2024-03-09 18:37:04 +01:00
} else {
2024-03-14 15:58:03 +01:00
wattron ( window , COLOR_PAIR ( 5 ) ) ;
2024-03-09 18:37:04 +01:00
}
2024-03-10 11:54:08 +01:00
/* draw top border */
mvwaddch ( window , 0 , 0 , ACS_ULCORNER ) ; /* upper left corner */
2024-03-17 13:28:27 +01:00
mvwhline ( window , 0 , 1 , ACS_HLINE , width - 2 ) ; /* top horizontal line */
mvwaddch ( window , 0 , width - 1 , ACS_URCORNER ) ; /* upper right corner */
2024-03-09 18:37:04 +01:00
2024-03-10 11:54:08 +01:00
/* draw side border */
mvwvline ( window , 1 , 0 , ACS_VLINE , LINES - 2 ) ; /* left vertical line */
2024-03-17 13:28:27 +01:00
mvwvline ( window , 1 , width - 1 , ACS_VLINE , LINES - 2 ) ; /* right vertical line */
2024-03-09 18:37:04 +01:00
2024-03-10 21:23:15 +01:00
/* draw bottom border
* make space for the panel */
mvwaddch ( window , LINES - PH - 1 , 0 , ACS_LLCORNER ) ; /* lower left corner */
2024-03-17 13:28:27 +01:00
mvwhline ( window , LINES - PH - 1 , 1 , ACS_HLINE , width - 2 ) ; /* bottom horizontal line */
mvwaddch ( window , LINES - PH - 1 , width - 1 , ACS_LRCORNER ) ; /* lower right corner */
2024-03-09 20:14:26 +01:00
2024-03-10 11:54:08 +01:00
/* turn color off after turning it on */
2024-03-09 18:37:04 +01:00
if ( active ) {
2024-03-14 15:58:03 +01:00
wattroff ( window , COLOR_PAIR ( 7 ) ) ;
2024-03-09 18:37:04 +01:00
} else {
2024-03-14 15:58:03 +01:00
wattroff ( window , COLOR_PAIR ( 5 ) ) ;
2024-03-09 18:37:04 +01:00
}
2024-03-10 11:54:08 +01:00
wrefresh ( window ) ; /* Refresh the window to see the colored border and title */
2024-03-09 18:37:04 +01:00
}