OSDN Git Service

25feb6c171bc63f0dff5af0dc792997adc485aae
[mhash384/mhash384.git] / frontend / src / main.cpp
1 /* ---------------------------------------------------------------------------------------------- */
2 /* MHash-384 - Simple fast portable secure hashing library                                        */
3 /* Copyright(c) 2016-2020 LoRd_MuldeR <mulder2@gmx.de>                                            */
4 /*                                                                                                */
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:                           */
10 /*                                                                                                */
11 /* The above copyright notice and this permission notice shall be included in all copies or       */
12 /* substantial portions of the Software.                                                          */
13 /*                                                                                                */
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 /* ---------------------------------------------------------------------------------------------- */
20
21 #include "mhash384.h"
22 #include "common.h"
23 #include "self_test.h"
24 #include "utils.h"
25 #include "sys_info.h"
26 #include <ctime>
27 #include <sys/stat.h>
28
29 /* Win32 I/O stuff */
30 #ifdef _WIN32
31 #include <fcntl.h>
32 #include <io.h>
33 #ifdef _MSC_VER
34 #define fstat _fstat
35 #define stat _stat
36 #define fileno _fileno
37 #endif
38 #ifndef _O_U8TEXT
39 #define _O_U8TEXT 0x40000
40 #endif
41 #endif
42
43 /* System type */
44 #define SYSTEM_TYPE STR(SYSTEM_NAME) STR("-") STR(SYSTEM_ARCH)
45
46 /* Buffer size */
47 static const size_t BUFFER_SIZE = 8192U;
48
49 /* Mode of operation */
50 typedef enum
51 {
52         MODE_DEFAULT  =  0,
53         MODE_MANPAGE  =  1,
54         MODE_VERSION  =  2,
55         MODE_SELFTEST =  3,
56         MODE_STRESS   =  4,
57         MODE_UNKNOWN  = -1
58 }
59 opmode_t;
60
61 /*
62  * Print the logo
63  */
64 static void print_logo(void)
65 {
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);
71         fflush(stderr);
72 }
73
74 /*
75  * Print the version
76  */
77 static void print_version(void)
78 {
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);
83 }
84
85 /*
86  * Print the manpage
87  */
88 static void print_manpage(const CHAR_T *const argv0)
89 {
90         const CHAR_T *const program_name = get_basename(argv0);
91         print_logo();
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);
105 }
106
107 /*
108  * Parse command-line options
109  */
110 static opmode_t parse_options(int &arg_offset, options_t &options, const int argc, const CHAR_T *const *const argv)
111 {
112         opmode_t mode = MODE_DEFAULT;
113         memset(&options, 0, sizeof(options_t));
114         bool stop_here = false;
115
116         while((!stop_here) && (arg_offset < argc) && (!STRNICMP(argv[arg_offset], STR("--"), 2U)))
117         {
118                 const CHAR_T *const argstr = argv[arg_offset] + 2U;
119                 if(!(*argstr))
120                 {
121                         stop_here = true;
122                 }
123                 else if(!STRICMP(argstr, STR("keep-going")))
124                 {
125                         options.keep_going = true;
126                 }
127                 else if(!STRICMP(argstr, STR("short")))
128                 {
129                         options.short_format = true;
130                 }
131                 else if (!STRICMP(argstr, STR("base64")))
132                 {
133                         options.base64 = true;
134                 }
135                 else if(!STRICMP(argstr, STR("lower-case")))
136                 {
137                         options.lower_case = true;
138                 }
139                 else if(!STRICMP(argstr, STR("help")))
140                 {
141                         mode = MODE_MANPAGE;
142                 }
143                 else if(!STRICMP(argstr, STR("version")))
144                 {
145                         mode = MODE_VERSION;
146                 }
147                 else if(!STRICMP(argstr, STR("self-test")))
148                 {
149                         mode = MODE_SELFTEST;
150                 }
151                 else if(!STRICMP(argstr, STR("stress")))
152                 {
153                         mode = MODE_STRESS;
154                 }
155                 else if(!STRICMP(argstr, STR("benchmark")))
156                 {
157                         options.benchmark = true;
158                 }
159                 else
160                 {
161                         print_logo();
162                         FPRINTF(stderr, STR("Error: Specified option \"%") PRI_CHAR STR("\" is unknown. Please type \"--help\" for details!\n"), argv[arg_offset]);
163                         fflush(stderr);
164                         return MODE_UNKNOWN;
165                 }
166                 ++arg_offset;
167         }
168
169         if (options.base64 && options.lower_case)
170         {
171                 print_logo();
172                 FPUTS(STR("Error: Options \"--base64\" and \"--lower-case\" are mutually exclusive!\n"), stderr);
173                 fflush(stderr);
174                 return MODE_UNKNOWN;
175         }
176
177         return mode;
178 }
179
180 /*
181  * Process input file
182  */
183 static bool process_file(const CHAR_T *const file_name, const options_t options)
184 {
185         /* File description */
186         const CHAR_T *const file_description = file_name ? file_name : STR("<STDIN>");
187
188         /* Open the input file */
189         errno = 0;
190         FILE *const input = file_name ? FOPEN(file_name, STR("rb")) : stdin;
191         if(!input)
192         {
193                 FPRINTF(stderr, STR("Error: File \"%") PRI_CHAR STR("\" could not be opened for reading! [errno: %d]\n"), file_description, errno);
194                 fflush(stderr);
195                 return false;
196         }
197
198         /* Check if file is directory (this is required for Linux!)*/
199         struct stat file_info;
200         if(!fstat(fileno(input), &file_info))
201         {
202                 if((file_info.st_mode & S_IFMT) == S_IFDIR)
203                 {
204                         FPRINTF(stderr, STR("Error: File \"%") PRI_CHAR STR("\" is a directory!\n"), file_description);
205                         fclose(input);
206                         fflush(stderr);
207                         return false;
208                 }
209         }
210
211         /* Initialize hash state */
212         MHash384 mhash384;
213         uint8_t buffer[BUFFER_SIZE];
214         bool success = true;
215
216         /* Process complete input */
217         for(;;)
218         {
219                 const size_t length = fread(buffer, sizeof(uint8_t), BUFFER_SIZE, input);
220                 if(!length)
221                 {
222                         break; /*EOF or error*/
223                 }
224                 mhash384.update(buffer, length);
225         }
226
227         /* Print the final digest */
228         if(!ferror(input))
229         {
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);
235                 fflush(stdout);
236         }
237         else
238         {
239                 FPRINTF(stderr, STR("Error: File \"%") PRI_CHAR STR("\" encountered an I/O error!\n"), file_description);
240                 fflush(stderr);
241                 success = false;
242         }
243
244         /* Close the input file */
245         if(file_name)
246         {
247                 fclose(input);
248         }
249
250         return success;
251 }
252
253 /*
254  * Main function
255  */
256 static int mhash384_main(int argc, CHAR_T* argv[])
257 {
258         int arg_offset = 1;
259         bool success = false;
260
261 #ifdef _WIN32
262         _setmode(_fileno(stdout), _O_U8TEXT);
263         _setmode(_fileno(stderr), _O_U8TEXT);
264         _setmode(_fileno(stdin ), _O_BINARY);
265 #endif /*_WIN32*/
266         
267         /* Parse all command-line options */
268         options_t options;
269         const opmode_t mode = parse_options(arg_offset, options, argc, argv);
270         if(mode == MODE_UNKNOWN)
271         {
272                 return EXIT_FAILURE;
273         }
274
275         /* Remember startup time */
276         const clock_t time_start = options.benchmark ? clock() : 0U;
277
278         /* Select mode of operation */
279         switch(mode)
280         {
281         case MODE_MANPAGE:
282                 /* Print help screen and exit* */
283                 print_manpage(argv[0U]);
284                 success = true;
285                 break;
286
287         case MODE_VERSION:
288                 /* Print program version and exit */
289                 print_version();
290                 success = true;
291                 break;
292
293         case MODE_SELFTEST:
294                 /* Run the self-test */
295                 print_logo();
296                 success = self_test(options);
297                 break;
298
299         case MODE_STRESS:
300                 /* Enable stress-test mode*/
301                 print_logo();
302                 success = stress_test((arg_offset < argc) ? argv[arg_offset] : NULL, options);
303                 break;
304
305         default:
306                 /* Process all input files */
307                 if(arg_offset < argc)
308                 {
309                         while(arg_offset < argc)
310                         {
311                                 if(process_file(argv[arg_offset++], options))
312                                 {
313                                         success = true;
314                                 }
315                                 else if(!options.keep_going)
316                                 {
317                                         success = false;
318                                         break;
319                                 }
320                         }
321                 }
322                 else
323                 {
324                         success = process_file(NULL, options); /*stdin*/
325                 }
326         }
327
328         /* Print total time */
329         if(options.benchmark && ((mode == MODE_DEFAULT) || ((mode >= MODE_SELFTEST) && (mode <= MODE_STRESS))))
330         {
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));
333         }
334
335         /* Completed */
336         return success ? EXIT_SUCCESS : EXIT_FAILURE;
337 }
338
339 /*
340  * Global exception handler
341  */
342 static int _main_(int argc, CHAR_T* argv[])
343 {
344 #ifdef NDEBUG
345         try
346         {
347                 return mhash384_main(argc, argv);
348         }
349         catch (std::exception& ex)
350         {
351                 FPRINTF(stderr, STR("\nEXCEPTION: %") PRI_char STR("\n"), ex.what());
352                 FORCE_EXIT(-1);
353         }
354         catch (const char *const ex)
355         {
356                 FPRINTF(stderr, STR("\nEXCEPTION: %") PRI_char STR("\n"), ex);
357                 FORCE_EXIT(-1);
358         }
359         catch (...)
360         {
361                 FPUTS(STR("\nEXCEPTION: An unknown exception has been caught!\n"), stderr);
362                 FORCE_EXIT(-1);
363         }
364 #else
365         return mhash384_main(argc, argv);
366 #endif
367 }
368
369 /*
370  * Entry point function
371  */
372 int EXTRY_POINT(int argc, CHAR_T* argv[])
373 {
374 #if defined(_WIN32) && !defined(__GNUC__) && defined(NDEBUG)
375         __try
376         {
377                 return _main_(argc, argv);
378         }
379         __except(1)
380         {
381                 FPUTS(STR("\nEXCEPTION: Unhandeled structured exception caught!\n"), stderr);
382                 FORCE_EXIT(-1);
383         }
384 #else
385         return _main_(argc, argv);
386 #endif
387 }
388
389 /*
390  * For legacy MinGW (not required for Mingw-w64, use '-municode' instead)
391  */
392 #if defined(_WIN32) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
393 extern "C"
394 void __wgetmainargs(int*, wchar_t***, wchar_t***, int, int*);
395 int main()
396 {
397         wchar_t **enpv, **argv;
398         int argc, si = 0;
399         __wgetmainargs(&argc, &argv, &enpv, 1, &si);
400         return wmain(argc, argv);
401 }
402 #endif