1 /* ---------------------------------------------------------------------------------------------- */
2 /* MHash-384 - Simple fast portable secure hashing library */
3 /* Copyright(c) 2016-2020 LoRd_MuldeR <mulder2@gmx.de> */
5 /* Permission is hereby granted, free of charge, to any person obtaining a copy of this software */
6 /* and associated documentation files (the "Software"), to deal in the Software without */
7 /* restriction, including without limitation the rights to use, copy, modify, merge, publish, */
8 /* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the */
9 /* Software is furnished to do so, subject to the following conditions: */
11 /* The above copyright notice and this permission notice shall be included in all copies or */
12 /* substantial portions of the Software. */
14 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING */
15 /* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
16 /* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, */
17 /* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
18 /* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
19 /* ---------------------------------------------------------------------------------------------- */
23 #include "self_test.h"
36 #define fileno _fileno
39 #define _O_U8TEXT 0x40000
44 #define SYSTEM_TYPE STR(SYSTEM_NAME) STR("-") STR(SYSTEM_ARCH)
47 static const size_t BUFFER_SIZE = 8192U;
49 /* Mode of operation */
64 static void print_logo(void)
66 uint16_t ver_major, ver_minor, ver_patch;
67 mhash384_version(&ver_major, &ver_minor, &ver_patch);
68 FPRINTF(stderr, ver_patch ? STR("MHash-384 v%u.%02u-%u") : STR("MHash-384 v%u.%02u"), ver_major, ver_minor, ver_patch);
69 FPUTS(STR(", ") SYSTEM_TYPE STR(" [") STR(__DATE__) STR("]\n"), stderr);
70 FPUTS(STR("Created by LoRd_MuldeR <mulder2@gmx.de>, released under the MIT license.\n\n"), stderr);
77 static void print_version(void)
79 uint16_t ver_major, ver_minor, ver_patch;
80 mhash384_version(&ver_major, &ver_minor, &ver_patch);
81 FPRINTF(stdout, STR("MHash-384 v%u.%02u-%u, ") SYSTEM_TYPE STR("\n"), ver_major, ver_minor, ver_patch);
82 FPRINTF(stdout, STR("Built on ") STR(__DATE__) STR(" at ") STR(__TIME__) STR(", using ") STR(COMPILER_FMT) STR("\n"), COMPILER_ARG);
88 static void print_manpage(const CHAR_T *const argv0)
90 const CHAR_T *const program_name = get_basename(argv0);
92 FPUTS(STR("Usage:\n"), stderr);
93 FPRINTF(stderr, STR(" %") PRI_CHAR STR(" [OPTIONS] [<FILE_1> <FILE_2> ... <FILE_N>]\n\n"), program_name);
94 FPUTS(STR("Options:\n"), stderr);
95 FPUTS(STR(" --keep-going Try to proceed with the remaining files, if a file has failed\n"), stderr);
96 FPUTS(STR(" --short Print the digest in short format (no file names)\n"), stderr);
97 FPUTS(STR(" --lower-case Print the digest in lower-case letters (default: upper-case)\n"), stderr);
98 FPUTS(STR(" --base64 Print the digest in Base64 format (default: Hex format)\n"), stderr);
99 FPUTS(STR(" --help Print help screen and exit\n"), stderr);
100 FPUTS(STR(" --version Print program version and exit\n"), stderr);
101 FPUTS(STR(" --self-test Run self-test and exit\n"), stderr);
102 FPUTS(STR(" --stress Enable stress test mode; strings are read from the input file\n"), stderr);
103 FPUTS(STR(" --benchmark Measure the overall time required for the operation\n\n"), stderr);
104 FPUTS(STR("If *no* input file is specified, data is read from the standard input (stdin)\n"), stderr);
108 * Parse command-line options
110 static opmode_t parse_options(int &arg_offset, options_t &options, const int argc, const CHAR_T *const *const argv)
112 opmode_t mode = MODE_DEFAULT;
113 memset(&options, 0, sizeof(options_t));
114 bool stop_here = false;
116 while((!stop_here) && (arg_offset < argc) && (!STRNICMP(argv[arg_offset], STR("--"), 2U)))
118 const CHAR_T *const argstr = argv[arg_offset] + 2U;
123 else if(!STRICMP(argstr, STR("keep-going")))
125 options.keep_going = true;
127 else if(!STRICMP(argstr, STR("short")))
129 options.short_format = true;
131 else if (!STRICMP(argstr, STR("base64")))
133 options.base64 = true;
135 else if(!STRICMP(argstr, STR("lower-case")))
137 options.lower_case = true;
139 else if(!STRICMP(argstr, STR("help")))
143 else if(!STRICMP(argstr, STR("version")))
147 else if(!STRICMP(argstr, STR("self-test")))
149 mode = MODE_SELFTEST;
151 else if(!STRICMP(argstr, STR("stress")))
155 else if(!STRICMP(argstr, STR("benchmark")))
157 options.benchmark = true;
162 FPRINTF(stderr, STR("Error: Specified option \"%") PRI_CHAR STR("\" is unknown. Please type \"--help\" for details!\n"), argv[arg_offset]);
169 if (options.base64 && options.lower_case)
172 FPUTS(STR("Error: Options \"--base64\" and \"--lower-case\" are mutually exclusive!\n"), stderr);
183 static bool process_file(const CHAR_T *const file_name, const options_t options)
185 /* File description */
186 const CHAR_T *const file_description = file_name ? file_name : STR("<STDIN>");
188 /* Open the input file */
190 FILE *const input = file_name ? FOPEN(file_name, STR("rb")) : stdin;
193 FPRINTF(stderr, STR("Error: File \"%") PRI_CHAR STR("\" could not be opened for reading! [errno: %d]\n"), file_description, errno);
198 /* Check if file is directory (this is required for Linux!)*/
199 struct stat file_info;
200 if(!fstat(fileno(input), &file_info))
202 if((file_info.st_mode & S_IFMT) == S_IFDIR)
204 FPRINTF(stderr, STR("Error: File \"%") PRI_CHAR STR("\" is a directory!\n"), file_description);
211 /* Initialize hash state */
213 uint8_t buffer[BUFFER_SIZE];
216 /* Process complete input */
219 const size_t length = fread(buffer, sizeof(uint8_t), BUFFER_SIZE, input);
222 break; /*EOF or error*/
224 mhash384.update(buffer, length);
227 /* Print the final digest */
230 const uint8_t *const digest = mhash384.finish();
231 const std::string string = options.base64 ? bytes_to_base64(digest, MHASH384_SIZE) : bytes_to_hex(digest, MHASH384_SIZE, options.lower_case);
232 const CHAR_T *const source_name = file_name ? file_name : STR("-");
233 const CHAR_T *const format = options.short_format ? STR("%") PRI_char STR("\n") : STR("%") PRI_char STR(" %") PRI_CHAR STR("\n");
234 FPRINTF(stdout, format, string.c_str(), source_name);
239 FPRINTF(stderr, STR("Error: File \"%") PRI_CHAR STR("\" encountered an I/O error!\n"), file_description);
244 /* Close the input file */
256 static int mhash384_main(int argc, CHAR_T* argv[])
259 bool success = false;
262 _setmode(_fileno(stdout), _O_U8TEXT);
263 _setmode(_fileno(stderr), _O_U8TEXT);
264 _setmode(_fileno(stdin ), _O_BINARY);
267 /* Parse all command-line options */
269 const opmode_t mode = parse_options(arg_offset, options, argc, argv);
270 if(mode == MODE_UNKNOWN)
275 /* Remember startup time */
276 const clock_t time_start = options.benchmark ? clock() : 0U;
278 /* Select mode of operation */
282 /* Print help screen and exit* */
283 print_manpage(argv[0U]);
288 /* Print program version and exit */
294 /* Run the self-test */
296 success = self_test(options);
300 /* Enable stress-test mode*/
302 success = stress_test((arg_offset < argc) ? argv[arg_offset] : NULL, options);
306 /* Process all input files */
307 if(arg_offset < argc)
309 while(arg_offset < argc)
311 if(process_file(argv[arg_offset++], options))
315 else if(!options.keep_going)
324 success = process_file(NULL, options); /*stdin*/
328 /* Print total time */
329 if(options.benchmark && ((mode == MODE_DEFAULT) || ((mode >= MODE_SELFTEST) && (mode <= MODE_STRESS))))
331 const clock_t total_time = clock() - time_start;
332 FPRINTF(stderr, STR("Operation took %.1f second(s).\n"), total_time / ((double)CLOCKS_PER_SEC));
336 return success ? EXIT_SUCCESS : EXIT_FAILURE;
340 * Global exception handler
342 static int _main_(int argc, CHAR_T* argv[])
347 return mhash384_main(argc, argv);
349 catch (std::exception& ex)
351 FPRINTF(stderr, STR("\nEXCEPTION: %") PRI_char STR("\n"), ex.what());
354 catch (const char *const ex)
356 FPRINTF(stderr, STR("\nEXCEPTION: %") PRI_char STR("\n"), ex);
361 FPUTS(STR("\nEXCEPTION: An unknown exception has been caught!\n"), stderr);
365 return mhash384_main(argc, argv);
370 * Entry point function
372 int EXTRY_POINT(int argc, CHAR_T* argv[])
374 #if defined(_WIN32) && !defined(__GNUC__) && defined(NDEBUG)
377 return _main_(argc, argv);
381 FPUTS(STR("\nEXCEPTION: Unhandeled structured exception caught!\n"), stderr);
385 return _main_(argc, argv);
390 * For legacy MinGW (not required for Mingw-w64, use '-municode' instead)
392 #if defined(_WIN32) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
394 void __wgetmainargs(int*, wchar_t***, wchar_t***, int, int*);
397 wchar_t **enpv, **argv;
399 __wgetmainargs(&argc, &argv, &enpv, 1, &si);
400 return wmain(argc, argv);