rhash-1.4.6/0000775000000000000000000000000015010731530011322 5ustar rootrootrhash-1.4.6/file_set.h0000664000000000000000000000222114703622112013265 0ustar rootroot/* file_set.h - functions to manipulate a set of files with its message digests */ #ifndef FILE_SET_H #define FILE_SET_H #ifdef __cplusplus extern "C" { #endif /** * Filepath with its string-hash (for fast search). */ typedef struct file_set_item { unsigned hash; char* filepath; char* search_filepath; /* for case-insensitive comparison */ } file_set_item; /* array to store filenames from a parsed hash file */ struct vector_t; typedef struct vector_t file_set; #define file_set_new() rsh_vector_new((void(*)(void*))file_set_item_free) /* allocate new file set */ #define file_set_free(set) rsh_vector_free(set) /* free memory */ #define file_set_get(set, index) ((file_set_item*)((set)->array[index])) /* get i-th element */ #define file_set_add(set, item) rsh_vector_add_ptr(set, item) /* add a file_set_item to file_set */ void file_set_item_free(file_set_item* item); void file_set_add_name(file_set* set, const char* filename); void file_set_sort(file_set* set); void file_set_sort_by_path(file_set* set); int file_set_exist(file_set* set, const char* filename); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* FILE_SET_H */ rhash-1.4.6/calc_sums.h0000664000000000000000000000443215010476501013453 0ustar rootroot/* calc_sums.h */ #ifndef CALC_SUMS_H #define CALC_SUMS_H #include "common_func.h" #include "file.h" #include "hash_check.h" #ifdef __cplusplus extern "C" { #endif /* Hash function identifiers and bit masks */ #define bit64_to_hash_id(bit64) ((unsigned)RHASH_EXTENDED_BIT ^ get_ctz64(bit64)) #define hash_id_to_bit64(hash_id) ((hash_id) & RHASH_EXTENDED_BIT ? \ (uint64_t)1 << (unsigned)((hash_id) & ~RHASH_EXTENDED_BIT): (uint64_t)(hash_id)) #define hash_id_to_extended(hash_id) ((hash_id) & RHASH_EXTENDED_BIT ? hash_id : \ (unsigned)RHASH_EXTENDED_BIT ^ get_ctz(hash_id)) int hash_mask_to_hash_ids(uint64_t hash_mask, unsigned max_count, unsigned* hash_ids, unsigned* out_count); int set_openssl_enabled_hash_mask(uint64_t hash_mask); uint64_t get_openssl_supported_hash_mask(void); uint64_t get_all_supported_hash_mask(void); /* Hash function calculation */ /** * Information about a file to calculate/verify message digests for. */ struct file_info { uint64_t size; /* the size of the hashed file */ uint64_t msg_offset; /* rctx->msg_size before hashing this file */ uint64_t time; /* file processing time in milliseconds */ file_t* file; /* the file being processed */ struct rhash_context* rctx; /* state of hash algorithms */ struct hash_parser* hp; /* parsed line of a hash file */ uint64_t hash_mask; /* mask of ids of calculated hash functions */ int processing_result; /* -1/-2 for i/o error, 0 on success, 1 on a hash mismatch */ }; int calc_sums(struct file_info* info); int calculate_and_print_sums(FILE* out, file_t* out_file, file_t* file); int find_embedded_crc32(file_t* file, unsigned* crc32); int rename_file_by_embeding_crc32(struct file_info* info); int save_torrent_to(file_t* torrent_file, struct rhash_context* rctx); /* Benchmarking */ /** Benchmarking flag: measure the CPU "clocks per byte" speed */ #define BENCHMARK_CPB 1 /** Benchmarking flag: print benchmark result in tab-delimited format */ #define BENCHMARK_RAW 2 /** * Benchmark hash functions. * * @param hash_mask bit mask for hash functions to benchmark * @param flags benchmark flags, can contain BENCHMARK_CPB, BENCHMARK_RAW */ void run_benchmark(uint64_t hash_mask, unsigned flags); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* CALC_SUMS_H */ rhash-1.4.6/output.h0000664000000000000000000000434214703622112013041 0ustar rootroot/* output.h */ #ifndef OUTPUT_H #define OUTPUT_H #include #include #ifdef __cplusplus extern "C" { #endif struct file_info; struct file_t; /** * A 'method' to output percents. */ struct percents_output_info_t { int (*init)(struct file_info* info); void (*update)(struct file_info* info, uint64_t offset); int (*finish)(struct file_info* info, int process_res); const char* name; }; /* pointer to the selected percents output method */ extern struct percents_output_info_t* percents_output; #define init_percents(info) percents_output->init(info) #define update_percents(info, offset) percents_output->update(info, offset) #define finish_percents(info, process_res) percents_output->finish(info, process_res) /* initialization of percents output method */ void setup_output(void); void setup_percents(void); enum FileOutputFlags { OutDefaultFlags = 0x0, OutForceUtf8 = 0x1, OutCountSymbols = 0x2, OutBaseName = 0x4, OutDirName = 0x8, OutEscape = 0x10, OutEscapePrefixed = 0x20, }; int fprintf_file_t(FILE* out, const char* format, struct file_t* file, unsigned output_flags); int fprint_urlencoded(FILE* out, const char* str, int upper_case); void log_msg(const char* format, ...); void log_msg_file_t(const char* format, struct file_t* file); void die(const char* format, ...); #define log_warning log_error #define log_warning_msg_file_t log_error_msg_file_t void log_error(const char* format, ...); void log_error_file_t(struct file_t* file); void log_error_msg_file_t(const char* format, struct file_t* file); void fatal_error_impl(const char* file, int line, const char* format, ...); #define RSH_REQUIRE_IMPL(condition, file, line, ...) \ while(!(condition)) { \ fatal_error_impl(file, line, __VA_ARGS__); \ } #define RSH_REQUIRE(condition, ...) \ RSH_REQUIRE_IMPL(condition, __FILE__, __LINE__, __VA_ARGS__) void report_interrupted(void); int print_verifying_header(struct file_t* file); int print_verifying_footer(void); int print_check_stats(void); int print_verbose_algorithms(FILE* out, uint64_t hash_mask); void print_time_stats(uint64_t time, uint64_t size, int total); void print_file_time_stats(struct file_info* info); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* OUTPUT_H */ rhash-1.4.6/Makefile0000664000000000000000000003033415010476501012771 0ustar rootroot include config.mak HEADERS = calc_sums.h hash_print.h common_func.h hash_update.h file.h file_mask.h file_set.h find_file.h hash_check.h output.h parse_cmdline.h rhash_main.h win_utils.h platform.h version.h SOURCES = calc_sums.c hash_print.c common_func.c hash_update.c file.c file_mask.c file_set.c find_file.c hash_check.c output.c parse_cmdline.c rhash_main.c win_utils.c OBJECTS = $(SOURCES:.c=.o) WIN_DIST_FILES = dist/MD5.bat dist/magnet.bat dist/rhashrc.sample OTHER_FILES = configure Makefile ChangeLog INSTALL.md COPYING README.md \ build/vc-2010/rhash.vcxproj dist/rhash.spec.in dist/rhash.1.in dist/rhash.1.win.sed \ docs/CONTRIBUTING.md docs/LIBRHASH.md librhash/Doxyfile po/rhash.pot \ tests/test_rhash.sh tests/test1K.data LIBRHASH_FILES = librhash/algorithms.c librhash/algorithms.h \ librhash/byte_order.c librhash/byte_order.h librhash/plug_openssl.c librhash/plug_openssl.h \ librhash/rhash.c librhash/rhash.h librhash/rhash_torrent.c librhash/rhash_torrent.h \ librhash/aich.c librhash/aich.h librhash/blake2b.c librhash/blake2b.h \ librhash/blake2s.c librhash/blake2s.h librhash/blake3.c librhash/blake3.h \ librhash/crc32.c librhash/crc32.h \ librhash/ed2k.c librhash/ed2k.h librhash/edonr.c librhash/edonr.h \ librhash/gost12.c librhash/gost12.h librhash/gost94.c librhash/gost94.h \ librhash/has160.c librhash/has160.h librhash/hex.c librhash/hex.h librhash/md4.c \ librhash/md4.h librhash/md5.c librhash/md5.h librhash/ripemd-160.c librhash/ripemd-160.h \ librhash/sha1.c librhash/sha1.h librhash/sha3.c librhash/sha3.h \ librhash/sha256.c librhash/sha256.h librhash/sha512.c librhash/sha512.h \ librhash/sha_ni.c librhash/sha_ni.h librhash/snefru.c librhash/snefru.h \ librhash/tiger.c librhash/tiger.h librhash/tiger_sbox.c \ librhash/torrent.h librhash/torrent.c librhash/tth.c librhash/tth.h \ librhash/whirlpool.c librhash/whirlpool.h librhash/whirlpool_sbox.c \ librhash/test_lib.c librhash/test_lib.h librhash/test_utils.c librhash/test_utils.h \ librhash/ustd.h librhash/util.c librhash/util.h librhash/Makefile I18N_FILES = po/ca.po po/de.po po/en_AU.po po/es.po po/fr.po po/gl.po po/it.po po/pt_BR.po po/ro.po po/ru.po po/uk.po ALL_FILES = $(SOURCES) $(HEADERS) $(LIBRHASH_FILES) $(OTHER_FILES) $(WIN_DIST_FILES) $(I18N_FILES) SPECFILE = dist/rhash.spec LIBRHASH_PC = dist/librhash.pc RPMTOP = rpms RPMDIRS = SOURCES SPECS BUILD SRPMS RPMS RHASH_NAME = rhash RHASH_BINARY = rhash$(EXEC_EXT) INSTALL_PROGRAM = $(INSTALL) -m 755 INSTALL_DATA = $(INSTALL) -m 644 all: $(BUILD_TARGETS) build: $(RHASH_BINARY) lib-shared: $(LIBRHASH_SHARED) lib-static: $(LIBRHASH_STATIC) install: build-install-binary install-data install-symlinks $(EXTRA_INSTALL) install-data: install-man install-conf uninstall: uninstall-binary uninstall-data uninstall-symlinks uninstall-lib uninstall-gmo uninstall-pkg-config config.mak: echo "Run the ./configure script first" && false # creating archives WIN_SUFFIX = win32 PACKAGE_NAME = $(RHASH_NAME)-$(VERSION) ARCHIVE_TARGZ = $(PACKAGE_NAME)-src.tar.gz ARCHIVE_TARGZ_CORE = $(RHASH_NAME)-core-$(VERSION)-src.tar.gz ARCHIVE_TARGZ_BINDINGS = $(RHASH_NAME)-bindings-$(VERSION)-src.tar.gz ARCHIVE_TARGZ_DEB = $(RHASH_NAME)_$(VERSION).orig.tar.gz ARCHIVE_XZ = $(PACKAGE_NAME)-src.tar.xz ARCHIVE_ZIP = $(PACKAGE_NAME)-$(WIN_SUFFIX).zip WIN_ZIP_DIR = RHash-$(VERSION)-$(WIN_SUFFIX) dist: tgz win-dist: zip zip: $(ARCHIVE_ZIP) dgz: check $(ARCHIVE_TARGZ_DEB) build-install-binary: $(BUILD_TARGETS) +$(MAKE) install-binary mkdir-bin: $(INSTALL) -d $(BINDIR) # install binary without (re-)compilation install-binary: mkdir-bin $(INSTALL_PROGRAM) $(RHASH_BINARY) $(BINDIR)/$(RHASH_BINARY) install-man: $(INSTALL) -d $(MANDIR)/man1 $(INSTALL_DATA) dist/rhash.1 $(MANDIR)/man1/rhash.1 install-conf: $(INSTALL) -d $(SYSCONFDIR) tr -d \\r < dist/rhashrc.sample > rc.tmp && $(INSTALL_DATA) rc.tmp $(SYSCONFDIR)/rhashrc rm -f rc.tmp # dependencies should be properly set, otherwise 'make -j' can fail install-symlinks: mkdir-bin install-man install-binary cd $(BINDIR) && for f in $(SYMLINKS); do $(LN_S) $(RHASH_BINARY) $$f$(EXEC_EXT); done cd $(MANDIR)/man1 && for f in $(SYMLINKS); do $(LN_S) rhash.1 $$f.1; done install-pkg-config: $(INSTALL) -d $(PKGCONFIGDIR) $(INSTALL_DATA) $(LIBRHASH_PC) $(PKGCONFIGDIR)/ uninstall-binary: rm -f $(BINDIR)/$(RHASH_BINARY) uninstall-data: rm -f $(MANDIR)/man1/rhash.1 uninstall-symlinks: for f in $(SYMLINKS); do rm -f $(BINDIR)/$$f$(EXEC_EXT) $(MANDIR)/man1/$$f.1; done uninstall-pkg-config: rm -f $(PKGCONFIGDIR)/librhash.pc uninstall-lib: +cd librhash && $(MAKE) uninstall-lib install-lib-static: $(LIBRHASH_STATIC) install-lib-headers +cd librhash && $(MAKE) install-lib-static install-lib-shared: $(LIBRHASH_SHARED) +cd librhash && $(MAKE) install-lib-shared install-lib-headers: +cd librhash && $(MAKE) install-lib-headers install-lib-so-link: +cd librhash && $(MAKE) install-so-link $(LIBRHASH_SHARED): $(LIBRHASH_FILES) +cd librhash && $(MAKE) lib-shared $(LIBRHASH_STATIC): $(LIBRHASH_FILES) +cd librhash && $(MAKE) lib-static test-lib: test-lib-$(LIBRHASH_TYPE) test-lib-static: $(LIBRHASH_STATIC) +cd librhash && $(MAKE) test-static test-lib-shared: $(LIBRHASH_SHARED) +cd librhash && $(MAKE) test-shared test-libs: $(LIBRHASH_STATIC) $(LIBRHASH_SHARED) +cd librhash && $(MAKE) test-static test-shared test-full: $(RHASH_BINARY) /bin/sh tests/test_rhash.sh $(TEST_OPTIONS) --full ./$(RHASH_BINARY) test: $(RHASH_BINARY) /bin/sh tests/test_rhash.sh $(TEST_OPTIONS) ./$(RHASH_BINARY) print-info: lib-$(LIBRHASH_TYPE) +cd librhash && $(MAKE) print-info # check that source tree is consistent check: grep -q '\* === Version $(VERSION) ===' ChangeLog grep -q '^#define VERSION "$(VERSION)"' version.h test ! -f bindings/version.properties || grep -q '^version=$(VERSION)$$' bindings/version.properties $(RHASH_BINARY): $(OBJECTS) $(LIBRHASH_PATH) $(CC) $(OBJECTS) $(LIBRHASH_PATH) $(LDFLAGS) -o $@ # NOTE: dependences were generated by 'gcc -Ilibrhash -MM *.c' # we are using plain old makefile style to support BSD make calc_sums.o: calc_sums.c calc_sums.h common_func.h file.h hash_check.h \ file_set.h hash_print.h output.h parse_cmdline.h platform.h rhash_main.h \ win_utils.h librhash/rhash.h librhash/rhash_torrent.h $(CC) -c $(CFLAGS) $< -o $@ common_func.o: common_func.c common_func.h output.h parse_cmdline.h \ version.h win_utils.h $(CC) -c $(CFLAGS) $< -o $@ file.o: file.c file.h common_func.h parse_cmdline.h platform.h \ win_utils.h $(CC) -c $(CFLAGS) $< -o $@ file_mask.o: file_mask.c file_mask.h common_func.h file.h $(CC) -c $(CFLAGS) $< -o $@ file_set.o: file_set.c file_set.h common_func.h hash_print.h output.h \ parse_cmdline.h rhash_main.h file.h librhash/rhash.h $(CC) -c $(CFLAGS) $< -o $@ find_file.o: find_file.c find_file.h common_func.h file.h output.h \ parse_cmdline.h platform.h win_utils.h $(CC) -c $(CFLAGS) $< -o $@ hash_check.o: hash_check.c hash_check.h file.h file_set.h calc_sums.h \ common_func.h hash_print.h output.h parse_cmdline.h rhash_main.h \ librhash/rhash.h $(CC) -c $(CFLAGS) $< -o $@ hash_print.o: hash_print.c hash_print.h calc_sums.h common_func.h file.h \ hash_check.h file_set.h output.h parse_cmdline.h rhash_main.h \ win_utils.h librhash/rhash.h $(CC) -c $(CFLAGS) $< -o $@ hash_update.o: hash_update.c hash_update.h calc_sums.h common_func.h \ file.h hash_check.h file_set.h file_mask.h hash_print.h output.h \ parse_cmdline.h rhash_main.h win_utils.h $(CC) -c $(CFLAGS) $< -o $@ output.o: output.c output.h calc_sums.h common_func.h file.h hash_check.h \ file_set.h parse_cmdline.h platform.h rhash_main.h win_utils.h \ librhash/rhash.h $(CC) -c $(CFLAGS) $< -o $@ parse_cmdline.o: parse_cmdline.c parse_cmdline.h calc_sums.h \ common_func.h file.h hash_check.h file_set.h file_mask.h find_file.h \ hash_print.h output.h rhash_main.h win_utils.h librhash/rhash.h $(CC) -c $(CFLAGS) $(CONFCFLAGS) $< -o $@ rhash_main.o: rhash_main.c rhash_main.h file.h calc_sums.h common_func.h \ hash_check.h file_set.h file_mask.h find_file.h hash_print.h \ hash_update.h output.h parse_cmdline.h win_utils.h librhash/rhash.h $(CC) -c $(CFLAGS) $(LOCALECFLAGS) $< -o $@ win_utils.o: win_utils.c win_utils.h common_func.h file.h parse_cmdline.h $(CC) -c $(CFLAGS) $< -o $@ dist/rhash.1.win.html: dist/rhash.1 dist/rhash.1.win.sed sed -f dist/rhash.1.win.sed dist/rhash.1 | rman -fHTML -roff | \ sed -e '/ dist/rhash.1.win.html # verify the result grep -q "utf8" dist/rhash.1.win.html grep -q "APPDATA" dist/rhash.1.win.html dist/rhash.1.html: dist/rhash.1 -rman --version 2>/dev/null && (rman -fHTML -roff dist/rhash.1 | sed -e '/ $@) dist/rhash.1.txt: dist/rhash.1 -groff --version 2>/dev/null && (groff -t -e -mandoc -Tascii dist/rhash.1 | sed -e 's/.\[[0-9]*m//g' > $@) permissions: find . build dist docs librhash po tests -maxdepth 1 -type f -exec chmod -x '{}' \; chmod +x configure tests/test_rhash.sh copy-dist: $(ALL_FILES) permissions rm -rf $(PACKAGE_NAME) mkdir $(PACKAGE_NAME) cp -rl --parents $(ALL_FILES) $(PACKAGE_NAME)/ tgz-core: check +$(MAKE) copy-dist PACKAGE_NAME=$(PACKAGE_NAME)-core tar czf $(ARCHIVE_TARGZ_CORE) --owner=root --group=root $(PACKAGE_NAME)-core/ rm -rf $(PACKAGE_NAME)-core tgz-bindings: +cd bindings && $(MAKE) gzip ARCHIVE_GZIP=../$(ARCHIVE_TARGZ_BINDINGS) tgz: check clean-bindings +$(MAKE) copy-dist +cd bindings && $(MAKE) copy-dist COPYDIR=../$(PACKAGE_NAME)/bindings tar czf $(ARCHIVE_TARGZ) --owner=root:0 --group=root:0 $(PACKAGE_NAME)/ rm -rf $(PACKAGE_NAME) xz: check clean-bindings +$(MAKE) copy-dist +cd bindings && $(MAKE) copy-dist COPYDIR=../$(PACKAGE_NAME)/bindings tar cJf $(ARCHIVE_XZ) --owner=root:0 --group=root:0 $(PACKAGE_NAME)/ rm -rf $(PACKAGE_NAME) win-dir: $(WIN_DIST_FILES) dist/rhash.1.win.html test -s dist/rhash.1.win.html && test -x $(RHASH_BINARY) -rm -rf $(WIN_ZIP_DIR) mkdir $(WIN_ZIP_DIR) cp $(RHASH_BINARY) ChangeLog $(WIN_DIST_FILES) $(WIN_ZIP_DIR)/ cp dist/rhash.1.win.html $(WIN_ZIP_DIR)/rhash-doc.html $(ARCHIVE_ZIP): $(WIN_DIST_FILES) dist/rhash.1.win.html +$(MAKE) win-dir zip -9r $(ARCHIVE_ZIP) $(WIN_ZIP_DIR) rm -rf $(WIN_ZIP_DIR) $(ARCHIVE_TARGZ_DEB) : $(ALL_FILES) +$(MAKE) tgz mv -f $(ARCHIVE_TARGZ) $(ARCHIVE_TARGZ_DEB) # rpm packaging $(SPECFILE): $(SPECFILE).in config.mak sed -e 's/@VERSION@/$(VERSION)/' $(SPECFILE).in > $(SPECFILE) rpm: tgz -for i in $(RPMDIRS); do mkdir -p $(RPMTOP)/$$i; done cp -f $(ARCHIVE_TARGZ) $(RPMTOP)/SOURCES rpmbuild -ba --clean --define "_topdir `pwd`/$(RPMTOP)" $(SPECFILE) mv -f `find $(RPMTOP) -name "*rhash*-$(VERSION)*.rpm"` . rm -rf $(RPMTOP) clean-bindings: +cd bindings && $(MAKE) clean clean-local: rm -f *.o $(RHASH_BINARY) rm -f po/*.gmo po/*.po~ po/compile-gmo.tag distclean: clean-local rm -f config.log config.mak dist/rhash.1 $(SPECFILE) $(LIBRHASH_PC) +cd librhash && $(MAKE) distclean clean: clean-local +cd librhash && $(MAKE) clean update-po: xgettext *.c -k_ -cTRANSLATORS -o po/rhash.pot \ --msgid-bugs-address='Aleksey ' --package-name='RHash' for f in $(I18N_FILES); do \ msgmerge -U $$f po/rhash.pot; \ done po/compile-gmo.tag: $(I18N_FILES) for f in $(I18N_FILES); do \ g=`basename $$f .po`; \ msgfmt -o po/$$g.gmo $$f; \ done touch $@ compile-gmo: po/compile-gmo.tag install-gmo: compile-gmo for f in $(I18N_FILES); do \ l=`basename $$f .po`; \ $(INSTALL) -d $(LOCALEDIR)/$$l/LC_MESSAGES; \ $(INSTALL_DATA) po/$$l.gmo $(LOCALEDIR)/$$l/LC_MESSAGES/rhash.mo; \ done uninstall-gmo: for f in $(I18N_FILES); do \ rm -f $(LOCALEDIR)/`basename $$f .po`/LC_MESSAGES/rhash.mo; \ done .PHONY: all build lib-shared lib-static clean clean-bindings distclean clean-local \ test test-shared test-static test-full test-lib test-libs test-lib-shared test-lib-static \ install build-install-binary install-binary install-lib-shared install-lib-static \ install-lib-headers install-lib-so-link install-conf install-data install-gmo install-man \ install-symlinks install-pkg-config uninstall-gmo uninstall-pkg-config \ uninstall uninstall-binary uninstall-data uninstall-lib uninstall-symlinks \ print-info check copy-dist update-po compile-gmo mkdir-bin permissions \ dgz dist tgz tgz-bindings tgz-core rpm win-dist win-dir xz zip rhash-1.4.6/README.md0000664000000000000000000000335014703622112012605 0ustar rootroot# RHash RHash (Recursive Hasher) is a console utility for calculation and verification of magnet links and various message digests, including CRC32, CRC32C, MD4, MD5, SHA1, SHA256, SHA512, SHA3, AICH, ED2K, DC++ TTH, BitTorrent BTIH, Tiger, GOST R 34.11-94, GOST R 34.11-2012, RIPEMD-160, HAS-160, EDON-R, and Whirlpool. Message digests are used to ensure and verify integrity of large volumes of data for a long-term storing or transferring. ### Program features: * Ability to process directories recursively. * Output in a predefined (SFV, BSD-like) or a user-defined format. * Calculation of Magnet links. * Updating hash files (adding message digests of files missing in the hash file). * Calculates several message digests in one pass. * Portability: the program works the same on Linux, Unix, macOS or Windows. ## Installation ```shell ./configure && make install ``` For more complicated cases of installation see the [INSTALL.md] file. ## Documentation * RHash [ChangeLog] * [The LibRHash Library] documentation ## Links * Project Home Page: http://rhash.sourceforge.net/ * Official Releases: https://github.com/rhash/RHash/releases/ * Binary Windows Releases: https://sf.net/projects/rhash/files/rhash/ * The table of the supported by RHash [hash functions](http://sf.net/p/rhash/wiki/HashFunctions/) * ECRYPT [The Hash Function Zoo](http://ehash.iaik.tugraz.at/wiki/The_Hash_Function_Zoo) * ECRYPT [Benchmarking of hash functions](https://bench.cr.yp.to/results-hash.html) ## Contribution Please read the [Contribution guidelines](docs/CONTRIBUTING.md) document. ## License The code is distributed under [BSD Zero Clause License](COPYING). [INSTALL.md]: INSTALL.md [The LibRHash Library]: docs/LIBRHASH.md [ChangeLog]: ChangeLog rhash-1.4.6/COPYING0000664000000000000000000000134014703622112012356 0ustar rootroot BSD Zero Clause License Copyright (c) 2005, Aleksey Kravchenko Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. rhash-1.4.6/parse_cmdline.c0000664000000000000000000012552215010476501014306 0ustar rootroot/* parse_cmdline.c - parsing of command line options */ #include "parse_cmdline.h" #include "calc_sums.h" #include "file_mask.h" #include "find_file.h" #include "hash_print.h" #include "output.h" #include "rhash_main.h" #include "win_utils.h" #include "librhash/rhash.h" #include #include #include #include #include #include #include #ifdef _WIN32 # include /* for CommandLineToArgvW(), GetCommandLineW(), ... */ #endif typedef struct options_t options_t; struct options_t conf_opt; /* config file parsed options */ struct options_t opt; /* command line options */ static const char* get_full_program_version(void) { static char version_buffer[64]; sprintf(version_buffer, "%s v%s\n", PROGRAM_NAME, get_version_string()); assert(strlen(version_buffer) < sizeof(version_buffer)); return version_buffer; } static void on_verbose(options_t* o) { if (o->verbose < 2) o->verbose++; } static void print_version(void) { rsh_fprintf(rhash_data.out, "%s", get_full_program_version()); rsh_exit(0); } static void print_help_line(const char* option, const char* format, ...) { va_list args; va_start(args, format); rsh_fprintf(rhash_data.out, "%s", option); rsh_vfprintf(rhash_data.out, format, args); va_end(args); } /** * Print program help. */ static void print_help(void) { const char* checksum_format; const char* digest_format; assert(rhash_data.out != NULL); /* print program version and usage */ rsh_fprintf(rhash_data.out, _("%s\n" "Usage: %s [OPTION...] [FILE | -]...\n" " %s --printf= [FILE | -]...\n\n"), get_full_program_version(), CMD_FILENAME, CMD_FILENAME); rsh_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")); /* TRANSLATORS: help screen line template for CRC32 and CRC32C */ checksum_format = _("Calculate %s checksum.\n"); /* TRANSLATORS: help screen line template for MD5, SHA1, e.t.c.\n" */ digest_format = _("Calculate %s message digest.\n"); print_help_line(" -C, --crc32 ", checksum_format, "CRC32"); print_help_line(" --crc32c ", checksum_format, "CRC32C"); print_help_line(" --md4 ", digest_format, "MD4"); print_help_line(" -M, --md5 ", digest_format, "MD5"); print_help_line(" -H, --sha1 ", digest_format, "SHA1"); print_help_line(" --sha224, --sha256, --sha384, --sha512 ", digest_format, "SHA2"); print_help_line(" --sha3-224, --sha3-256, --sha3-384, --sha3-512 ", digest_format, "SHA3"); print_help_line(" --blake2s, --blake2b ", digest_format, "BLAKE2S/BLAKE2B"); print_help_line(" --blake3 ", digest_format, "BLAKE3"); print_help_line(" -T, --tth ", digest_format, "TTH"); print_help_line(" --btih ", digest_format, "BitTorrent InfoHash"); print_help_line(" -A, --aich ", digest_format, "AICH"); print_help_line(" -E, --ed2k ", digest_format, "eDonkey"); print_help_line(" -L, --ed2k-link ", _("Calculate and print eDonkey link.\n")); print_help_line(" --tiger ", digest_format, "Tiger"); print_help_line(" -G, --gost12-256 ", digest_format, _("GOST R 34.11-2012, 256 bit")); print_help_line(" --gost12-512 ", digest_format, _("GOST R 34.11-2012, 512 bit")); /* TRANSLATORS: This hash function name should be translated to Russian only */ print_help_line(" --gost94 ", digest_format, _("GOST R 34.11-94")); /* TRANSLATORS: This hash function name should be translated to Russian only */ print_help_line(" --gost94-cryptopro ", digest_format, _("GOST R 34.11-94 CryptoPro")); print_help_line(" --ripemd160 ", digest_format, "RIPEMD-160"); print_help_line(" --has160 ", digest_format, "HAS-160"); print_help_line(" --edonr256, --edonr512 ", digest_format, "EDON-R 256/512"); print_help_line(" --snefru128, --snefru256 ", digest_format, "SNEFRU-128/256"); print_help_line(" -a, --all ", _("Calculate all supported hash functions.\n")); print_help_line(" -c, --check ", _("Check hash files specified by command line.\n")); print_help_line(" -u, --update= ", _("Update the specified hash file.\n")); print_help_line(" --missing= ", _("List files from hash file that are missing or inaccessible.\n")); print_help_line(" --unverified= ", _("List command-line files missing from given hash file.\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 hash functions, one per line.\n")); print_help_line(" -B, --benchmark ", _("Benchmark selected algorithm.\n")); print_help_line(" -v, --verbose ", _("Be verbose.\n")); print_help_line(" --brief ", _("Use brief form of hash file verification report.\n")); print_help_line(" -r, --recursive ", _("Process directories recursively.\n")); print_help_line(" --file-list= ", _("Process a list of files.\n")); print_help_line(" -m, --message= ", _("Process the text message.\n")); print_help_line(" --skip-ok ", _("Don't print OK messages for successfully verified files.\n")); print_help_line(" --ignore-missing ", _("Ignore missing files, while verifying a hash file.\n")); print_help_line(" -i, --ignore-case ", _("Ignore case of filenames when updating hash files.\n")); print_help_line(" -P, --percents ", _("Show percents, while calculating or verifying message digests.\n")); print_help_line(" --speed ", _("Output per-file and total processing speed.\n")); print_help_line(" --max-depth= ", _("Descend at most levels of directories.\n")); if (rhash_is_openssl_supported()) print_help_line(" --openssl= ", _("Specify hash functions to be calculated using OpenSSL.\n")); print_help_line(" -o, --output= ", _("File to output calculation or checking results.\n")); print_help_line(" -l, --log= ", _("File to log errors and verbose information.\n")); print_help_line(" --sfv ", _("Print message digests, using SFV format (default).\n")); print_help_line(" --bsd ", _("Print message digests, using BSD-like format.\n")); print_help_line(" --simple ", _("Print message digests, using simple format.\n")); print_help_line(" --one-hash ", _("Print one message digest per line without file information.\n")); print_help_line(" --hex ", _("Print message digests in hexadecimal format.\n")); print_help_line(" --base32 ", _("Print message digests in Base32 format.\n")); print_help_line(" -b, --base64 ", _("Print message digests in Base64 format.\n")); print_help_line(" -g, --magnet ", _("Print message digests as magnet links.\n")); print_help_line(" --torrent ", _("Create torrent files.\n")); #ifdef _WIN32 print_help_line(" --utf8 ", _("Use UTF-8 encoding for output (Windows only).\n")); print_help_line(" --win ", _("Use Windows codepage for output (Windows only).\n")); print_help_line(" --dos ", _("Use DOS codepage for output (Windows only).\n")); #endif print_help_line(" --template= ", _("Load a printf-like template from the \n")); print_help_line(" -p, --printf= ", _("Format and print message digests.\n")); print_help_line(" ", _("See the RHash manual for details.\n")); rsh_exit(0); } /** * Print the names of all supported hash algorithms to the console. */ static void list_hashes(void) { uint64_t hash_mask = get_all_supported_hash_mask(); while (hash_mask) { uint64_t bit64 = hash_mask & -hash_mask; const char* hash_name = rhash_get_name(bit64_to_hash_id(bit64)); if (hash_name) rsh_fprintf(rhash_data.out, "%s\n", hash_name); hash_mask ^= bit64; } rsh_exit(0); } /** * Add a hash function to the list of calulated ones. * If RHASH_ALL_HASHES is passed as hash_id, then all * hash functions will be calculated. * * @param o pointer to the options structure to update * @param hash_id hash function identifier */ static void add_hash_id(options_t* o, unsigned hash_id) { if (hash_id == RHASH_ALL_HASHES) o->hash_mask = get_all_supported_hash_mask(); else o->hash_mask |= hash_id_to_bit64(hash_id); } /** * Process a mode option, requiring a hash file. * * @param o pointer to the options structure to update * @param path the path of the hash file * @param mode the mode bit-flag */ static void hash_file_mode(options_t* o, tstr_t path, unsigned mode) { if (o->search_data) { o->update_file = path; o->mode |= mode; } } /** * Add a special file. * * @param o pointer to the options structure to update * @param path the path of the file * @param type the type of the option */ static void add_special_file(options_t* o, tstr_t path, unsigned file_mode) { if (o->search_data) { file_search_add_file(o->search_data, path, file_mode); opt.has_files = 1; } } enum file_suffix_type { MASK_ACCEPT, MASK_EXCLUDE, MASK_CRC_ACCEPT }; /** * Process --accept, --exclude and --crc-accept options. * * @param o pointer to the options structure to update * @param accept_string comma delimited string to parse * @param type the type of the option */ static void add_file_suffix(options_t* o, char* accept_string, unsigned type) { file_mask_array** ptr = (type == MASK_ACCEPT ? &o->files_accept : type == MASK_EXCLUDE ? &o->files_exclude : &o->crc_accept); if (!*ptr) *ptr = file_mask_new(); file_mask_add_list(*ptr, accept_string); } /** * Process --bt_announce option. * * @param o pointer to the options structure * @param announce_url the url to parse * @param unused a tottaly unused parameter */ static void bt_announce(options_t* o, char* announce_url, unsigned unused) { (void)unused; /* skip empty string */ if (!announce_url || !announce_url[0]) return; if (!o->bt_announce) o->bt_announce = rsh_vector_new_simple(); rsh_vector_add_ptr(o->bt_announce, rsh_strdup(announce_url)); } /** * Process an --openssl option. * * @param o pointer to the options structure to update * @param openssl_hashes comma delimited string with names of hash functions * @param type ignored */ static void openssl_flags(options_t* o, char* openssl_hashes, unsigned type) { uint64_t openssl_supported_hash_mask = get_openssl_supported_hash_mask(); char* cur; char* next; (void)type; if (!rhash_is_openssl_supported()) { log_warning(_("compiled without openssl support\n")); return; } /* set the openssl_mask */ for (cur = openssl_hashes; cur && *cur; cur = next) { print_hash_info* info; size_t length; next = strchr(cur, ','); length = (next != NULL ? (size_t)(next++ - cur) : strlen(cur)); if (length == 0) continue; for (info = hash_info_table; info->hash_id; info++) { uint64_t hash_bit64 = hash_id_to_bit64(info->hash_id); if ((hash_bit64 & openssl_supported_hash_mask) == 0) continue; if (memcmp(cur, info->short_name, length) == 0 && info->short_name[length] == '\0') { o->openssl_mask |= hash_bit64; break; } } if (!info->hash_id) { cur[length] = '\0'; /* terminate wrong hash function name */ log_warning(_("openssl option doesn't support '%s' hash function\n"), cur); } } /* mark hash mask as valid to handle disabling openssl by --openssl="" */ o->openssl_mask |= OPENSSL_MASK_VALID_BIT; } /** * Process --video option. * * @param o pointer to the options structure to update */ static void accept_video(options_t* o) { add_file_suffix(o, ".avi,.ogm,.mkv,.mp4,.mpeg,.mpg,.asf,.rm,.wmv,.vob", MASK_ACCEPT); } /** * Say nya! Keep secret! =) */ static void nya(void) { rsh_fprintf(rhash_data.out, " /\\__/\\\n (^ _ ^.) %s\n (_uu__)\n", /* TRANSLATORS: Keep it secret ;) */ _("Purrr...")); rsh_exit(0); } /** * Process on --max-depth 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)) { die(_("max-depth parameter is not a number: %s\n"), number); } o->find_max_depth = atoi(number); } /** * 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)) { die(_("bt-piece-length parameter is not a number: %s\n"), number); } o->bt_piece_length = (size_t)atoi(number); } /** * 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 { die(_("path-separator is neither '/' nor '\\': %s\n"), sep); } } /** * Function pointer to store an option handler. */ typedef void(*opt_handler_t)(void); /** * Information about a command line option. */ typedef struct cmdline_opt_t { unsigned short type; /* how to process the option, see option_type_t below */ char short1, short2; /* short option names */ char* long_name; /* long option name */ opt_handler_t handler; /* option handler */ void* ptr; /* auxiliary pointer, e.g. to an opt field */ unsigned param; /* optional integer parameter */ } cmdline_opt_t; enum option_type_t { F_NEED_PARAM = 16, /* flag: option needs a parameter */ F_OUTPUT_OPT = 32, /* flag: option changes program output */ F_UFLG = 1, /* set a bit flag in a uint32_t field */ F_UENC = F_UFLG | F_OUTPUT_OPT, /* an encoding changing option */ F_CSTR = 2 | F_NEED_PARAM, /* store parameter as a C string */ F_TSTR = 3 | F_NEED_PARAM, /* store parameter as a tstr_t */ F_TOUT = 4 | F_NEED_PARAM | F_OUTPUT_OPT, F_VFNC = 5, /* just call a function */ F_PFNC = 6 | F_NEED_PARAM, /* process option parameter by calling a handler */ F_TFNC = 7 | F_NEED_PARAM, /* process option parameter by calling a handler */ F_UFNC = 8 | F_NEED_PARAM, /* pass UTF-8 encoded parameter to the handler */ F_PRNT = 9, /* print a constant C-string and exit */ }; #define is_param_required(option_type) ((option_type) & F_NEED_PARAM) #define is_output_modifier(option_type) ((option_type) & F_OUTPUT_OPT) /* supported program options */ cmdline_opt_t cmdline_opt[] = { /* program modes */ { F_UFLG, 'c', 0, "check", 0, &opt.mode, MODE_CHECK }, { F_UFLG, 'k', 0, "check-embedded", 0, &opt.mode, MODE_CHECK_EMBEDDED }, { F_TFNC, 'u', 0, "update", (opt_handler_t)hash_file_mode, 0, MODE_UPDATE }, { F_TFNC, 0, 0, "missing", (opt_handler_t)hash_file_mode, 0, MODE_MISSING }, { F_TFNC, 0, 0, "unverified", (opt_handler_t)hash_file_mode, 0, MODE_UNVERIFIED }, { F_UFLG, 'B', 0, "benchmark", 0, &opt.mode, MODE_BENCHMARK }, { F_UFLG, 0, 0, "torrent", 0, &opt.mode, MODE_TORRENT }, { F_VFNC, 0, 0, "list-hashes", (opt_handler_t)list_hashes, 0, 0 }, { F_VFNC, 'h', 0, "help", (opt_handler_t)print_help, 0, 0 }, { F_VFNC, 'V', 0, "version", (opt_handler_t)print_version, 0, 0 }, { F_VFNC, 'v', 0, "verbose", (opt_handler_t)on_verbose, 0, 0 }, /* hash functions options */ { F_VFNC, 'a', 0, "all", (opt_handler_t)add_hash_id, 0, RHASH_ALL_HASHES }, { F_VFNC, 'C', 0, "crc32", (opt_handler_t)add_hash_id, 0, RHASH_CRC32 }, { F_VFNC, 0, 0, "crc32c", (opt_handler_t)add_hash_id, 0, RHASH_CRC32C }, { F_VFNC, 0, 0, "md4", (opt_handler_t)add_hash_id, 0, RHASH_MD4 }, { F_VFNC, 'M', 0, "md5", (opt_handler_t)add_hash_id, 0, RHASH_MD5 }, { F_VFNC, 'H', 0, "sha1", (opt_handler_t)add_hash_id, 0, RHASH_SHA1 }, { F_VFNC, 0, 0, "sha224", (opt_handler_t)add_hash_id, 0, RHASH_SHA224 }, { F_VFNC, 0, 0, "sha256", (opt_handler_t)add_hash_id, 0, RHASH_SHA256 }, { F_VFNC, 0, 0, "sha384", (opt_handler_t)add_hash_id, 0, RHASH_SHA384 }, { F_VFNC, 0, 0, "sha512", (opt_handler_t)add_hash_id, 0, RHASH_SHA512 }, { F_VFNC, 0, 0, "sha3-224", (opt_handler_t)add_hash_id, 0, RHASH_SHA3_224 }, { F_VFNC, 0, 0, "sha3-256", (opt_handler_t)add_hash_id, 0, RHASH_SHA3_256 }, { F_VFNC, 0, 0, "sha3-384", (opt_handler_t)add_hash_id, 0, RHASH_SHA3_384 }, { F_VFNC, 0, 0, "sha3-512", (opt_handler_t)add_hash_id, 0, RHASH_SHA3_512 }, { F_VFNC, 0, 0, "tiger", (opt_handler_t)add_hash_id, 0, RHASH_TIGER }, { F_VFNC, 'T', 0, "tth", (opt_handler_t)add_hash_id, 0, RHASH_TTH }, { F_VFNC, 0, 0, "btih", (opt_handler_t)add_hash_id, 0, RHASH_BTIH }, { F_VFNC, 'E', 0, "ed2k", (opt_handler_t)add_hash_id, 0, RHASH_ED2K }, { F_VFNC, 'A', 0, "aich", (opt_handler_t)add_hash_id, 0, RHASH_AICH }, { F_VFNC, 'G', 0, "gost12-256", (opt_handler_t)add_hash_id, 0, RHASH_GOST12_256 }, { F_VFNC, 0, 0, "gost12-512", (opt_handler_t)add_hash_id, 0, RHASH_GOST12_512 }, { F_VFNC, 0, 0, "gost94", (opt_handler_t)add_hash_id, 0, RHASH_GOST94 }, { F_VFNC, 0, 0, "gost94-cryptopro", (opt_handler_t)add_hash_id, 0, RHASH_GOST94_CRYPTOPRO }, /* legacy: the following two gost options are left for compatibility */ { F_VFNC, 0, 0, "gost", (opt_handler_t)add_hash_id, 0, RHASH_GOST94 }, { F_VFNC, 0, 0, "gost-cryptopro", (opt_handler_t)add_hash_id, 0, RHASH_GOST94_CRYPTOPRO }, { F_VFNC, 'W', 0, "whirlpool", (opt_handler_t)add_hash_id, 0, RHASH_WHIRLPOOL }, { F_VFNC, 0, 0, "ripemd160", (opt_handler_t)add_hash_id, 0, RHASH_RIPEMD160 }, { F_VFNC, 0, 0, "has160", (opt_handler_t)add_hash_id, 0, RHASH_HAS160 }, { F_VFNC, 0, 0, "snefru128", (opt_handler_t)add_hash_id, 0, RHASH_SNEFRU128 }, { F_VFNC, 0, 0, "snefru256", (opt_handler_t)add_hash_id, 0, RHASH_SNEFRU256 }, { F_VFNC, 0, 0, "edonr256", (opt_handler_t)add_hash_id, 0, RHASH_EDONR256 }, { F_VFNC, 0, 0, "edonr512", (opt_handler_t)add_hash_id, 0, RHASH_EDONR512 }, { F_VFNC, 0, 0, "blake2s", (opt_handler_t)add_hash_id, 0, RHASH_BLAKE2S }, { F_VFNC, 0, 0, "blake2b", (opt_handler_t)add_hash_id, 0, RHASH_BLAKE2B }, { F_VFNC, 0, 0, "blake3", (opt_handler_t)add_hash_id, 0, RHASH_BLAKE3 }, /* output formats */ { F_UFLG, 0, 0, "sfv", 0, &opt.fmt, FMT_SFV }, { F_UFLG, 0, 0, "bsd", 0, &opt.fmt, FMT_BSD }, { F_UFLG, 0, 0, "simple", 0, &opt.fmt, FMT_SIMPLE }, { F_UFLG, 0, 0, "one-hash", 0, &opt.fmt, FMT_ONE_HASH }, { F_UFLG, 'L', 0, "ed2k-link", 0, &opt.fmt, FMT_ED2K_LINK }, { F_UFLG, 'g', 0, "magnet", 0, &opt.fmt, FMT_MAGNET }, { F_UFLG, 0, 0, "uppercase", 0, &opt.flags, OPT_UPPERCASE }, { F_UFLG, 0, 0, "lowercase", 0, &opt.flags, OPT_LOWERCASE }, { F_TSTR, 0, 0, "template", 0, &opt.template_file, 0 }, { F_CSTR, 'p', 0, "printf", 0, &opt.printf_str, 0 }, /* other options */ { F_UFLG, 'r', 'R', "recursive", 0, &opt.flags, OPT_RECURSIVE }, { F_TFNC, 'm', 0, "message", (opt_handler_t)add_special_file, 0, FileIsData }, { F_TFNC, 0, 0, "file-list", (opt_handler_t)add_special_file, 0, FileIsList }, { F_UFLG, 0, 0, "follow", 0, &opt.flags, OPT_FOLLOW }, { F_UFLG, 0, 0, "brief", 0, &opt.flags, OPT_BRIEF }, { F_UFLG, 0, 0, "gost-reverse", 0, &opt.flags, OPT_GOST_REVERSE }, { F_UFLG, 0, 0, "skip-ok", 0, &opt.flags, OPT_SKIP_OK }, { F_UFLG, 0, 0, "ignore-missing", 0, &opt.flags, OPT_IGNORE_MISSING }, { F_UFLG, 'i', 0, "ignore-case", 0, &opt.flags, OPT_IGNORE_CASE }, { F_UENC, 'P', 0, "percents", 0, &opt.flags, OPT_PERCENTS }, { F_UFLG, 0, 0, "speed", 0, &opt.flags, OPT_SPEED }, { F_UFLG, 'e', 0, "embed-crc", 0, &opt.flags, OPT_EMBED_CRC }, { F_CSTR, 0, 0, "embed-crc-delimiter", 0, &opt.embed_crc_delimiter, 0 }, { F_PFNC, 0, 0, "path-separator", (opt_handler_t)set_path_separator, 0, 0 }, { F_TOUT, 'o', 0, "output", 0, &opt.output, 0 }, { F_TOUT, 'l', 0, "log", 0, &opt.log, 0 }, { F_PFNC, 'q', 0, "accept", (opt_handler_t)add_file_suffix, 0, MASK_ACCEPT }, { F_PFNC, 't', 0, "crc-accept", (opt_handler_t)add_file_suffix, 0, MASK_CRC_ACCEPT }, { F_PFNC, 0, 0, "exclude", (opt_handler_t)add_file_suffix, 0, MASK_EXCLUDE }, { F_VFNC, 0, 0, "video", (opt_handler_t)accept_video, 0, 0 }, { F_VFNC, 0, 0, "nya", (opt_handler_t)nya, 0, 0 }, { F_PFNC, 0, 0, "max-depth", (opt_handler_t)set_max_depth, 0, 0 }, { F_UFLG, 0, 0, "bt-private", 0, &opt.flags, OPT_BT_PRIVATE }, { F_UFLG, 0, 0, "bt-transmission", 0, &opt.flags, OPT_BT_TRANSMISSION }, { F_PFNC, 0, 0, "bt-piece-length", (opt_handler_t)set_bt_piece_length, 0, 0 }, { F_UFNC, 0, 0, "bt-announce", (opt_handler_t)bt_announce, 0, 0 }, { F_TSTR, 0, 0, "bt-batch", 0, &opt.bt_batch_file, 0 }, { F_UFLG, 0, 0, "benchmark-raw", 0, &opt.flags, OPT_BENCH_RAW }, { F_UFLG, 0, 0, "no-detect-by-ext", 0, &opt.flags, OPT_NO_DETECT_BY_EXT }, { F_UFLG, 0, 0, "no-path-escaping", 0, &opt.flags, OPT_NO_PATH_ESCAPING }, { F_UFLG, 0, 0, "hex", 0, &opt.flags, OPT_HEX }, { F_UFLG, 0, 0, "base32", 0, &opt.flags, OPT_BASE32 }, { F_UFLG, 'b', 0, "base64", 0, &opt.flags, OPT_BASE64 }, { F_PFNC, 0, 0, "openssl", (opt_handler_t)openssl_flags, 0, 0 }, /* for compatibility */ { F_PFNC, 0, 0, "maxdepth", (opt_handler_t)set_max_depth, 0, 0 }, #ifdef _WIN32 /* code pages (windows only) */ { F_UENC, 0, 0, "utf8", 0, &opt.flags, OPT_UTF8 }, { F_UENC, 0, 0, "win", 0, &opt.flags, OPT_ENC_WIN }, { F_UENC, 0, 0, "dos", 0, &opt.flags, OPT_ENC_DOS }, /* legacy: the following two options are left for compatibility */ { F_UENC, 0, 0, "ansi", 0, &opt.flags, OPT_ENC_WIN }, { F_UENC, 0, 0, "oem", 0, &opt.flags, OPT_ENC_DOS }, #endif { 0,0,0,0,0,0,0 } }; cmdline_opt_t cmdline_file = { F_TFNC, 0, 0, "FILE", (opt_handler_t)add_special_file, 0, 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) { die(_("unknown option: %s\n"), (option_name ? option_name : "?")); } /* structure to store command line option information */ typedef struct parsed_option_t { cmdline_opt_t* o; const char* name; /* the parsed option name */ char buf[4]; void* parameter; /* option argument, if required */ } parsed_option_t; /** * 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) { die(_("argument is required for option %s\n"), option->name); } #ifdef _WIN32 if (option_type == F_TOUT || option_type == F_TFNC || option_type == F_TSTR) { /* leave the value in UTF-16 */ value = (char*)rsh_wcsdup((wchar_t*)option->parameter); } else if (option_type == F_UFNC) { /* convert from UTF-16 to UTF-8 */ value = convert_wcs_to_str((wchar_t*)option->parameter, ConvertToUtf8 | ConvertExact); } else { /* convert from UTF-16 */ value = convert_wcs_to_str((wchar_t*)option->parameter, ConvertToPrimaryEncoding); } rsh_vector_add_ptr(opt.mem, value); #else value = (char*)option->parameter; #endif } /* 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_TSTR: case F_TOUT: /* save the option parameter */ *(char**)((char*)opts + ((char*)o->ptr - (char*)&opt)) = value; break; case F_PFNC: case F_TFNC: case F_UFNC: /* call option parameter handler */ ( (void(*)(options_t*, char*, unsigned))o->handler )(opts, value, o->param); break; case F_VFNC: ( (void(*)(options_t*, unsigned))o->handler )(opts, o->param); /* call option handler */ break; case F_PRNT: log_msg("%s", (char*)o->ptr); rsh_exit(0); break; default: assert(0); /* impossible option type */ } } #ifdef _WIN32 # define rsh_tgetenv(name) _wgetenv(name) #else # define rsh_tgetenv(name) getenv(name) #endif #define COUNTOF(array) (sizeof(array) / sizeof(*array)) enum ConfigLookupFlags { ConfFlagNeedSplit = 8, ConfFlagNoVars = 16 }; /** * Check if a config file, specified by path subparts, is a regular file. * On success the resulting path is stored as rhash_data.config_file. * * @param path_parts subparts of the path * @param flags check flags * @return 1 if the file is regular, 0 otherwise */ static int try_config(ctpath_t path_parts[], unsigned flags) { const size_t parts_count = flags & 3; tpath_t allocated = NULL; ctpath_t path = NULL; size_t i; for (i = 0; i < parts_count; i++) { ctpath_t sub_path = path_parts[i]; if (sub_path[0] == RSH_T('$') && !(flags & ConfFlagNoVars)) { sub_path = rsh_tgetenv(sub_path + 1); if (!sub_path || !sub_path[0]) { free(allocated); return 0; } #ifndef _WIN32 /* check if the variable should be splitted */ if (flags == (2 | ConfFlagNeedSplit) && i == 0) { tpath_t next; ctpath_t parts[2]; parts[1] = path_parts[1]; sub_path = allocated = rsh_strdup(sub_path); do { next = strchr(sub_path, ':'); if (next) *(next++) = '\0'; if (sub_path[0]) { parts[0] = sub_path; if (try_config(parts, COUNTOF(parts) | ConfFlagNoVars)) { free(allocated); return 1; } } sub_path = next; } while (sub_path); free(allocated); return 0; } #endif } if (path) { tpath_t old_allocated = allocated; path = allocated = make_tpath(path, sub_path); free(old_allocated); } else { path = sub_path; } } assert(!rhash_data.config_file.real_path); { unsigned init_flags = FileInitRunFstat | (!allocated ? FileInitReusePath : 0); int res = file_init(&rhash_data.config_file, path, init_flags); free(allocated); if (res == 0 && FILE_ISREG(&rhash_data.config_file)) return 1; file_cleanup(&rhash_data.config_file); return 0; } } /** * Search for config file. * * @return 1 if config file is found, 0 otherwise */ static int find_conf_file(void) { #ifndef SYSCONFDIR # define SYSCONFDIR "/etc" #endif #ifndef _WIN32 /* Linux/Unix part */ static ctpath_t xdg_conf_home[2] = { "$XDG_CONFIG_HOME", "rhash/rhashrc" }; static ctpath_t xdg_conf_default[2] = { "$HOME", ".config/rhash/rhashrc" }; static ctpath_t xdg_conf_dirs[2] = { "$XDG_CONFIG_DIRS", "rhash/rhashrc" }; static ctpath_t home_conf[2] = { "$HOME", ".rhashrc" }; static ctpath_t sysconf_dir[1] = { SYSCONFDIR "/rhashrc" }; return (try_config(xdg_conf_home, COUNTOF(xdg_conf_home)) || try_config(xdg_conf_default, COUNTOF(xdg_conf_default)) || try_config(xdg_conf_dirs, COUNTOF(xdg_conf_dirs) | ConfFlagNeedSplit) || try_config(home_conf, COUNTOF(home_conf)) || try_config(sysconf_dir, COUNTOF(sysconf_dir))); #else /* _WIN32 */ static ctpath_t app_data[2] = { L"$APPDATA", L"RHash\\rhashrc" }; static ctpath_t home_conf[3] = { L"$HOMEDRIVE", L"$HOMEPATH", L"rhashrc" }; if (try_config(app_data, COUNTOF(app_data)) || try_config(home_conf, COUNTOF(home_conf))) { return 1; } else { tpath_t prog_dir[2]; prog_dir[0] = get_program_dir(); prog_dir[1] = L"rhashrc"; return try_config((ctpath_t*)prog_dir, COUNTOF(prog_dir)); } #endif /* _WIN32 */ } /** * 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; unsigned line_number = 0; int res; /* initialize conf_opt */ memset(&conf_opt, 0, sizeof(opt)); conf_opt.find_max_depth = -1; if (!find_conf_file()) return 0; assert(!!rhash_data.config_file.real_path); assert(FILE_ISREG(&rhash_data.config_file)); fd = file_fopen(&rhash_data.config_file, FOpenRead); 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; char* value; line_number++; if (*line == 0 || IS_COMMENT(*line)) continue; /* search for '=' */ index = strcspn(line, "="); if (line[index] == 0) { log_warning(_("%s:%u: can't parse line \"%s\"\n"), file_get_print_path(&rhash_data.config_file, FPathUtf8 | FPathNotNull), line_number, 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:%u: unknown option \"%s\"\n"), file_get_print_path(&rhash_data.config_file, FPathUtf8 | FPathNotNull), line_number, 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[] = { "on", "yes", "true", 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); } /** * Find long option info, by it's name and retrieve its parameter if required. * Error is reported for unknown options. * * @param option structure to receive the parsed option info * @param parg pointer to a command line argument */ static void parse_long_option(parsed_option_t* option, rsh_tchar*** parg) { size_t length; rsh_tchar* eq_sign; cmdline_opt_t* t; char* name; #ifdef _WIN32 rsh_tchar* wname = **parg; /* "--