OSDN Git Service

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