OSDN Git Service

Refactored error handlers.
[mhash384/mhash384.git] / src / utilities.h
1 /* ---------------------------------------------------------------------------------------------- */
2 /* MHash-384 - Example application (utility functions)                                            */
3 /* Copyright(c) 2016-2018 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 #ifndef MHASH_CLI_UTILS_INCLUDED
22 #define MHASH_CLI_UTILS_INCLUDED
23
24 /*Enable large files*/
25 #define __USE_LARGEFILE64 1
26
27 /*Includes*/
28 #include "compat.h"
29 #include "sysinfo.h"
30
31 /*CRT includes*/
32 #include <stdio.h>
33 #include <string.h>
34 #include <sys/stat.h>
35 #include <signal.h>
36 #include <ctype.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <float.h>
40
41 /*POSIX API*/
42 #ifdef __unix__
43 #include <unistd.h>
44 #endif
45
46 /*Win32-only header*/
47 #ifdef _WIN32
48 #include <io.h>
49 #endif
50
51 /*Hash size*/
52 #ifdef __cplusplus
53 #define MY_HASH_LENGTH mhash_384::MHash384::HASH_LEN
54 #else
55 #define MY_HASH_LENGTH MHASH_384_LEN
56 #endif
57
58 /*Build date*/
59 static const CHAR *const BUILD_DATE = T(__DATE__);
60
61 /*Mode*/
62 #define OPMODE_HELP 1
63 #define OPMODE_VERS 2
64 #define OPMODE_TEST 3
65
66 /*Parameters*/
67 typedef struct param_t
68 {
69         int opmode;
70         int enable_bench;
71         int show_progress;
72         int use_upper_case;
73         int curly_brackets;
74         int raw_output;
75         int ignore_errors;
76 }
77 param_t;
78
79 /*Version*/
80 typedef struct version_t
81 {
82         uint_fast16_t major;
83         uint_fast16_t minor;
84         uint_fast16_t patch;
85 }
86 version_t;
87
88 /*Get version*/
89 static version_t get_version(void)
90 {
91         version_t version_info;
92 #ifdef __cplusplus
93         mhash_384::MHash384::version(version_info.major, version_info.minor, version_info.patch);
94 #else
95         version_info.major = MHASH_384_VERSION_MAJOR;
96         version_info.minor = MHASH_384_VERSION_MINOR;
97         version_info.patch = MHASH_384_VERSION_PATCH;
98 #endif
99         return version_info;
100 }
101
102 /*The logo*/
103 static void print_logo(void)
104 {
105         const version_t version = get_version();
106         FPRINTF(stderr, T("\nMHash384 v%u.%u.%u, simple fast portable header-only hashing library [%s]\n"), (unsigned int)version.major, (unsigned int)version.minor, (unsigned int)version.patch, BUILD_DATE);
107         FPRINTF(stderr, T("Copyright(c) 2016-%s LoRd_MuldeR <mulder2@gmx.de>, released under the MIT License.\n\n"), &BUILD_DATE[7]);
108 }
109
110 /*Lib version*/
111 static void print_vers(void)
112 {
113         const version_t version = get_version();
114         FPRINTF(stdout, T("mhash-384 version %u.%u.%u (built %s)\n"), (unsigned int)version.major, (unsigned int)version.minor, (unsigned int)version.patch, BUILD_DATE);
115 }
116
117 /*File name suffix*/
118 #ifdef _WIN32
119 #define EXE_SUFFIX ".exe"
120 #else
121 #define EXE_SUFFIX ""
122 #endif
123
124 /*Print help screen*/
125 static void print_help(void)
126 {
127         print_logo();
128         FPRINTF(stderr, T("Built with %s v%u.%u.%u on %s [%s]\n\n"), T(COMPILER_TYPE), COMPILER_VERS_MAJOR, COMPILER_VERS_MINOR, COMPILER_VERS_PATCH, T(SYSTEM_TYPE), T(COMPILER_ARCH));
129         FPUTS(T("Usage:\n"), stderr);
130         FPRINTF(stderr, T("  mhash384%s [options] [input_file]...\n\n"), T(EXE_SUFFIX));
131         FPUTS(T("Options:\n"), stderr);
132         FPUTS(T("  -p, --progress  show progress while processing\n"), stderr);
133         FPUTS(T("  -u, --upper     print digest in upper case letters\n"), stderr);
134         FPUTS(T("  -c, --curly     print digest using C-style curly brackets\n"), stderr);
135         FPUTS(T("  -r, --raw       output \"raw\" bytes (no \"hex\" encoding)\n"), stderr);
136         FPUTS(T("  -b, --bench     compute and print throughput\n"), stderr);
137         FPUTS(T("  -i, --ignore    ignore errors and proceed with ntext file\n"), stderr);
138         FPUTS(T("  -v, --version   print the version string and exit\n"), stderr);
139         FPUTS(T("  -t, --test      execute self-test and exit\n"), stderr);
140         FPUTS(T("  -h, --help      print this help screen and exit\n\n"), stderr);
141         FPUTS(T("If no input file is specified, input will be read from STDIN.\n\n"), stderr);
142 }
143
144 /*Check specific option*/
145 #define IS_OPTION(ARGV, IS_LONG, NAME_SHRT, NAME_LONG) \
146         ((IS_LONG) ? (!STRICMP((ARGV), (NAME_LONG))) : (TOLOWER(*(ARGV)) == (NAME_SHRT)))
147
148 /*Parse option*/
149 static int parse_option(param_t *param, const CHAR *const argv, const int is_long)
150 {
151         if (IS_OPTION(argv, is_long, T('b'), T("bench")))
152         {
153                 param->enable_bench = 1;
154                 return 1;
155         }
156         if (IS_OPTION(argv, is_long, T('i'), T("ignore")))
157         {
158                 param->ignore_errors = 1;
159                 return 1;
160         }
161         if (IS_OPTION(argv, is_long, T('p'), T("progress")))
162         {
163                 param->show_progress = 1;
164                 return 1;
165         }
166         if (IS_OPTION(argv, is_long, T('u'), T("upper")))
167         {
168                 param->use_upper_case = 1;
169                 return 1;
170         }
171         if (IS_OPTION(argv, is_long, T('c'), T("curly")))
172         {
173                 param->curly_brackets = 1;
174                 return 1;
175         }
176         if (IS_OPTION(argv, is_long, T('r'), T("raw")))
177         {
178                 param->raw_output = 1;
179                 return 1;
180         }
181         if (IS_OPTION(argv, is_long, T('h'), T("help")))
182         {
183                 param->opmode = OPMODE_HELP;
184                 return 1;
185         }
186         if (IS_OPTION(argv, is_long, T('v'), T("version")))
187         {
188                 param->opmode = OPMODE_VERS;
189                 return 1;
190         }
191         if (IS_OPTION(argv, is_long, T('t'), T("test")))
192         {
193                 param->opmode = OPMODE_TEST;
194                 return 1;
195         }
196         return 0;
197 }
198
199 /*Parse arguments*/
200 static int parse_arguments(param_t *const param, int argc, CHAR *argv[])
201 {
202         int i, j;
203         memset(param, 0, sizeof(param_t));
204         for (i = 1; i < argc; ++i)
205         {
206                 if (argv[i][0] == T('-'))
207                 {
208                         if (argv[i][1] == T('-'))
209                         {
210                                 if (!argv[i][2])
211                                 {
212                                         ++i;
213                                         break; /*stop*/
214                                 }
215                                 if (!parse_option(param, &argv[i][2], 1))
216                                 {
217                                         print_logo();
218                                         FPRINTF(stderr, T("Unknown option:\n%s\n\n"), argv[i]);
219                                         return 0;
220                                 }
221                                 continue;
222                         }
223                         else if (argv[i][1])
224                         {
225                                 for (j = 1; argv[i][j]; ++j)
226                                 {
227                                         if(!parse_option(param, &argv[i][j], 0))
228                                         {
229                                                 print_logo();
230                                                 FPRINTF(stderr, T("Unknown option(s):\n%s\n\n"), argv[i]);
231                                                 return 0;
232                                         }
233                                 }
234                                 continue;
235                         }
236                 }
237                 break; /*no more options*/
238         }
239         if (param->raw_output && (param->use_upper_case || param->curly_brackets))
240         {
241                 print_logo();
242                 FPRINTF(stderr, T("Error: Options \"-%c\" and \"-r\" are mutually exclusive!\n\n"), param->use_upper_case ? T('u') : T('c'));
243                 return 0;
244         }
245         return i;
246 }
247
248 /*file size*/
249 static int get_file_info(FILE *const file, uint64_t *const file_size, MODE_T *const file_type)
250 {
251         STAT64_T info;
252         if (!FSTAT64(FILENO(file), &info))
253         {
254                 *file_type = (info.st_mode & S_IFMT);
255                 *file_size = (*file_type == S_IFREG) ? (uint64_t)info.st_size : UINT64_MAX;
256                 return 1;
257         }
258         return 0;
259 }
260
261 /*progress*/
262 static void print_progress(const uint64_t size_total, const uint64_t size_processed)
263 {
264         if (size_total != UINT64_MAX)
265         {
266                 const double progress = (((double)size_processed) / (((double)size_total) + DBL_EPSILON)) * 100.0;
267                 FPRINTF(stderr, T("\r%.1f%% of %") T(PRIu64) T(" bytes processed..."), progress, size_total);
268         }
269         else
270         {
271                 FPRINTF(stderr, T("\r%") T(PRIu64) T(" bytes processed..."), size_processed);
272         }
273 }
274
275 /*print digest*/
276 #define _PUT_HEX_CHAR(W,X,Y,Z) FPUTC(W[((X) >> (Y)) & 0xFU], (Z))
277 static void print_digest(FILE *const stream, const uint8_t *const digest, const int uppercase, const int curly)
278 {
279         static const CHAR *const HEX_UPR = T("0123456789ABCDEF");
280         static const CHAR *const HEX_LWR = T("0123456789abcdef");
281         const CHAR *const hex = uppercase ? HEX_UPR : HEX_LWR;
282         uint16_t count;
283         if (curly)
284         {
285                 FPUTS(T("{ "), stream);
286         }
287         for (count = 0U; count < MY_HASH_LENGTH; ++count)
288         {
289                 if (curly)
290                 {
291                         FPUTS(count ? T(", 0x") : T("0x"), stream);
292                 }
293                 _PUT_HEX_CHAR(hex, digest[count], 4, stream);
294                 _PUT_HEX_CHAR(hex, digest[count], 0, stream);
295         }
296         if (curly)
297         {
298                 FPUTS(T(" }"), stream);
299         }
300 }
301
302 /*sigint handler*/
303 static volatile int g_interrupted;
304 static void sigint_handler(int sig_no)
305 {
306         g_interrupted = 1;
307         signal(sig_no, sigint_handler);
308         fclose(stdin);
309 }
310
311 /*clear all errors*/
312 static void clear_errors()
313 {
314         errno = 0;
315         SET_SYSERRNO(0);
316 }
317
318 /*error handler*/
319 static void print_error(const CHAR *const message, const CHAR *const file_name, const param_t *const param, const int multi_file)
320 {
321         const CHAR *const source_name = file_name ? file_name : T("<STDIN>");
322         if (param->ignore_errors && multi_file)
323         {
324                 FPRINTF(stderr, T("Skipped file: %s\n"), source_name); /*skip error message*/
325         }
326         else
327         {
328                 const errno_t error = errno;
329                 const unsigned long syserrno = SYSERRNO;
330                 print_logo();
331                 if (error && syserrno)
332                 {
333                         FPRINTF(stderr, T("%s:\n%s\n\n>>> %s [Code: 0x%X] <<<\n\n"), message, source_name, STRERROR(error), syserrno);
334                 }
335                 else
336                 {
337                         FPRINTF(stderr, T("%s:\n%s\n\n>>> %s <<<\n\n"), message, source_name, STRERROR(error));
338                 }
339         }
340 }
341
342 #endif /*MHASH_CLI_UTILS_INCLUDED*/