예제 #1
0
/**
 * 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 */
	}
}
예제 #2
0
파일: output.c 프로젝트: rhash/RHash
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);
	}
}
예제 #3
0
/**
 * Say nya! Keep secret! =)
 */
static void nya(void)
{
	fprintf(rhash_data.out, "  /\\__/\\\n (^ _ ^.) %s\n  (_uu__)\n",
		/* TRANSLATORS: Keep secret ;) */
		_("Purrr..."));
	rsh_exit(0);
}
예제 #4
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);
}
예제 #5
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;
}
예제 #6
0
/**
 * 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;
}
예제 #7
0
/**
 * 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);
}
예제 #8
0
/**
 * 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);
}
예제 #9
0
/**
 * 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;
}
예제 #10
0
/**
 * 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);
}
예제 #11
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;
}
예제 #12
0
/**
 * 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;
}
예제 #13
0
/**
 * 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);
	}
}
예제 #14
0
/**
 * 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 */
}
예제 #15
0
파일: output.c 프로젝트: SBasalaev/RHash
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);
}
예제 #16
0
/*
 * 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;

}
예제 #17
0
/**
 * 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);
}
예제 #18
0
/**
 * 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);
}
예제 #19
0
/**
 * 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;
}
예제 #20
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);
}
예제 #21
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);
}