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"
37 #define fileno _fileno
40 #define _O_U8TEXT 0x40000
45 #define SYSTEM_TYPE STR(SYSTEM_NAME) STR("-") STR(SYSTEM_ARCH)
48 static const size_t BUFFER_SIZE = 8192U;
50 /* Mode of operation */
65 static void print_logo(void)
67 uint16_t ver_major, ver_minor, ver_patch;
68 mhash384_version(&ver_major, &ver_minor, &ver_patch);
69 FPRINTF(stderr, ver_patch ? STR("MHash-384 v%u.%02u-%u") : STR("MHash-384 v%u.%02u"), ver_major, ver_minor, ver_patch);
70 FPUTS(STR(", ") SYSTEM_TYPE STR(" [") STR(__DATE__) STR("]\n"), stderr);
71 FPUTS(STR("Created by LoRd_MuldeR <mulder2@gmx.de>, released under the MIT license.\n\n"), stderr);
78 static void print_version(void)
80 uint16_t ver_major, ver_minor, ver_patch;
81 mhash384_version(&ver_major, &ver_minor, &ver_patch);
82 FPRINTF(stdout, STR("MHash-384 v%u.%02u-%u, ") SYSTEM_TYPE STR("\n"), ver_major, ver_minor, ver_patch);
83 FPRINTF(stdout, STR("Built on ") STR(__DATE__) STR(" at ") STR(__TIME__) STR(", using ") STR(COMPILER_FMT) STR("\n"), COMPILER_ARG);
89 static void print_manpage(const CHAR_T *const argv0)
91 const CHAR_T *const program_name = get_basename(argv0);
93 FPUTS(STR("Usage:\n"), stderr);
94 FPRINTF(stderr, STR(" %") PRI_CHAR STR(" [OPTIONS] [<FILE_1> <FILE_2> ... <FILE_N>]\n\n"), program_name);
95 FPUTS(STR("Options:\n"), stderr);
96 FPUTS(STR(" --keep-going Try to proceed with the remaining files, if a file has failed\n"), stderr);
97 FPUTS(STR(" --short Print the digest in short format (no file names)\n"), stderr);
98 FPUTS(STR(" --lower-case Print the digest in lower-case letters (default: upper-case)\n"), stderr);
99 FPUTS(STR(" --base64 Print the digest in Base64 format (default: Hex format)\n"), stderr);
100 FPUTS(STR(" --help Print help screen and exit\n"), stderr);
101 FPUTS(STR(" --version Print program version and exit\n"), stderr);
102 FPUTS(STR(" --self-test Run self-test and exit\n"), stderr);
103 FPUTS(STR(" --stress Enable stress test mode; strings are read from the input file\n"), stderr);
104 FPUTS(STR(" --benchmark Measure the overall time required for the operation\n\n"), stderr);
105 FPUTS(STR("If *no* input file is specified, data is read from the standard input (stdin)\n"), stderr);
109 * Parse command-line options
111 static opmode_t parse_options(int &arg_offset, options_t &options, const int argc, const CHAR_T *const *const argv)
113 opmode_t mode = MODE_DEFAULT;
114 memset(&options, 0, sizeof(options_t));
115 bool stop_here = false;
117 while((!stop_here) && (arg_offset < argc) && (!STRNICMP(argv[arg_offset], STR("--"), 2U)))
119 const CHAR_T *const argstr = argv[arg_offset] + 2U;
124 else if(!STRICMP(argstr, STR("keep-going")))
126 options.keep_going = true;
128 else if(!STRICMP(argstr, STR("short")))
130 options.short_format = true;
132 else if (!STRICMP(argstr, STR("base64")))
134 options.base64 = true;
136 else if(!STRICMP(argstr, STR("lower-case")))
138 options.lower_case = true;
140 else if(!STRICMP(argstr, STR("help")))
144 else if(!STRICMP(argstr, STR("version")))
148 else if(!STRICMP(argstr, STR("self-test")))
150 mode = MODE_SELFTEST;
152 else if(!STRICMP(argstr, STR("stress")))
156 else if(!STRICMP(argstr, STR("benchmark")))
158 options.benchmark = true;
163 FPRINTF(stderr, STR("Error: Specified option \"%") PRI_CHAR STR("\" is unknown!\n"), argv[arg_offset]);
164 FPRINTF(stderr, STR("Type \"%") PRI_CHAR STR(" --help\" for a list of available options...\n"), argv[0U]);
171 if (options.base64 && options.lower_case)
174 FPUTS(STR("Error: Options \"--base64\" and \"--lower-case\" are mutually exclusive!\n"), stderr);
185 static bool process_file(const CHAR_T *const file_name, const options_t options)
187 /* File description */
188 const CHAR_T *const file_description = file_name ? file_name : STR("<STDIN>");
190 /* Open the input file */
192 FILE *const input = file_name ? FOPEN(file_name, STR("rb")) : stdin;
195 FPRINTF(stderr, STR("Error: File \"%") PRI_CHAR STR("\" could not be opened for reading! [errno: %d]\n"), file_description, errno);
200 /* Check if file is directory (this is required for Linux!)*/
201 struct stat file_info;
202 if(!fstat(fileno(input), &file_info))
204 if((file_info.st_mode & S_IFMT) == S_IFDIR)
206 FPRINTF(stderr, STR("Error: File \"%") PRI_CHAR STR("\" is a directory!\n"), file_description);
213 /* Initialize hash state */
215 uint8_t buffer[BUFFER_SIZE];
218 /* Process complete input */
221 const size_t length = fread(buffer, sizeof(uint8_t), BUFFER_SIZE, input);
224 break; /*EOF or error*/
226 mhash384.update(buffer, length);
229 /* Print the final digest */
232 const uint8_t *const digest = mhash384.finish();
233 const std::string string = options.base64 ? bytes_to_base64(digest, MHASH384_SIZE) : bytes_to_hex(digest, MHASH384_SIZE, options.lower_case);
234 const CHAR_T *const source_name = file_name ? file_name : STR("-");
235 const CHAR_T *const format = options.short_format ? STR("%") PRI_char STR("\n") : STR("%") PRI_char STR(" %") PRI_CHAR STR("\n");
236 FPRINTF(stdout, format, string.c_str(), source_name);
241 FPRINTF(stderr, STR("Error: File \"%") PRI_CHAR STR("\" encountered an I/O error!\n"), file_description);
246 /* Close the input file */
258 static int mhash384_main(int argc, CHAR_T* argv[])
261 bool success = false;
264 _setmode(_fileno(stdout), _O_U8TEXT);
265 _setmode(_fileno(stderr), _O_U8TEXT);
266 _setmode(_fileno(stdin ), _O_BINARY);
269 /* Parse all command-line options */
271 const opmode_t mode = parse_options(arg_offset, options, argc, argv);
272 if(mode == MODE_UNKNOWN)
277 /* Remember startup time */
278 const clock_t time_start = options.benchmark ? clock() : 0U;
280 /* Select mode of operation */
284 /* Print help screen and exit* */
285 print_manpage(argv[0U]);
290 /* Print program version and exit */
296 /* Run the self-test */
298 success = self_test(options);
302 /* Enable stress-test mode*/
304 success = stress_test((arg_offset < argc) ? argv[arg_offset] : NULL, options);
308 /* Process all input files */
309 if(arg_offset < argc)
311 while(arg_offset < argc)
313 if(process_file(argv[arg_offset++], options))
317 else if(!options.keep_going)
326 success = process_file(NULL, options); /*stdin*/
330 /* Print total time */
331 if(options.benchmark && ((mode == MODE_DEFAULT) || ((mode >= MODE_SELFTEST) && (mode <= MODE_STRESS))))
333 const clock_t total_time = clock() - time_start;
334 FPRINTF(stderr, STR("Operation took %.1f second(s).\n"), total_time / ((double)CLOCKS_PER_SEC));
338 return success ? EXIT_SUCCESS : EXIT_FAILURE;
342 * Global exception handler
344 static int _main_(int argc, CHAR_T* argv[])
349 return mhash384_main(argc, argv);
351 catch (std::exception& ex)
353 FPRINTF(stderr, STR("\nEXCEPTION: %") PRI_char STR("\n"), ex.what());
356 catch (const char *const ex)
358 FPRINTF(stderr, STR("\nEXCEPTION: %") PRI_char STR("\n"), ex);
363 FPUTS(STR("\nEXCEPTION: An unknown exception has been caught!\n"), stderr);
367 return mhash384_main(argc, argv);
372 * Entry point function
374 int EXTRY_POINT(int argc, CHAR_T* argv[])
376 #if defined(_WIN32) && !defined(__GNUC__) && defined(NDEBUG)
379 return _main_(argc, argv);
383 FPUTS(STR("\nEXCEPTION: Unhandeled structured exception caught!\n"), stderr);
387 return _main_(argc, argv);
392 * For legacy MinGW (not required for Mingw-w64, use '-municode' instead)
394 #if defined(_WIN32) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
396 void __wgetmainargs(int*, wchar_t***, wchar_t***, int, int*);
399 wchar_t **enpv, **argv;
401 __wgetmainargs(&argc, &argv, &enpv, 1, &si);
402 return wmain(argc, argv);