OSDN Git Service

Improved error handling on Win32.
[mhash384/mhash384.git] / src / main.c
1 /* ---------------------------------------------------------------------------------------------- */
2 /* MHash-384 - Example application (plain C)                                                      */
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 /*Includes*/
22 #include "mhash_384.h"
23 #include "utilities.h"
24
25 /*Test includes*/
26 #ifndef NO_SELFTEST
27 #include "self_test.h"
28 #endif
29
30 /*CRT includes*/
31 #include <errno.h>
32 #include <time.h>
33 #include <float.h>
34
35 /*Constants*/
36 #define BUFF_SIZE 4096
37
38 /*process a single file*/
39 static int process_file(const int multi_file, const param_t *const param, uint64_t *const bytes_total, CHAR *const file_name)
40 {
41         FILE *source;
42         uint64_t file_size, bytes_processed;
43         MODE_T file_type;
44         mhash_384_t context;
45         uint8_t buffer[BUFF_SIZE], result[MHASH_384_LEN];
46         uint_fast32_t count;
47         uint_fast16_t update_iter;
48
49         /*clear error indicators first*/
50         CLEAR_ERRORS();
51
52         /*check if file is accessible*/
53         if (file_name && ACCESS(file_name, R_OK))
54         {
55                 PRINT_ERROR(T("Specified input file is inaccessible"));
56                 return 0;
57         }
58
59         /*open source file*/
60         if (!(source = file_name ? FOPEN(file_name, T("rb")) : stdin))
61         {
62                 PRINT_ERROR(T("Failed to open specified input file"));
63                 return 0;
64         }
65
66         /*determine file properties*/
67         if(!get_file_info(source, &file_size, &file_type))
68         {
69                 PRINT_ERROR(T("Failed to determine file properties"));
70                 return 0;
71         }
72
73         /*is a directory?*/
74         if(file_type == S_IFDIR)
75         {
76                 errno = EISDIR;
77                 PRINT_ERROR(T("Unsupported file type encountered"));
78                 return 0;
79         }
80
81         /*initialization*/
82         mhash_384_initialize(&context);
83         update_iter = 0;
84         bytes_processed = 0;
85
86         /*process file contents*/
87         while (!(ferror(source) || feof(source)))
88         {
89                 count = (uint_fast32_t)fread(buffer, sizeof(uint8_t), BUFF_SIZE, source);
90                 if (count > 0)
91                 {
92                         mhash_384_update(&context, buffer, count);
93                         bytes_processed += count;
94                 }
95                 if (g_interrupted || (count != BUFF_SIZE))
96                 {
97                         break; /*stop*/
98                 }
99                 if (param->show_progress && ((update_iter++ & 0x3FF) == 0))
100                 {
101                         print_progress(file_size, bytes_processed);
102                         fflush(stderr);
103                 }
104         }
105
106         /*check file error status*/
107         if ((!g_interrupted) && ferror(source))
108         {
109                 errno = EIO; /*fread() doesn't set errno!*/
110                 PRINT_ERROR(T("Error encountered while reading from file"));
111                 fclose(source);
112                 return 0;
113         }
114
115         /*update gloabl counter*/
116         *bytes_total += bytes_processed;
117
118         /*final progress*/
119         if (param->show_progress)
120         {
121                 print_progress(file_size, bytes_processed);
122                 FPRINTF(stderr, T(" %s\n"), g_interrupted ? T("stop!") : T("done"));
123                 fflush(stderr);
124         }
125
126         /*finalize the hash*/
127         mhash_384_finalize(&context, result);
128
129         /*output result as Hex string*/
130         if (param->raw_output)
131         {
132                 fwrite(result, sizeof(uint8_t), MY_HASH_LENGTH, stdout);
133         }
134         else
135         {
136                 print_digest(stdout, result, param->use_upper_case, param->curly_brackets);
137                 if (multi_file)
138                 {
139                         FPRINTF(stdout, param->curly_brackets ? T("  /* %s */") : T("  %s"), file_name);
140                 }
141                 FPUTC(T('\n'), stdout);
142         }
143
144         /*flush*/
145         fflush(stdout);
146
147         /*check for interruption*/
148         if (g_interrupted)
149         {
150                 FPUTS(T("\nInterrupted!\n\n"), stderr);
151                 fflush(stderr);
152                 FORCE_EXIT(SIGINT);
153         }
154
155         /*clean up*/
156         if (source != stdin)
157         {
158                 fclose(source);
159         }
160
161         return 1;
162 }
163
164 /*The "main" function*/
165 int MAIN(int argc, CHAR *argv[])
166 {
167         param_t param;
168         int retval, file_id;
169         clock_t ts_start, ts_finish;
170         uint64_t bytes_total;
171
172         /*set up std i/o streams*/
173         SETMODE(stdin,  0);
174         SETMODE(stderr, 0);
175
176         /*disable buffering*/
177         setvbuf(stderr, NULL, _IONBF, 0);
178
179         /*install CTRL+C handler*/
180         signal(SIGINT, sigint_handler);
181
182         /*process command-line arguments*/
183         if ((file_id = parse_arguments(&param, argc, argv)) < 1)
184         {
185                 return 1;
186         }
187
188         /*select mode of operation*/
189         switch (param.opmode)
190         {
191         case OPMODE_HELP:
192                 print_help();
193                 return 0;
194         case OPMODE_VERS:
195                 print_vers();
196                 return 0;
197         case OPMODE_TEST:
198 #ifdef NO_SELFTEST
199                 FPUTS("Not compiled with self-test code!\n", stderr);
200                 return 1;
201 #else
202                 return self_test();
203 #endif
204         }
205
206         /*set up stdout mode*/
207         SETMODE(stdout, param.raw_output);
208
209         /*initialize*/
210         ts_start = clock();
211         bytes_total = 0;
212         retval = EXIT_SUCCESS;
213
214         /*process all input files*/
215         if (file_id < argc)
216         {
217                 const int multi_file = file_id < (argc - 1);
218                 while (file_id < argc)
219                 {
220                         if (!process_file(multi_file, &param, &bytes_total, argv[file_id++]))
221                         {
222                                 retval = EXIT_FAILURE;
223                                 if (!param.ignore_errors)
224                                 {
225                                         break; /*fail!*/
226                                 }
227                         }
228                 }
229         }
230         else
231         {
232                 if(!process_file(0U, &param, &bytes_total, NULL))
233                 {
234                         retval = EXIT_FAILURE;
235                 }
236         }
237
238         /*finalize*/
239         ts_finish = clock();
240
241         /*output stats*/
242         if (param.enable_bench)
243         {
244                 const double time_total = (ts_finish - ts_start) / (double)CLOCKS_PER_SEC;
245                 const double throughput = bytes_total / (time_total + DBL_EPSILON);
246                 FPRINTF(stderr, T("\nProcessed %") T(PRIu64) T(" bytes in %.1f seconds, %.1f bytes/sec.\n\n"), bytes_total, time_total, throughput);
247                 fflush(stderr);
248         }
249
250         /*exit*/
251         return retval;
252 }