/** * Process given command line option * * @param opts the structure to store results of option processing * @param option option to process */ static void apply_option(options_t *opts, parsed_option_t* option) { cmdline_opt_t* o = option->o; unsigned short option_type = o->type; char* value = NULL; /* check if option requires a parameter */ if(is_param_required(option_type)) { if(!option->parameter) { log_error(_("argument is required for option %s\n"), option->name); rsh_exit(2); } #ifdef _WIN32 /* convert from UTF-16 if not a filepath */ if(option_type != F_OPTH) { value = w2c((wchar_t*)option->parameter); rsh_vector_add_ptr(opt.mem, value); } else #endif { value = (char*)option->parameter; } } /* process option, choosing the method by type */ switch(option_type) { case F_UFLG: case F_UENC: *(unsigned*)((char*)opts + ((char*)o->ptr - (char*)&opt)) |= o->param; break; case F_CSTR: case F_OPTH: /* save the option parameter */ *(char**)((char*)opts + ((char*)o->ptr - (char*)&opt)) = value; break; case F_PFNC: /* call option parameter handler */ ( ( void(*)(options_t *, char*, unsigned) )o->ptr )(opts, value, o->param); break; case F_VFNC: ( ( void(*)(options_t *) )o->ptr )(opts); /* call option handler */ break; case F_PRNT: log_msg("%s", (char*)o->ptr); rsh_exit(0); break; default: assert(0); /* impossible option type */ } }
static void setup_log_stream(FILE **p_stream, const opt_tchar* stream_path) { if (stream_path && !(*p_stream = rsh_tfopen(stream_path, RSH_T("w"))) ) { log_file_error(t2c(stream_path)); rsh_exit(2); } }
/** * Say nya! Keep secret! =) */ static void nya(void) { fprintf(rhash_data.out, " /\\__/\\\n (^ _ ^.) %s\n (_uu__)\n", /* TRANSLATORS: Keep secret ;) */ _("Purrr...")); rsh_exit(0); }
/** * Print program help. */ static void print_help(void) { assert(rhash_data.out != NULL); /* print program version and usage */ fprintf(rhash_data.out, _("%s\n" "Usage: %s [OPTION...] [FILE | -]...\n" " %s --printf=<format string> [FILE | -]...\n\n"), VERSION_STRING, CMD_FILENAME, CMD_FILENAME); fprintf(rhash_data.out, _("Options:\n")); print_help_line(" -V, --version ", _("Print program version and exit.\n")); print_help_line(" -h, --help ", _("Print this help screen.\n")); print_help_line(" -C, --crc32 ", _("Calculate CRC32 hash sum.\n")); print_help_line(" --md4 ", _("Calculate MD4 hash sum.\n")); print_help_line(" -M, --md5 ", _("Calculate MD5 hash sum.\n")); print_help_line(" -H, --sha1 ", _("Calculate SHA1 hash sum.\n")); print_help_line(" --sha224, --sha256, --sha384, --sha512 ", _("Calculate SHA2 hash sum.\n")); print_help_line(" -T, --tth ", _("Calculate TTH sum.\n")); print_help_line(" --btih ", _("Calculate BitTorrent InfoHash.\n")); print_help_line(" -A, --aich ", _("Calculate AICH hash.\n")); print_help_line(" -E, --ed2k ", _("Calculate eDonkey hash sum.\n")); print_help_line(" -L, --ed2k-link ", _("Calculate and print eDonkey link.\n")); print_help_line(" --tiger ", _("Calculate Tiger hash sum.\n")); print_help_line(" -G, --gost ", _("Calculate GOST R 34.11-94 hash.\n")); print_help_line(" --gost-cryptopro ", _("CryptoPro version of the GOST R 34.11-94 hash.\n")); print_help_line(" --ripemd160 ", _("Calculate RIPEMD-160 hash.\n")); print_help_line(" --has160 ", _("Calculate HAS-160 hash.\n")); print_help_line(" --edonr256, --edonr512 ", _("Calculate EDON-R 256/512 hash.\n")); print_help_line(" --snefru128, --snefru256 ", _("Calculate SNEFRU-128/256 hash.\n")); print_help_line(" -a, --all ", _("Calculate all supported hashes.\n")); print_help_line(" -c, --check ", _("Check hash files specified by command line.\n")); print_help_line(" -u, --update ", _("Update hash files specified by command line.\n")); print_help_line(" -e, --embed-crc ", _("Rename files by inserting crc32 sum into name.\n")); print_help_line(" -k, --check-embedded ", _("Verify files by crc32 sum embedded in their names.\n")); print_help_line(" --list-hashes ", _("List the names of supported hashes, one per line.\n")); print_help_line(" -B, --benchmark ", _("Benchmark selected algorithm.\n")); print_help_line(" -v, --verbose ", _("Be verbose.\n")); print_help_line(" -r, --recursive ", _("Process directories recursively.\n")); print_help_line(" --skip-ok ", _("Don't print OK messages for successfully verified files.\n")); print_help_line(" -i, --ignore-case ", _("Ignore case of filenames when updating hash files.\n")); print_help_line(" --percents ", _("Show percents, while calculating or checking hashes.\n")); print_help_line(" --speed ", _("Output per-file and total processing speed.\n")); print_help_line(" --maxdepth=<n> ", _("Descend at most <n> levels of directories.\n")); print_help_line(" -o, --output=<file> ", _("File to output calculation or checking results.\n")); print_help_line(" -l, --log=<file> ", _("File to log errors and verbose information.\n")); print_help_line(" --sfv ", _("Print hash sums, using SFV format (default).\n")); print_help_line(" --bsd ", _("Print hash sums, using BSD-like format.\n")); print_help_line(" --simple ", _("Print hash sums, using simple format.\n")); print_help_line(" -m, --magnet ", _("Print hash sums as magnet links.\n")); print_help_line(" --torrent ", _("Create torrent files.\n")); #ifdef _WIN32 print_help_line(" --ansi ", _("Use Windows codepage for output (Windows only).\n")); #endif print_help_line(" --template=<file> ", _("Load a printf-like template from the <file>\n")); print_help_line(" -p, --printf=<format string> ", _("Format and print hash sums.\n See the RHash manual for details.\n")); rsh_exit(0); }
/** * Allocates a buffer via calloc with reporting memory error to stderr. * * @param num number of elements to be allocated * @param size size of elements * @param srcfile source file to report error on fail * @param srcline source code line to be reported on fail * @return allocated block */ void* rhash_calloc(size_t num, size_t size, const char* srcfile, int srcline) { void* res = calloc(num, size); if (!res) { rsh_report_error(srcfile, srcline, "calloc(%u, %u) failed\n", (unsigned)num, (unsigned)size); rsh_exit(2); } return res; }
/** * Reallocates a buffer via realloc with reporting memory error to stderr. * * @param mem a memory block to re-allocate * @param size the new size of the block * @param srcfile source file to report error on fail * @param srcline source code line to be reported on fail * @return re-allocated memory buffer */ void* rhash_realloc(void* mem, size_t size, const char* srcfile, int srcline) { void* res = realloc(mem, size); if (!res) { rsh_report_error(srcfile, srcline, "realloc(%p, %u) failed\n", mem, (unsigned)size); rsh_exit(2); } return res; }
/** * Set the length of a BitTorrent file piece. * * @param o pointer to the processed option * @param number string containing the piece length number * @param param unused parameter */ static void set_bt_piece_length(options_t *o, char* number, unsigned param) { (void)param; if(strspn(number, "0123456789") < strlen(number)) { log_error(_("bt-piece-length parameter is not a number: %s\n"), number); rsh_exit(2); } o->bt_piece_length = (size_t)atoi(number); }
/** * Process on --maxdepth option. * * @param o pointer to the processed option * @param number the string containing the max-depth number * @param param unused parameter */ static void set_max_depth(options_t *o, char* number, unsigned param) { (void)param; if(strspn(number, "0123456789") < strlen(number)) { log_error(_("maxdepth parameter is not a number: %s\n"), number); rsh_exit(2); } o->find_max_depth = atoi(number); }
/** * Allocates a buffer via malloc with reporting memory error to stderr. * * @param size size of the block to allocate * @param srcfile source file to report error on fail * @param srcline source code line to be reported on fail * @return allocated block */ void* rhash_malloc(size_t size, const char* srcfile, int srcline) { void* res = malloc(size); if (!res) { rsh_report_error(srcfile, srcline, "%s(%u) failed\n", "malloc", (unsigned)size); rsh_exit(2); } return res; }
/** * Print the names of all supported hash algorithms to the console. */ static void list_hashes(void) { int id; for(id = 1; id < RHASH_ALL_HASHES; id <<= 1) { const char* hash_name = rhash_get_name(id); if(hash_name) fprintf(rhash_data.out, "%s\n", hash_name); } rsh_exit(0); }
/** * Duplicate c-string with reporting memory error to stderr. * * @param str the zero-terminated string to duplicate * @param srcfile source file to report error on fail * @param srcline source code line to be reported on fail * @return allocated memory buffer with copied string */ char* rhash_strdup(const char* str, const char* srcfile, int srcline) { #ifndef __STRICT_ANSI__ char* res = strdup(str); #else char* res = (char*)malloc(strlen(str)+1); if (res) strcpy(res, str); #endif if (!res) { rsh_report_error(srcfile, srcline, "strdup(\"%s\") failed\n", str); rsh_exit(2); } return res; }
/** * Duplicate wide string with reporting memory error to stderr. * * @param str the zero-terminated string to duplicate * @param srcfile source file to report error on fail * @param srcline source code line to be reported on fail * @return allocated memory buffer with copied string */ wchar_t* rhash_wcsdup(const wchar_t* str, const char* srcfile, int srcline) { #ifndef __STRICT_ANSI__ wchar_t* res = wcsdup(str); #else wchar_t* res = (wchar_t*)malloc((wcslen(str) + 1) * sizeof(wchar_t)); if (res) wcscpy(res, str); #endif if (!res) { rsh_report_error(srcfile, srcline, "wcsdup(\"%u\") failed\n", (wcslen(str) + 1)); rsh_exit(2); } return res; }
/** * Set the path separator to use when printing paths * * @param o pointer to the processed option * @param sep file separator, can be only '/' or '\' * @param param unused parameter */ static void set_path_separator(options_t *o, char* sep, unsigned param) { (void)param; if((*sep == '/' || *sep == '\\') && sep[1] == 0) { o->path_separator = *sep; #if defined(_WIN32) /* MSYS environment changes '/' in command line to HOME, see http://www.mingw.org/wiki/FAQ */ } else if(getenv("MSYSTEM") || getenv("TERM")) { log_warning(_("wrong path-separator, use '//' instead of '/' on MSYS\n")); o->path_separator = '/'; #endif } else { log_error(_("path-separator is not '/' or '\\': %s\n"), sep); rsh_exit(2); } }
/** * Handler for the SIGINT signal, sent when user press Ctrl+C. * The handler prints message and exits the program. * * @param signum the processed signal identifier SIGINT */ static void ctrl_c_handler(int signum) { fflush(rhash_data.out); fprintf(rhash_data.log, _("Interrupted by user...\n")); fflush(rhash_data.log); /* print intermediate check results if process was interrupted */ if((opt.mode & (MODE_CHECK | MODE_CHECK_EMBEDDED)) && rhash_data.processed > 0) { print_check_stats(); fflush(rhash_data.out); } /* restore precious signal handler and call it */ if(prev_sigint_handler && prev_sigint_handler != SIG_ERR) { signal(signum, prev_sigint_handler); prev_sigint_handler(signum); } rsh_exit(1); /* exit the program */ }
static void setup_log_stream(FILE **p_stream, const char *stream_path) { if(stream_path) { #ifdef _WIN32 if( !(*p_stream = _wfsopen((wchar_t*)stream_path, L"w", _SH_DENYNO)) ) { stream_path = w2c((wchar_t*)stream_path); #else if( !(*p_stream = fopen(stream_path, "w")) ) { #endif log_error(_("%s: %s\n"), stream_path, strerror(errno)); rsh_exit(2); } } } /** * Initialize pointers to output functions. */ void setup_output(void) { rhash_data.out = stdout; rhash_data.log = stderr; if(opt.flags & OPT_PERCENTS) { /* we don't use _fileno() cause it is not in ISO C90, and so * is incompatible with the GCC -ansi option */ if(rhash_data.log == stderr && isatty(2)) { percents_output = &p_perc; /* one-line percents */ } else { percents_output = &dots_perc; /* print percents as dots */ } } else { percents_output = &dummy_perc; /* no percents */ } setup_log_stream(&rhash_data.out, opt.output); setup_log_stream(&rhash_data.log, opt.log); }
/* * This is the core routine for my read term code. Here is where things like * the up arrow key, editing keys, etc are dealt with. */ char *_rsh_do_read_line(){ char c; rsh_buf_clean(&buf); buf.used = 1; /* For empty command lines. */ current = end; DECREMENT(current); rsh_stack_clean(&hist_stack); CUR_SAVE(); /* Now that we have a buffer... Start filling it. */ while ( read(rsh_fd, &c, 1) != 0 ){ /* Handle escape sequences. 0x1b -> hex escape character. */ if ( c == 0x1b ){ _rsh_handle_escape_seq(); /* Regardless, we do not want to put the escape character into the buf */ continue; } /* Reset the backup buffer so up/down arrow works the way we expect. */ rsh_buf_clean(&backup); /* Deal with EOT from the terminal (Cntr-D). Hack to make RSH work for * project submission. */ if ( c == 0x04 ) rsh_exit(0); /* Backspace */ if ( c == 0x7f ){ _rsh_do_backspace(); goto _display; } /* * Deal with the newline. Otherwise, just add the character to the buffer. */ if ( c == '\n' ) rsh_buf_append(&buf, c); else rsh_buf_insert(&buf, c); /* Now redisplay the buffer. */ _display: rsh_buf_display(&buf); /* If we hit a NL then we should return. */ if ( c == '\n') break; } /* Make sure the buffer is NULL terminated. */ rsh_buf_append(&buf, 0); /* Now set the terminal cursor to where its exepcted to be. */ CUR_NEXT_LINE(); fflush(stdout); return buf.buf; }
/** * Log a message and exit the program. * * @param msg the message to log */ static void die(const char* msg) { log_error(msg); rsh_exit(2); }
/** * Log an error about unknown option and exit the program. * * @param option_name the name of the unknown option encountered */ static void fail_on_unknow_option(const char* option_name) { log_error(_("unknown option: %s"), (option_name ? option_name : "?")); rsh_exit(2); }
/** * Parse command line arguments. * * @param cmd_line structure to store parsed options data */ static void parse_cmdline_options(struct parsed_cmd_line_t* cmd_line) { int argc; int n_files = 0, b_opt_end = 0; rsh_tchar** files; rsh_tchar **parg, **end_arg; parsed_option_t *next_opt; #ifdef _WIN32 parg = cmd_line->warg = CommandLineToArgvW(GetCommandLineW(), &argc); if( NULL == parg || argc < 1) { die(_("CommandLineToArgvW failed\n")); } #else argc = cmd_line->argc; parg = cmd_line->argv; #endif /* allocate array for files */ files = (rsh_tchar**)rsh_malloc(argc * sizeof(rsh_tchar*)); end_arg = parg + argc; /* loop by program arguments */ for(parg++; parg < end_arg; parg++) { /* if argument is not an option */ if((*parg)[0] != RSH_T('-') || (*parg)[1] == 0 || b_opt_end) { /* it's a file, note that '-' is interpreted as stdin */ files[n_files++] = *parg; continue; } assert((*parg)[0] == RSH_T('-') && (*parg)[1] != 0); if((*parg)[1] == L'-' && (*parg)[2] == 0) { b_opt_end = 1; /* string "--" means end of options */ continue; } /* check for "--" */ if((*parg)[1] == RSH_T('-')) { cmdline_opt_t *t; /* allocate parsed_option */ rsh_blocks_vector_add_empty(&cmd_line->options, 16, sizeof(parsed_option_t)); next_opt = rsh_blocks_vector_get_item(&cmd_line->options, cmd_line->options.size - 1, 16, parsed_option_t); /* find the long option */ parse_long_option(next_opt, &parg); t = next_opt->o; /* process encoding and -o/-l options early */ if(is_output_modifier(t->type)) { apply_option(&opt, next_opt); } } else if((*parg)[1] != 0) { /* found '-'<some string> */ rsh_tchar* ptr; /* parse short options. A string of several characters is interpreted * as separate short options */ for(ptr = *parg + 1; *ptr; ptr++) { cmdline_opt_t *t; char ch = (char)*ptr; #ifdef _WIN32 if(((unsigned)*ptr) >= 128) { ptr[1] = 0; fail_on_unknow_option(w2c(ptr)); } #endif /* allocate parsed_option */ rsh_blocks_vector_add_empty(&cmd_line->options, 16, sizeof(parsed_option_t)); next_opt = rsh_blocks_vector_get_item(&cmd_line->options, cmd_line->options.size - 1, 16, parsed_option_t); next_opt->buf[0] = '-', next_opt->buf[1] = ch, next_opt->buf[2] = '\0'; next_opt->name = next_opt->buf; next_opt->parameter = NULL; /* search for the short option */ for(t = cmdline_opt; t->type && ch != t->short1 && ch != t->short2; t++); if(!t->type) fail_on_unknow_option(next_opt->buf); next_opt->o = t; if(is_param_required(t->type)) { next_opt->parameter = (ptr[1] ? ptr + 1 : *(++parg)); if(!next_opt->parameter) { /* note: need to check for parameter here, for early -o/-l options processing */ log_error(_("argument is required for option %s\n"), next_opt->name); rsh_exit(2); } } /* process encoding and -o/-l options early */ if(is_output_modifier(t->type)) { apply_option(&opt, next_opt); } if(next_opt->parameter) break; /* a parameter ends the short options string */ } } } /* for */ cmd_line->n_files = n_files; cmd_line->files = files; }
/** * 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); }
/** * 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); }