/* File: dirn.c Copyright (C) 1998-2009 Christophe GRENIER This software is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_NCURSES #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_TIME_H #include #endif #include "types.h" #include "common.h" #include "intrf.h" #include "intrfn.h" #include "dir.h" #include "log.h" #include "log_part.h" #include "dirn.h" #include "askloc.h" extern const char *monstr[]; static int dir_partition_aux(disk_t *disk, const partition_t *partition, dir_data_t *dir_data, const unsigned long int inode, const unsigned int depth, char **current_cmd); static long int dir_aff_ncurses(disk_t *disk, const partition_t *partition, dir_data_t *dir_data, const file_data_t*dir_list, const unsigned long int inode, const unsigned int depth); static int copy_dir(disk_t *disk, const partition_t *partition, dir_data_t *dir_data, const file_data_t *dir); #define INTER_DIR (LINES-25+16) static long int dir_aff_ncurses(disk_t *disk, const partition_t *partition, dir_data_t *dir_data, const file_data_t*dir_list, const unsigned long int inode, const unsigned int depth) { /* Return value * -1: quit * 1: back * other: new inode * */ int quit=0; WINDOW *window=(WINDOW*)dir_data->display; do { int offset=0; int pos_num=0; const file_data_t *current_file; const file_data_t *pos=dir_list; int old_LINES=LINES; aff_copy(window); wmove(window,3,0); aff_part(window, AFF_PART_ORDER|AFF_PART_STATUS, disk, partition); wmove(window,4,0); wprintw(window,"Directory %s\n",dir_data->current_directory); do { int i; int car; for(i=0,current_file=dir_list;(current_file!=NULL) && (inext,i++); for(i=offset;(current_file!=NULL) &&((i-offset)next) { char str[11]; char datestr[80]; wmove(window, 6+i-offset, 0); wclrtoeol(window); /* before addstr for BSD compatibility */ if(current_file==pos) wattrset(window, A_REVERSE); if((current_file->status&FILE_STATUS_DELETED)!=0 && has_colors()) wbkgdset(window,' ' | COLOR_PAIR(1)); if(current_file->stat.st_mtime!=0) { struct tm *tm_p; tm_p = localtime(¤t_file->stat.st_mtime); snprintf(datestr, sizeof(datestr),"%2d-%s-%4d %02d:%02d", tm_p->tm_mday, monstr[tm_p->tm_mon], 1900 + tm_p->tm_year, tm_p->tm_hour, tm_p->tm_min); /* May have to use %d instead of %e */ } else { strncpy(datestr, " ",sizeof(datestr)); } mode_string(current_file->stat.st_mode,str); wprintw(window, "%s %5u %5u ", str, (unsigned int)current_file->stat.st_uid, (unsigned int)current_file->stat.st_gid); wprintw(window, "%7llu", (long long unsigned int)current_file->stat.st_size); /* screen may overlap due to long filename */ wprintw(window, " %s %s", datestr, current_file->name); if((current_file->status&FILE_STATUS_DELETED)!=0 && has_colors()) wbkgdset(window,' ' | COLOR_PAIR(0)); if(current_file==pos) wattroff(window, A_REVERSE); } wmove(window, 6-1, 51); wclrtoeol(window); if(offset>0) wprintw(window, "Previous"); /* Clear the last line, useful if overlapping */ wmove(window,6+i-offset,0); wclrtoeol(window); wmove(window, 6+INTER_DIR, 51); wclrtoeol(window); if(current_file!=NULL) wprintw(window, "Next"); if(dir_list==NULL) { wmove(window,6,0); wprintw(window,"No file found, filesystem seems damaged."); } /* Redraw the bottom of the screen everytime because very long filenames may have corrupt it*/ mvwaddstr(window,LINES-2,0,"Use "); if(depth>0) { if(has_colors()) wbkgdset(window,' ' | A_BOLD | COLOR_PAIR(0)); waddstr(window, "Left"); if(has_colors()) wbkgdset(window,' ' | COLOR_PAIR(0)); waddstr(window," arrow to go back, "); } if(has_colors()) wbkgdset(window,' ' | A_BOLD | COLOR_PAIR(0)); waddstr(window,"Right"); if(has_colors()) wbkgdset(window,' ' | COLOR_PAIR(0)); waddstr(window," arrow to change directory, "); if(dir_data->copy_file!=NULL) { if(has_colors()) wbkgdset(window,' ' | A_BOLD | COLOR_PAIR(0)); waddstr(window,"c"); if(has_colors()) wbkgdset(window,' ' | COLOR_PAIR(0)); waddstr(window," to copy, "); } wmove(window,LINES-1,4); if((dir_data->capabilities&CAPA_LIST_DELETED)!=0) { if(has_colors()) wbkgdset(window,' ' | A_BOLD | COLOR_PAIR(0)); waddstr(window,"h"); if(has_colors()) wbkgdset(window,' ' | COLOR_PAIR(0)); if((dir_data->param&FLAG_LIST_DELETED)==0) waddstr(window," to unhide deleted files, "); else waddstr(window," to hide deleted files, "); } if(has_colors()) wbkgdset(window,' ' | A_BOLD | COLOR_PAIR(0)); waddstr(window,"q"); if(has_colors()) wbkgdset(window,' ' | COLOR_PAIR(0)); waddstr(window," to quit"); wrefresh(window); /* Using gnome terminal under FC3, TERM=xterm, the screen is not always correct */ wredrawln(window,0,getmaxy(window)); /* redrawwin def is boggus in pdcur24 */ car=wgetch(window); wmove(window,5,0); wclrtoeol(window); switch(car) { case key_ESC: case 'q': case 'M': quit=1; break; case '-': case KEY_LEFT: case '4': if(depth>0) return 1; break; case 'h': if((dir_data->capabilities&CAPA_LIST_DELETED)!=0) dir_data->param^=FLAG_LIST_DELETED; return inode; } if(dir_list!=NULL) { switch(car) { case KEY_UP: case '8': if(pos->prev!=NULL) { pos=pos->prev; pos_num--; } break; case KEY_DOWN: case '2': if(pos->next!=NULL) { pos=pos->next; pos_num++; } break; case 'p': case 'P': case '+': case ' ': case KEY_RIGHT: case '\r': case '\n': case '6': case KEY_ENTER: #ifdef PADENTER case PADENTER: #endif if((pos!=NULL) && (LINUX_S_ISDIR(pos->stat.st_mode)!=0)) { unsigned long int new_inode=pos->stat.st_ino; if((new_inode!=inode) &&(strcmp(pos->name,".")!=0)) { if(strcmp(pos->name,"..")==0) return 1; if(strlen(dir_data->current_directory)+1+strlen(pos->name)+1<=sizeof(dir_data->current_directory)) { if(strcmp(dir_data->current_directory,"/")) strcat(dir_data->current_directory,"/"); strcat(dir_data->current_directory,pos->name); return (long int)new_inode; } } } break; case KEY_PPAGE: for(i=0;(iprev!=NULL);i++) { pos=pos->prev; pos_num--; } break; case KEY_NPAGE: for(i=0;(inext!=NULL);i++) { pos=pos->next; pos_num++; } break; case 'c': if(dir_data->copy_file!=NULL) { const unsigned int current_directory_namelength=strlen(dir_data->current_directory); if(strcmp(pos->name,"..")!=0 && current_directory_namelength+1+strlen(pos->name)current_directory)-1) { if(strcmp(dir_data->current_directory,"/")) strcat(dir_data->current_directory,"/"); if(strcmp(pos->name,".")!=0) strcat(dir_data->current_directory,pos->name); if(dir_data->local_dir==NULL) { if(LINUX_S_ISDIR(pos->stat.st_mode)!=0) dir_data->local_dir=ask_location("Are you sure you want to copy %s and any files below to the directory %s ? [Y/N]", dir_data->current_directory); else dir_data->local_dir=ask_location("Are you sure you want to copy %s to the directory %s ? [Y/N]", dir_data->current_directory); } if(dir_data->local_dir!=NULL) { int res=-1; wmove(window,5,0); wclrtoeol(window); if(has_colors()) wbkgdset(window,' ' | A_BOLD | COLOR_PAIR(1)); wprintw(window,"Copying, please wait..."); if(has_colors()) wbkgdset(window,' ' | COLOR_PAIR(0)); wrefresh(window); if(LINUX_S_ISDIR(pos->stat.st_mode)!=0) { res=copy_dir(disk, partition, dir_data, pos); } else if(LINUX_S_ISREG(pos->stat.st_mode)!=0) { res=dir_data->copy_file(disk, partition, dir_data, pos); } wmove(window,5,0); wclrtoeol(window); if(res < -1) { if(has_colors()) wbkgdset(window,' ' | A_BOLD | COLOR_PAIR(1)); wprintw(window,"Copy failed!"); } else { if(has_colors()) wbkgdset(window,' ' | A_BOLD | COLOR_PAIR(2)); if(res < 0) wprintw(window,"Copy done! (Failed to copy some files)"); else wprintw(window,"Copy done!"); } if(has_colors()) wbkgdset(window,' ' | COLOR_PAIR(0)); } dir_data->current_directory[current_directory_namelength]='\0'; } } break; } if(pos_num=offset+INTER_DIR) offset=pos_num-INTER_DIR+1; } } while(quit==0 && old_LINES==LINES); } while(quit==0); return -1; } static int dir_partition_aux(disk_t *disk, const partition_t *partition, dir_data_t *dir_data, const unsigned long int inode, const unsigned int depth, char**current_cmd) { #define MAX_DIR_NBR 256 static unsigned long int inode_known[MAX_DIR_NBR]; if(depth==MAX_DIR_NBR) return 1; /* subdirectories depth is too high => Back */ if(dir_data->verbose>0) { log_info("\ndir_partition inode=%lu\n",inode); log_partition(disk, partition); } while(1) { const unsigned int current_directory_namelength=strlen(dir_data->current_directory); long int new_inode=-1; /* Quit */ file_data_t *dir_list; /* Not perfect for FAT32 root cluster */ inode_known[depth]=inode; dir_list=dir_data->get_dir(disk, partition, dir_data, inode); dir_aff_log(dir_data, dir_list); if(*current_cmd!=NULL) { dir_data->current_directory[current_directory_namelength]='\0'; delete_list_file(dir_list); return -1; /* Quit */ } new_inode=dir_aff_ncurses(disk, partition, dir_data,dir_list,inode,depth); if(new_inode==-1 || new_inode==1) /* -1:Quit or 1:Back */ { delete_list_file(dir_list); return new_inode; } if(new_inode>=2) { unsigned int new_inode_ok=1; unsigned int i; for(i=0;i<=depth && new_inode_ok!=0;i++) if((unsigned)new_inode==inode_known[i]) /* Avoid loop */ new_inode_ok=0; if(new_inode_ok>0) { dir_partition_aux(disk, partition, dir_data, (unsigned long int)new_inode, depth+1, current_cmd); } } /* restore current_directory name */ dir_data->current_directory[current_directory_namelength]='\0'; delete_list_file(dir_list); } } int dir_partition_aff(disk_t *disk, const partition_t *partition, dir_data_t *dir_data, const unsigned long int inode, char **current_cmd) { if(dir_data==NULL) return -1; return dir_partition_aux(disk, partition, dir_data, inode, 0, current_cmd); } /* Returns -2: no file copied -1: failed to copy some files 0: all files has been copied */ static int copy_dir(disk_t *disk, const partition_t *partition, dir_data_t *dir_data, const file_data_t *dir) { file_data_t *dir_list; const unsigned int current_directory_namelength=strlen(dir_data->current_directory); file_data_t *current_file; char *dir_name; int copy_bad=0; int copy_ok=0; if(dir_data->get_dir==NULL || dir_data->copy_file==NULL) return -2; dir_name=mkdir_local(dir_data->local_dir, dir_data->current_directory); dir_list=dir_data->get_dir(disk, partition, dir_data, (const unsigned long int)dir->stat.st_ino); for(current_file=dir_list;current_file!=NULL;current_file=current_file->next) { dir_data->current_directory[current_directory_namelength]='\0'; if(current_directory_namelength+1+strlen(current_file->name)current_directory)-1) { if(strcmp(dir_data->current_directory,"/")) strcat(dir_data->current_directory,"/"); strcat(dir_data->current_directory,current_file->name); if(LINUX_S_ISDIR(current_file->stat.st_mode)!=0) { int tmp=0; if(current_file->stat.st_ino != dir->stat.st_ino && strcmp(current_file->name,"..")!=0 && strcmp(current_file->name,".")!=0) tmp=copy_dir(disk, partition, dir_data, current_file); if(tmp>=-1) copy_ok=1; if(tmp<0) copy_bad=1; } else if(LINUX_S_ISREG(current_file->stat.st_mode)!=0) { // log_trace("copy_file %s\n",dir_data->current_directory); int tmp; tmp=dir_data->copy_file(disk, partition, dir_data, current_file); if(tmp==0) copy_ok=1; else copy_bad=1; } } } dir_data->current_directory[current_directory_namelength]='\0'; delete_list_file(dir_list); set_date(dir_name, dir->stat.st_atime, dir->stat.st_mtime); free(dir_name); return (copy_bad>0?(copy_ok>0?-1:-2):0); } #endif