/** * Assemble a filepath from its directory and filename. * * @param dir_path directory path * @param filename file name * @return filepath */ char* make_path(const char* dir_path, const char* filename) { char* buf; size_t len; assert(dir_path); assert(filename); /* remove leading path separators from filename */ while (IS_PATH_SEPARATOR(*filename)) filename++; if (dir_path[0] == '.' && dir_path[1] == 0) { /* do not extend filename for dir_path="." */ return rsh_strdup(filename); } /* copy directory path */ len = strlen(dir_path); buf = (char*)rsh_malloc(len + strlen(filename) + 2); strcpy(buf, dir_path); /* separate directory from filename */ if (len > 0 && !IS_PATH_SEPARATOR(buf[len-1])) { buf[len++] = SYS_PATH_SEPARATOR; } /* append filename */ strcpy(buf+len, filename); return buf; }
/** * Convert given string to lower case. * The result string will be allocated by malloc. * The allocated memory should be freed by calling free(). * * @param str a string to convert * @return converted string allocated by malloc */ char* str_tolower(const char* str) { char* buf = rsh_strdup(str); char* p; if (buf) { for (p = buf; *p; p++) *p = tolower(*p); } return buf; }
/** * Allocate new print_item. * * @param flags the print_item flags * @param hash_id optional hash_id * @param data optional string to store * @return allocated print_item */ static print_item* new_print_item(unsigned flags, unsigned hash_id, const char *data) { print_item* item = (print_item*)rsh_malloc(sizeof(print_item)); item->flags = flags; item->hash_id = hash_id; item->width = 0; item->data = (data ? rsh_strdup(data) : NULL); item->next = NULL; return item; }
void file_init(file_t* file, const char* path, int reuse_path) { memset(file, 0, sizeof(*file)); if (reuse_path) { file->path = (char*)path; file->mode = FILE_OPT_DONT_FREE_PATH; } else { file->path = rsh_strdup(path); } }
/** * Convert given C-string from encoding specified by * command line options to utf8. * * @param str the string to convert * @return converted string on success, NULL on fail */ char* win_to_utf8(const char* str) { char* res; wchar_t* buf; assert((opt.flags & (OPT_UTF8 | OPT_OEM | OPT_ANSI)) != 0); if(opt.flags & OPT_UTF8) return rsh_strdup(str); if((buf = c2w(str, 0)) == NULL) return NULL; res = wchar_to_cstr(buf, CP_UTF8, NULL); free(buf); return res; }
/** * Return allocated buffer with the directory part of the path. * The buffer must be freed by calling free(). * * @param path file path * @return directory */ char* get_dirname(const char* path) { const char *p = path + strlen(path) - 1; char *res; for (; p > path && !IS_PATH_SEPARATOR(*p); p--); if ((p - path) > 1) { res = (char*)rsh_malloc(p-path+1); memcpy(res, path, p-path); res[p-path] = 0; return res; } else { return rsh_strdup("."); } }
/** * Split the given string by comma and put the parts into array. * * @param vect the array to put the parsed elements to * @param comma_separated_list the string to split */ void file_mask_add_list(file_mask_array* vect, const char* comma_separated_list) { char *buf, *cur, *next; if (!comma_separated_list || !*comma_separated_list) { return; } buf = rsh_strdup(comma_separated_list); for (cur = buf; cur && *cur; cur = next) { next = strchr(cur, ','); if (next) *(next++) = '\0'; if (*cur != '\0') file_mask_add(vect, cur); } free(buf); }
/** * Set file path of the given item. * * @param item pointer to the item to change * @param filepath the file path to set */ static int file_set_item_set_filepath(file_set_item* item, const char* filepath) { if(item->search_filepath != item->filepath) free(item->search_filepath); free(item->filepath); item->filepath = rsh_strdup(filepath); if(!item->filepath) return 0; /* apply str_tolower if CASE_INSENSITIVE */ /* Note: strcasecmp() is not used instead of search_filepath due to portability issue */ /* Note: item->search_filepath is always correctly freed by file_set_item_free() */ item->search_filepath = (opt.flags & OPT_IGNORE_CASE ? str_tolower(item->filepath) : item->filepath); item->hash = file_set_make_hash(item->search_filepath); return 1; }
/** * Allocate and initialize a dir_entry. * * @param next next dir_entry in list * @param filename a filename to store in the dir_entry * @param type type of dir_entry * @return allocated dir_entry */ static dir_entry* dir_entry_new(dir_entry *next, char* filename, unsigned type) { dir_entry* e = (dir_entry*)malloc(sizeof(dir_entry)); if (!e) return NULL; if (filename) { e->filename = rsh_strdup(filename); if (!e->filename) { free(e); return NULL; } } else { e->filename = NULL; } e->next = next; e->type = type; return e; }
/** * Store print_path in a file_info struct, replacing if needed * system path separators with specified by user command line option. * * @param info pointer to the the file_info structure to change * @param print_path the print path to store */ static void file_info_set_print_path(struct file_info* info, const char* print_path) { char *p; char wrong_sep; /* check if path separator was specified by command line options */ if(opt.path_separator) { wrong_sep = (opt.path_separator == '/' ? '\\' : '/'); if((p = (char*)strchr(print_path, wrong_sep)) != NULL) { info->allocated_ptr = rsh_strdup(print_path); info->print_path = info->allocated_ptr; p = info->allocated_ptr + (p - print_path); /* replace wrong_sep in the print_path with separator defined by options */ for(; *p; p++) { if(*p == wrong_sep) *p = opt.path_separator; } return; } } /* if path was not replaces, than just store the value */ info->print_path = print_path; }
/** * Set optional name of the program generating the torrent * for storing into torrent file. * * @param ctx the torrent algorithm context * @param name the program name */ void torrent_set_program_name(torrent_ctx *ctx, const char* name) { ctx->program_name = rsh_strdup(name); }
/** * Calculate and print file hash sums using printf format. * * @param out a stream to print to * @param file the file to calculate sums for * @param print_path the path to print * @return 0 on success, -1 on fail */ int calculate_and_print_sums(FILE* out, file_t* file, const char *print_path) { struct file_info info; timedelta_t timer; int res = 0; memset(&info, 0, sizeof(info)); info.file = file; info.full_path = rsh_strdup(file->path); file_info_set_print_path(&info, print_path); info.size = 0; info.sums_flags = opt.sum_flags; if(file->mode & FILE_IFSTDIN) { print_path = "(stdin)"; } else { if(file->mode & FILE_IFDIR) return 0; /* don't handle directories */ info.size = file->size; /* total size, in bytes */ } /* initialize percents output */ init_percents(&info); rhash_timer_start(&timer); if(info.sums_flags) { /* calculate sums */ if(calc_sums(&info) < 0) { /* print error unless sharing access error occurred */ if(errno == EACCES) return 0; log_file_error(file->path); res = -1; } if(rhash_data.interrupted) { report_interrupted(); return 0; } } info.time = rhash_timer_stop(&timer); finish_percents(&info, res); if(opt.flags & OPT_EMBED_CRC) { /* rename the file */ rename_file_by_embeding_crc32(&info); } if((opt.mode & MODE_TORRENT) && !opt.bt_batch_file) { save_torrent(&info); } if((opt.mode & MODE_UPDATE) && opt.fmt == FMT_SFV) { /* updating SFV file: print SFV header line */ print_sfv_header_line(rhash_data.upd_fd, file, 0); if(opt.flags & OPT_VERBOSE) { print_sfv_header_line(rhash_data.log, file, 0); fflush(rhash_data.log); } rsh_file_cleanup(file); } if(rhash_data.print_list && res >= 0) { if (!opt.bt_batch_file) { print_line(out, rhash_data.print_list, &info); fflush(out); /* print calculated line to stderr or log-file if verbose */ if((opt.mode & MODE_UPDATE) && (opt.flags & OPT_VERBOSE)) { print_line(rhash_data.log, rhash_data.print_list, &info); fflush(rhash_data.log); } } if((opt.flags & OPT_SPEED) && info.sums_flags) { print_file_time_stats(&info); } } free(info.full_path); file_info_destroy(&info); return res; }
/** * Walk directory tree and call given callback function to process each file/directory. * * @param start_dir path to the directory to walk recursively * @param data the options specifying how to walk the directory tree * @return 0 on success, -1 on error */ static int dir_scan(file_t* start_dir, file_search_data* data) { dir_entry *dirs_stack = NULL; /* root of the dir_list */ dir_iterator* it; int level = 0; int max_depth = data->max_depth; int options = data->options; file_t file; if (max_depth < 0 || max_depth >= MAX_DIRS_DEPTH) { max_depth = MAX_DIRS_DEPTH - 1; } /* skip the directory if max_depth == 0 */ if (!max_depth) return 0; if (!FILE_ISDIR(start_dir)) { errno = ENOTDIR; return -1; } /* check if we should descend into the root directory */ if ((options & (FIND_WALK_DEPTH_FIRST | FIND_SKIP_DIRS)) == 0) { if (!data->call_back(start_dir, data->call_back_data)) return 0; } /* allocate array of counters of directory elements */ it = (dir_iterator*)malloc((MAX_DIRS_DEPTH + 1) * sizeof(dir_iterator)); if (!it) { return -1; } /* push dummy counter for the root element */ it[0].count = 1; it[0].dir_path = 0; memset(&file, 0, sizeof(file)); while (!(data->options & FIND_CANCEL)) { dir_entry **insert_at; char* dir_path; DIR *dp; struct dirent *de; /* climb down from the tree */ while (--it[level].count < 0) { /* do not need this dir_path anymore */ free(it[level].dir_path); if (--level < 0) { /* walked the whole tree */ assert(!dirs_stack); free(it); return 0; } } assert(level >= 0 && it[level].count >= 0); /* take a filename from dirs_stack and construct the next path */ if (level) { assert(dirs_stack != NULL); dir_path = make_path(it[level].dir_path, dirs_stack->filename); dir_entry_drop_head(&dirs_stack); } else { /* the first cycle: start from a root directory */ dir_path = rsh_strdup(start_dir->path); } if (!dir_path) continue; /* fill the next level of directories */ level++; assert(level < MAX_DIRS_DEPTH); it[level].count = 0; it[level].dir_path = dir_path; if ((options & (FIND_WALK_DEPTH_FIRST | FIND_SKIP_DIRS)) == FIND_WALK_DEPTH_FIRST) { int res; file_init(&file, dir_path, 1); res = file_stat2(&file, USE_LSTAT); /* check if we should skip the directory */ if (res < 0 || !data->call_back(&file, data->call_back_data)) { if (res < 0 && (options & FIND_LOG_ERRORS)) { data->errors_count++; } file_cleanup(&file); continue; } } file_cleanup(&file); /* step into directory */ dp = opendir(dir_path); if (!dp) continue; insert_at = &dirs_stack; while ((de = readdir(dp)) != NULL) { int res; /* skip the "." and ".." directories */ if (IS_CURRENT_OR_PARENT_DIR(de->d_name)) continue; file.mode = 0; file.path = make_path(dir_path, de->d_name); if (!file.path) continue; res = file_stat2(&file, USE_LSTAT); if (res >= 0) { /* process the file or directory */ if (FILE_ISDIR(&file) && (options & (FIND_WALK_DEPTH_FIRST | FIND_SKIP_DIRS))) { res = ((options & FIND_FOLLOW_SYMLINKS) || !FILE_ISLNK(&file)); } else if (FILE_ISREG(&file)) { /* handle file by callback function */ res = data->call_back(&file, data->call_back_data); } /* check if file is a directory and we need to walk it, */ /* but don't go deeper than max_depth */ if (FILE_ISDIR(&file) && res && level < max_depth && ((options & FIND_FOLLOW_SYMLINKS) || !FILE_ISLNK(&file))) { /* add the directory name to the dirs_stack */ if (dir_entry_insert(insert_at, de->d_name, file.mode)) { /* the directory name was successfully inserted */ insert_at = &((*insert_at)->next); it[level].count++; } } } else if (options & FIND_LOG_ERRORS) { /* report error only if FIND_LOG_ERRORS option is set */ log_file_error(file.path); data->errors_count++; } file_cleanup(&file); } closedir(dp); } while (dirs_stack) { dir_entry_drop_head(&dirs_stack); } while (level) { free(it[level--].dir_path); } free(it); assert(file.path == 0); return 0; }
/** * Walk directory tree and call given callback function to process each file/directory. * * @param start_dir path to the directory to walk recursively * @param options the options specifying how to walk the directory tree */ int find_file(file_t* start_dir, find_file_options* options) { dir_entry *dirs_stack = NULL; /* root of the dir_list */ dir_iterator* it; int level = 1; int max_depth = options->max_depth; int flags = options->options; dir_entry* entry; file_t file; if(max_depth < 0 || max_depth >= MAX_DIRS_DEPTH) { max_depth = MAX_DIRS_DEPTH; } /* skip the directory if max_depth == 0 */ if(max_depth == 0) { return 0; } memset(&file, 0, sizeof(file)); if((start_dir->mode & FILE_IFDIR) == 0) { errno = ENOTDIR; return -1; } /* check if we should descend into the root directory */ if((flags & (FIND_WALK_DEPTH_FIRST | FIND_SKIP_DIRS)) == 0) { if(!options->call_back(start_dir, options->call_back_data)) return 0; } it = (dir_iterator*)malloc(MAX_DIRS_DEPTH * sizeof(dir_iterator)); if(!it) return 0; /* push root directory into dirs_stack */ it[0].left = 1; it[0].prev_dir = rsh_strdup(start_dir->path); it[1].prev_dir = NULL; if(!it[0].prev_dir) { errno = ENOMEM; return -1; } entry = dir_entry_insert(&dirs_stack, NULL, 0); if(!entry) { free(it[0].prev_dir); free(it); errno = ENOMEM; return -1; } while(!(options->options & FIND_CANCEL)) { dir_entry *dir, **insert_at; char* dir_path; DIR *dp; struct dirent *de; /* walk back */ while((--level) >= 0 && it[level].left <= 0) free(it[level+1].prev_dir); if(level < 0) break; assert(dirs_stack != NULL); /* on the first cycle: level == 0, stack[0] == 0; */ dir = dirs_stack; /* take last dir from the list */ dirs_stack = dirs_stack->next; /* remove last dir from the list */ it[level].left--; dir_path = (!dir->filename ? rsh_strdup(it[level].prev_dir) : make_path(it[level].prev_dir, dir->filename) ); dir_entry_free(dir); if(!dir_path) continue; level++; it[level].left = 0; it[level].prev_dir = dir_path; if((flags & (FIND_WALK_DEPTH_FIRST | FIND_SKIP_DIRS)) == FIND_WALK_DEPTH_FIRST) { rsh_file_cleanup(&file); file.path = dir_path; /* check if we should skip the directory */ if(!options->call_back(&file, options->call_back_data)) continue; } /* read dir */ dp = opendir(dir_path); if(dp == NULL) continue; insert_at = &dirs_stack; while((de = readdir(dp)) != NULL) { int res; /* skip "." and ".." dirs */ if(de->d_name[0] == '.' && (de->d_name[1] == 0 || (de->d_name[1] == '.' && de->d_name[2] == 0 ))) continue; if( !(file.path = make_path(dir_path, de->d_name)) ) continue; res = rsh_file_stat2(&file, USE_LSTAT); /* process */ if(res >= 0) { if((file.mode & FILE_IFDIR) && (flags & (FIND_WALK_DEPTH_FIRST | FIND_SKIP_DIRS))) res = 1; else { /* handle file by callback function */ res = options->call_back(&file, options->call_back_data); } /* if file is a directory and we need to walk it */ if((file.mode & FILE_IFDIR) && res && level < max_depth) { /* don't go deeper if max_depth reached */ /* add directory to dirs_stack */ if(dir_entry_insert(insert_at, de->d_name, file.mode)) { /* if really added */ insert_at = &((*insert_at)->next); it[level].left++; } } } else if (options->options & FIND_LOG_ERRORS) { log_file_error(file.path); } rsh_file_cleanup(&file); free(file.path); } closedir(dp); if(it[level].left > 0) level++; } assert(dirs_stack == NULL); free(it); return 0; }
/** * RHash program entry point. * * @param argc number of program arguments including the program path * @param argv program arguments * @return the program exit code, zero on success and 1 on error */ int main(int argc, char *argv[]) { i18n_initialize(); /* initialize locale and translation */ memset(&rhash_data, 0, sizeof(rhash_data)); rhash_data.out = stdout; /* set initial output streams */ rhash_data.log = stderr; /* can be altered by options later */ init_hash_info_table(); read_options(argc, argv); /* load config and parse command line options */ prev_sigint_handler = signal(SIGINT, ctrl_c_handler); /* install SIGINT handler */ rhash_library_init(); /* in benchmark mode just run benchmark and exit */ if(opt.mode & MODE_BENCHMARK) { unsigned flags = (opt.flags & OPT_BENCH_RAW ? RHASH_BENCHMARK_CPB | RHASH_BENCHMARK_RAW : RHASH_BENCHMARK_CPB); if((opt.flags & OPT_BENCH_RAW) == 0) { fprintf(rhash_data.out, _("%s v%s benchmarking...\n"), PROGRAM_NAME, VERSION); } rhash_run_benchmark(opt.sum_flags, flags, rhash_data.out); rsh_exit(0); } if(opt.n_files == 0) { if(argc > 1) { log_warning(_("no files/directories were specified at command line\n")); } /* print short usage help */ log_msg(_("Usage: %s [OPTION...] <FILE>...\n\n" "Run `%s --help' for more help.\n"), CMD_FILENAME, CMD_FILENAME); rsh_exit(0); } /* setup printf formating string */ rhash_data.printf_str = opt.printf_str; if(opt.template_file) { if(!load_printf_template()) rsh_exit(2); } else if(!rhash_data.printf_str && !(opt.mode & (MODE_CHECK | MODE_CHECK_EMBEDDED))) { /* initialize printf output format according to '--<hashname>' options */ init_printf_format( (rhash_data.template_text = rsh_str_new()) ); rhash_data.printf_str = rhash_data.template_text->str; if(opt.flags & OPT_VERBOSE) { char* str = rsh_strdup(rhash_data.printf_str); log_msg(_("Format string is: %s\n"), str_trim(str)); free(str); } } if(rhash_data.printf_str) { rhash_data.print_list = parse_print_string(rhash_data.printf_str, &opt.sum_flags); } preprocess_files(); process_files(); options_destroy(&opt); rhash_destroy(&rhash_data); return (rhash_data.error_flag ? 1 : 0); }
/** * Set torrent announcement-url for storing into torrent file. * * @param ctx the torrent algorithm context * @param announce_url the announcement-url */ void torrent_set_announce(torrent_ctx *ctx, const char* announce_url) { free(ctx->announce); ctx->announce = rsh_strdup(announce_url); }
/* allocate and fill the file_search_data */ file_search_data* create_file_search_data(rsh_tchar** paths, size_t count, int max_depth) { size_t i; file_search_data* data = (file_search_data*)rsh_malloc(sizeof(file_search_data)); memset(data, 0, sizeof(file_search_data)); rsh_blocks_vector_init(&data->root_files); data->max_depth = max_depth; #ifdef _WIN32 /* expand wildcards and fill the root_files */ for (i = 0; i < count; i++) { int added = 0; size_t length, index; wchar_t* path = paths[i]; wchar_t* p = wcschr(path, L'\0') - 1; /* strip trailing '\','/' symbols (if not preceded by ':') */ for (; p > path && IS_PATH_SEPARATOR_W(*p) && p[-1] != L':'; p--) *p = 0; /* Expand a wildcard in the current file path and store results into data->root_files. * If a wildcard is not found then just the file path is stored. * NB, only wildcards in the last filename of the path are expanded. */ length = p - path + 1; index = wcscspn(path, L"*?"); if (index < length && wcscspn(path + index, L"/\\") >= (length - index)) { /* a wildcard is found without a directory separator after it */ wchar_t* parent; WIN32_FIND_DATAW d; HANDLE handle; /* find a directory separator before the file name */ for (; index > 0 && !IS_PATH_SEPARATOR(path[index]); index--); parent = (IS_PATH_SEPARATOR(path[index]) ? path : 0); handle = FindFirstFileW(path, &d); if (INVALID_HANDLE_VALUE != handle) { do { file_t file; int failed; if (IS_CURRENT_OR_PARENT_DIRW(d.cFileName)) continue; memset(&file, 0, sizeof(file)); file.wpath = make_pathw(parent, index + 1, d.cFileName); if (!file.wpath) continue; /* skip directories if not in recursive mode */ if (data->max_depth == 0 && (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) continue; /* convert file name */ file.path = wchar_to_cstr(file.wpath, WIN_DEFAULT_ENCODING, &failed); if (!failed) { failed = (file_statw(&file) < 0); } /* quietly skip unconvertible file names */ if (!file.path || failed) { if (failed) { data->errors_count++; } free(file.path); free(file.wpath); continue; } /* fill the file information */ file.mode |= FILE_IFROOT; add_root_file(data, &file); added++; } while (FindNextFileW(handle, &d)); FindClose(handle); } else { /* report error on the specified wildcard */ char * cpath = wchar_to_cstr(path, WIN_DEFAULT_ENCODING, NULL); set_errno_from_last_file_error(); log_file_error(cpath); free(cpath); data->errors_count++; } } else { int failed; file_t file; memset(&file, 0, sizeof(file)); /* if filepath is a dash string "-" */ if ((path[0] == L'-' && path[1] == L'\0')) { file.mode = FILE_IFSTDIN; file.path = rsh_strdup("(stdin)"); } else { file.path = wchar_to_cstr(path, WIN_DEFAULT_ENCODING, &failed); if (failed) { log_error(_("Can't convert the path to local encoding: %s\n"), file.path); free(file.path); data->errors_count++; continue; } file.wpath = path; if (file_statw(&file) < 0) { log_file_error(file.path); free(file.path); data->errors_count++; continue; } } /* mark the file as obtained from the command line */ file.mode |= FILE_IFROOT; file.wpath = rsh_wcsdup(path); add_root_file(data, &file); } } /* for */ #else /* copy file paths */ for (i = 0; i < count; i++) { file_t file; file_init(&file, paths[i], 0); if (IS_DASH_STR(file.path)) { file.mode = FILE_IFSTDIN; } else if (file_stat2(&file, USE_LSTAT) < 0) { log_file_error(file.path); file_cleanup(&file); data->errors_count++; continue; } file.mode |= FILE_IFROOT; add_root_file(data, &file); } #endif return data; }
/** * RHash program entry point. * * @param argc number of program arguments including the program path * @param argv program arguments * @return the program exit code, zero on success and 1 on error */ int main(int argc, char *argv[]) { find_file_options search_opt; timedelta_t timer; int sfv; i18n_initialize(); /* initialize locale and translation */ memset(&rhash_data, 0, sizeof(rhash_data)); rhash_data.out = stdout; /* set initial output streams */ rhash_data.log = stderr; /* can be altered by options later */ rhash_data.search_opt = &search_opt; init_hash_info_table(); read_options(argc, argv); /* load config and parse command line options */ prev_sigint_handler = signal(SIGINT, ctrl_c_handler); /* install SIGINT handler */ rhash_library_init(); /* in benchmark mode just run benchmark and exit */ if(opt.mode & MODE_BENCHMARK) { unsigned flags = (opt.flags & OPT_BENCH_RAW ? RHASH_BENCHMARK_CPB | RHASH_BENCHMARK_RAW : RHASH_BENCHMARK_CPB); if((opt.flags & OPT_BENCH_RAW) == 0) { fprintf(rhash_data.out, _("%s v%s benchmarking...\n"), PROGRAM_NAME, VERSION); } rhash_run_benchmark(opt.sum_flags, flags, rhash_data.out); rsh_exit(0); } if(opt.n_files == 0) { if(argc > 1) { log_warning(_("no files/directories were specified at command line\n")); } /* print short usage help */ log_msg(_("Usage: %s [OPTION...] <FILE>...\n\n" "Run `%s --help' for more help.\n"), CMD_FILENAME, CMD_FILENAME); rsh_exit(0); } /* setup printf formating string */ rhash_data.printf_str = opt.printf_str; if(opt.template_file) { if(!load_printf_template()) rsh_exit(2); } else if(!rhash_data.printf_str && !(opt.mode & (MODE_CHECK | MODE_CHECK_EMBEDDED))) { /* initialize printf output format according to '--<hashname>' options */ init_printf_format( (rhash_data.template_text = rsh_str_new()) ); rhash_data.printf_str = rhash_data.template_text->str; if(opt.flags & OPT_VERBOSE) { char* str = rsh_strdup(rhash_data.printf_str); log_msg(_("Format string is: %s\n"), str_trim(str)); free(str); } } if(rhash_data.printf_str) { rhash_data.print_list = parse_print_string(rhash_data.printf_str, &opt.sum_flags); } memset(&search_opt, 0, sizeof(search_opt)); search_opt.max_depth = (opt.flags & OPT_RECURSIVE ? opt.find_max_depth : 0); search_opt.options = FIND_SKIP_DIRS; search_opt.call_back = find_file_callback; if((sfv = (opt.fmt == FMT_SFV && !opt.mode))) { print_sfv_banner(rhash_data.out); } /* pre-process files */ if(sfv || opt.bt_batch_file) { /* note: errors are not reported on pre-processing */ search_opt.call_back_data = (void*)1; process_files((const char**)opt.files, opt.n_files, &search_opt); fflush(rhash_data.out); } /* measure total processing time */ rhash_timer_start(&timer); rhash_data.processed = 0; /* process files */ search_opt.options |= FIND_LOG_ERRORS; search_opt.call_back_data = (void*)0; process_files((const char**)opt.files, opt.n_files, &search_opt); if((opt.mode & MODE_CHECK_EMBEDDED) && rhash_data.processed > 1) { print_check_stats(); } if(!rhash_data.interrupted) { if(opt.bt_batch_file && rhash_data.rctx) { rhash_final(rhash_data.rctx, 0); save_torrent_to(opt.bt_batch_file, rhash_data.rctx); } if((opt.flags & OPT_SPEED) && !(opt.mode & (MODE_CHECK | MODE_UPDATE)) && rhash_data.processed > 1) { double time = rhash_timer_stop(&timer); print_time_stats(time, rhash_data.total_size, 1); } } else { /* check if interruption was not reported yet */ if(rhash_data.interrupted == 1) report_interrupted(); } options_destroy(&opt); rhash_destroy(&rhash_data); /* return non-zero error code if error occurred */ return (rhash_data.error_flag ? 1 : search_opt.errors_count ? 2 : rhash_data.interrupted ? 3 : 0); }
/** * Check hash sums in a hash file. * Lines beginning with ';' and '#' are ignored. * * @param hash_file_path - the path of the file with hash sums to verify. * @param chdir - true if function should emulate chdir to directory of filepath before checking it. * @return zero on success, -1 on fail */ int check_hash_file(file_t* file, int chdir) { FILE *fd; char buf[2048]; size_t pos; const char *ralign; timedelta_t timer; struct file_info info; const char* hash_file_path = file->path; int res = 0, line_num = 0; double time; /* process --check-embedded option */ if(opt.mode & MODE_CHECK_EMBEDDED) { unsigned crc32_be; if(find_embedded_crc32(hash_file_path, &crc32_be)) { /* initialize file_info structure */ memset(&info, 0, sizeof(info)); info.full_path = rsh_strdup(hash_file_path); info.file = file; file_info_set_print_path(&info, info.full_path); info.sums_flags = info.hc.hash_mask = RHASH_CRC32; info.hc.flags = HC_HAS_EMBCRC32; info.hc.embedded_crc32_be = crc32_be; res = verify_sums(&info); fflush(rhash_data.out); if(!rhash_data.interrupted) { if(res == 0) rhash_data.ok++; else if(res == -1 && errno == ENOENT) rhash_data.miss++; rhash_data.processed++; } free(info.full_path); file_info_destroy(&info); } else { log_warning(_("file name doesn't contain a CRC32: %s\n"), hash_file_path); return -1; } return 0; } /* initialize statistics */ rhash_data.processed = rhash_data.ok = rhash_data.miss = 0; rhash_data.total_size = 0; if(file->mode & FILE_IFSTDIN) { fd = stdin; hash_file_path = "<stdin>"; } else if( !(fd = rsh_fopen_bin(hash_file_path, "rb") )) { log_file_error(hash_file_path); return -1; } pos = strlen(hash_file_path)+16; ralign = str_set(buf, '-', (pos < 80 ? 80 - (int)pos : 2)); fprintf(rhash_data.out, _("\n--( Verifying %s )%s\n"), hash_file_path, ralign); fflush(rhash_data.out); rhash_timer_start(&timer); /* mark the directory part of the path, by setting the pos index */ if(chdir) { pos = strlen(hash_file_path); for(; pos > 0 && !IS_PATH_SEPARATOR(hash_file_path[pos]); pos--); if(IS_PATH_SEPARATOR(hash_file_path[pos])) pos++; } else pos = 0; /* read crc file line by line */ for(line_num = 0; fgets(buf, 2048, fd); line_num++) { char* line = buf; char* path_without_ext = NULL; /* skip unicode BOM */ if(line_num == 0 && buf[0] == (char)0xEF && buf[1] == (char)0xBB && buf[2] == (char)0xBF) line += 3; if(*line == 0) continue; /* skip empty lines */ if(is_binary_string(line)) { log_error(_("file is binary: %s\n"), hash_file_path); if(fd != stdin) fclose(fd); return -1; } /* skip comments and empty lines */ if(IS_COMMENT(*line) || *line == '\r' || *line == '\n') continue; memset(&info, 0, sizeof(info)); if(!hash_check_parse_line(line, &info.hc, !feof(fd))) continue; if(info.hc.hash_mask == 0) continue; info.print_path = info.hc.file_path; info.sums_flags = info.hc.hash_mask; /* see if crc file contains a hash sum without a filename */ if(info.print_path == NULL) { char* point; path_without_ext = rsh_strdup(hash_file_path); point = strrchr(path_without_ext, '.'); if(point) { *point = '\0'; file_info_set_print_path(&info, path_without_ext); } } if(info.print_path != NULL) { file_t file_to_check; int is_absolute = IS_PATH_SEPARATOR(info.print_path[0]); IF_WINDOWS(is_absolute = is_absolute || (info.print_path[0] && info.print_path[1] == ':')); /* if filename shall be prepended by a directory path */ if(pos && !is_absolute) { size_t len = strlen(info.print_path); info.full_path = (char*)rsh_malloc(pos + len + 1); memcpy(info.full_path, hash_file_path, pos); strcpy(info.full_path + pos, info.print_path); } else { info.full_path = rsh_strdup(info.print_path); } memset(&file_to_check, 0, sizeof(file_t)); file_to_check.path = info.full_path; rsh_file_stat(&file_to_check); info.file = &file_to_check; /* verify hash sums of the file */ res = verify_sums(&info); fflush(rhash_data.out); rsh_file_cleanup(&file_to_check); file_info_destroy(&info); if(rhash_data.interrupted) { free(path_without_ext); break; } /* update statistics */ if(res == 0) rhash_data.ok++; else if(res == -1 && errno == ENOENT) rhash_data.miss++; rhash_data.processed++; } free(path_without_ext); } time = rhash_timer_stop(&timer); fprintf(rhash_data.out, "%s\n", str_set(buf, '-', 80)); print_check_stats(); if(rhash_data.processed != rhash_data.ok) rhash_data.error_flag = 1; if(opt.flags & OPT_SPEED && rhash_data.processed > 1) { print_time_stats(time, rhash_data.total_size, 1); } rhash_data.processed = 0; res = ferror(fd); /* check that crc file has been read without errors */ if(fd != stdin) fclose(fd); return (res == 0 ? 0 : -1); }
/** * Parse config file of the program. * * @return 0 on success, -1 on fail */ static int read_config(void) { #define LINE_BUF_SIZE 2048 char buf[LINE_BUF_SIZE]; FILE* fd; parsed_option_t option; int res; /* initialize conf_opt and opt structures */ memset(&conf_opt, 0, sizeof(opt)); conf_opt.find_max_depth = -1; if(!find_conf_file()) return 0; fd = fopen(conf_opt.config_file, "r"); if(!fd) return -1; while(fgets(buf, LINE_BUF_SIZE, fd)) { size_t index; cmdline_opt_t* t; char* line = str_trim(buf); char *name, *value; if(*line == 0 || IS_COMMENT(*line)) continue; /* search for '=' */ index = strcspn(line, "="); if(line[index] == 0) { log_warning(_("%s: can't parse line \"%s\"\n"), conf_opt.config_file, line); continue; } line[index] = 0; name = str_trim(line); for(t = cmdline_opt; t->type; t++) { if(strcmp(name, t->long_name) == 0) { break; } } if(!t->type) { log_warning(_("%s: unknown option \"%s\"\n"), conf_opt.config_file, line); continue; } value = str_trim(line + index + 1); /* process a long option */ if(is_param_required(t->type)) { rsh_vector_add_ptr(opt.mem, (value = rsh_strdup(value)));; } else { /* possible boolean values for a config file variable */ static const char* strings[] = {"1", "on", "yes", 0}; const char** cmp; for(cmp = strings; *cmp && strcmp(value, *cmp); cmp++); if(*cmp == 0) continue; } option.name = name; option.parameter = value; option.o = t; apply_option(&conf_opt, &option); } res = fclose(fd); #ifdef _WIN32 if( (opt.flags & OPT_ENCODING) == 0 ) opt.flags |= (conf_opt.flags & OPT_ENCODING); #endif return (res == 0 ? 0 : -1); }