📄 vfs-file-task.c
字号:
/** C Implementation: vfs-file-task** Description:*** Author: Hong Jen Yee (PCMan) <pcman.tw (AT) gmail.com>, (C) 2005** Copyright: See COPYING file that comes with this distribution**/#ifdef HAVE_CONFIG_H# include <config.h>#endif#include "vfs-file-task.h"#include <unistd.h>#include <fcntl.h>#include <utime.h>#include <sys/types.h>#include <sys/stat.h>#include <glib.h>#include "glib-mem.h"#include <glib/gi18n.h>#include <stdio.h>#include <string.h>#include <errno.h>const mode_t chmod_flags[] = { S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH, S_ISUID, S_ISGID, S_ISVTX };/** void get_total_size_of_dir( const char* path, off_t* size )* Recursively count total size of all files in the specified directory.* If the path specified is a file, the size of the file is directly returned.* cancel is used to cancel the operation. This function will check the value* pointed by cancel in every iteration. If cancel is set to TRUE, the* calculation is cancelled.* NOTE: *size should be set to zero before calling this function.*/static void get_total_size_of_dir( VFSFileTask* task, const char* path, off_t* size );static gbooleancall_progress_callback( VFSFileTask* task ){ /* FIXME: This has some impact on performance */ gdouble percent = ( ( gdouble ) task->progress ) / task->total_size; int ipercent = ( int ) ( percent * 100 ); if ( ipercent != task->percent ) task->percent = ipercent; if ( task->progress_cb ) { task->progress_cb( task, task->percent, task->current_file, NULL, task->progress_cb_data ); } return FALSE;}static void call_state_callback( VFSFileTask* task, VFSFileTaskState state ){ task->state = state; if ( task->state_cb ) { if ( ! task->state_cb( task, state, NULL, task->state_cb_data ) ) task->state = VFS_FILE_TASK_ABORTED; else task->state = VFS_FILE_TASK_RUNNING; }}static gboolean should_abort( VFSFileTask* task ){ if ( task->state == VFS_FILE_TASK_QUERY_ABORT ) { call_state_callback( task, VFS_FILE_TASK_QUERY_ABORT ); if ( task->state == VFS_FILE_TASK_ABORTED ) return TRUE; } return ( task->state == VFS_FILE_TASK_ABORTED );}/** Check if the destination file exists.* If the dest_file exists, let the user choose a new destination,* skip this file, overwrite existing file, or cancel all file operation.* The returned string is the new destination file choosed by the user*/static gboolean check_overwrite( VFSFileTask* task, const gchar* dest_file, gboolean* dest_exists, char** new_dest_file ){ char * new_dest; new_dest = *new_dest_file = NULL; struct stat dest_stat; if ( task->overwrite_mode == VFS_FILE_TASK_OVERWRITE_ALL ) { *dest_exists = !lstat( dest_file, &dest_stat ); return TRUE; } if ( task->overwrite_mode == VFS_FILE_TASK_SKIP_ALL ) { *dest_exists = !lstat( dest_file, &dest_stat ); return FALSE; } *dest_exists = FALSE; if ( task->state_cb ) { while ( lstat( dest_file, &dest_stat ) != -1 ) { *dest_exists = TRUE; /* destination file exists */ task->state = VFS_FILE_TASK_QUERY_OVERWRITE; if ( ! task->state_cb( task, VFS_FILE_TASK_QUERY_OVERWRITE, &new_dest, task->state_cb_data ) ) { task->state = VFS_FILE_TASK_ABORTED; } else { task->state = VFS_FILE_TASK_RUNNING; } if ( should_abort( task ) ) return FALSE; if( task->overwrite_mode != VFS_FILE_TASK_RENAME ) { g_free( new_dest ); *new_dest_file = NULL; if( task->overwrite_mode == VFS_FILE_TASK_OVERWRITE || task->overwrite_mode == VFS_FILE_TASK_OVERWRITE_ALL ) { return TRUE; } else return FALSE; } if ( new_dest ) dest_file = new_dest; } *dest_exists = FALSE; *new_dest_file = new_dest; } return ! should_abort( task );}static voidvfs_file_task_do_copy( VFSFileTask* task, const char* src_file, const char* dest_file ){ GDir * dir; gchar* file_name; gchar* sub_src_file; gchar* sub_dest_file; struct stat file_stat; char buffer[ 4096 ]; int rfd; int wfd; ssize_t rsize; char* new_dest_file = NULL; gboolean dest_exists; int overwrite_mode; int result; if ( should_abort( task ) ) return ; if ( lstat( src_file, &file_stat ) == -1 ) { /* Error occurred */ call_state_callback( task, VFS_FILE_TASK_ERROR ); if ( should_abort( task ) ) return ; } task->current_file = src_file; task->current_dest = dest_file; call_progress_callback( task ); result = 0; if ( S_ISDIR( file_stat.st_mode ) ) { if ( ! check_overwrite( task, dest_file, &dest_exists, &new_dest_file ) ) goto _return_; if ( new_dest_file ) task->current_dest = dest_file = new_dest_file; if ( ! dest_exists ) result = mkdir( dest_file, file_stat.st_mode | 0700 ); if ( result == 0 ) { struct utimbuf times; task->progress += file_stat.st_size; call_progress_callback( task ); dir = g_dir_open( src_file, 0, NULL ); while ( (file_name = g_dir_read_name( dir )) ) { if ( should_abort( task ) ) break; sub_src_file = g_build_filename( src_file, file_name, NULL ); sub_dest_file = g_build_filename( dest_file, file_name, NULL ); vfs_file_task_do_copy( task, sub_src_file, sub_dest_file ); g_free( sub_dest_file ); g_free( sub_src_file ); } g_dir_close( dir ); chmod( dest_file, file_stat.st_mode ); times.actime = file_stat.st_atime; times.modtime = file_stat.st_mtime; utime( dest_file, × ); /* Move files to different device: Need to delete source files */ if ( task->type == VFS_FILE_TASK_MOVE && !should_abort( task ) ) { if ( (result = rmdir( src_file )) ) { task->error = errno; call_state_callback( task, VFS_FILE_TASK_ERROR ); if ( should_abort( task ) ) goto _return_; } } } else { /* result != 0, error occurred */ task->error = errno; call_state_callback( task, VFS_FILE_TASK_ERROR ); } } else if ( S_ISLNK( file_stat.st_mode ) ) { if ( ( rfd = readlink( src_file, buffer, sizeof( buffer ) ) ) > 0 ) { if ( ! check_overwrite( task, dest_file, &dest_exists, &new_dest_file ) ) goto _return_; if ( new_dest_file ) task->current_dest = dest_file = new_dest_file; if ( ( wfd = symlink( buffer, dest_file ) ) == 0 ) { chmod( dest_file, file_stat.st_mode ); /* Move files to different device: Need to delete source files */ if ( task->type == VFS_FILE_TASK_MOVE ) { result = unlink( src_file ); if ( result ) { task->error = errno; call_state_callback( task, VFS_FILE_TASK_ERROR ); } } task->progress += file_stat.st_size; call_progress_callback( task ); } else { task->error = errno; call_state_callback( task, VFS_FILE_TASK_ERROR ); } } else { task->error = errno; call_state_callback( task, VFS_FILE_TASK_ERROR ); } } else { if ( ( rfd = open( src_file, O_RDONLY ) ) >= 0 ) { if ( ! check_overwrite( task, dest_file, &dest_exists, &new_dest_file ) ) goto _return_; if ( new_dest_file ) task->current_dest = dest_file = new_dest_file; if ( ( wfd = creat( dest_file, file_stat.st_mode | S_IWUSR ) ) >= 0 ) { struct utimbuf times; while ( ( rsize = read( rfd, buffer, sizeof( buffer ) ) ) > 0 ) { if ( should_abort( task ) ) break; if ( write( wfd, buffer, rsize ) > 0 ) task->progress += rsize; else { task->error = errno; call_state_callback( task, VFS_FILE_TASK_ERROR ); close( wfd ); } call_progress_callback( task ); } close( wfd ); chmod( dest_file, file_stat.st_mode ); times.actime = file_stat.st_atime; times.modtime = file_stat.st_mtime; utime( dest_file, × ); /* Move files to different device: Need to delete source files */ if ( task->type == VFS_FILE_TASK_MOVE && !should_abort( task ) ) { result = unlink( src_file ); if ( result ) { task->error = errno; call_state_callback( task, VFS_FILE_TASK_ERROR ); } } } else { task->error = errno; call_state_callback( task, VFS_FILE_TASK_ERROR ); } close( rfd ); } else { task->error = errno; call_state_callback( task, VFS_FILE_TASK_ERROR ); } }_return_: g_free( new_dest_file );}static voidvfs_file_task_copy( char* src_file, VFSFileTask* task ){ gchar * file_name; gchar* dest_file; call_progress_callback( task ); file_name = g_path_get_basename( src_file ); dest_file = g_build_filename( task->dest_dir, file_name, NULL ); g_free( file_name ); vfs_file_task_do_copy( task, src_file, dest_file ); g_free( dest_file );}static voidvfs_file_task_do_move ( VFSFileTask* task, const char* src_file, const char* dest_file ){ GDir * dir; gchar* file_name; gchar* new_dest_file = NULL; gboolean dest_exists; gchar* sub_src_file; gchar* sub_dest_file; struct stat file_stat; int overwrite_mode, result; if ( should_abort( task ) ) return ; /* g_debug( "move \"%s\" to \"%s\"\n", src_file, dest_file ); */ if ( lstat( src_file, &file_stat ) == -1 ) { task->error = errno; /* Error occurred */ call_state_callback( task, VFS_FILE_TASK_ERROR ); if ( should_abort( task ) ) return ; } task->current_file = src_file; task->current_dest = dest_file; if ( should_abort( task ) ) return ; call_progress_callback( task ); if ( ! check_overwrite( task, dest_file, &dest_exists, &new_dest_file ) ) return ; if ( new_dest_file ) task->current_dest = dest_file = new_dest_file; result = rename( src_file, dest_file ); if ( result != 0 ) { task->error = errno; call_state_callback( task, VFS_FILE_TASK_ERROR ); if ( should_abort( task ) ) { g_free( new_dest_file ); return ; } } else chmod( dest_file, file_stat.st_mode ); task->progress += file_stat.st_size; call_progress_callback( task ); g_free( new_dest_file );}static voidvfs_file_task_move( char* src_file, VFSFileTask* task ){ struct stat src_stat; struct stat dest_stat; gchar* file_name; gchar* dest_file; if ( should_abort( task ) ) return ; task->current_file = src_file; file_name = g_path_get_basename( src_file ); dest_file = g_build_filename( task->dest_dir, file_name, NULL ); g_free( file_name ); if ( lstat( src_file, &src_stat ) == 0 && lstat( task->dest_dir, &dest_stat ) == 0 ) { /* Not on the same device */ if ( src_stat.st_dev != dest_stat.st_dev ) { /* g_print("not on the same dev: %s\n", src_file); */ vfs_file_task_do_copy( task, src_file, dest_file ); } else { /* g_print("on the same dev: %s\n", src_file); */ vfs_file_task_do_move( task, src_file, dest_file ); } } else { task->error = errno;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -