/[dtapublic]/projs/dtats/trunk/projs/2018/20180112_ifsfscan/c_main.c
ViewVC logotype

Contents of /projs/dtats/trunk/projs/2018/20180112_ifsfscan/c_main.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 210 - (show annotations) (download)
Wed Jul 18 01:31:21 2018 UTC (6 years, 4 months ago) by dashley
File MIME type: text/plain
File size: 17394 byte(s)
Rebuild, reorganization.
1 //$Header$
2 //{f0d952cc-7499-4d5c-9f46-d0b509c5701c}
3 //-------------------------------------------------------------------------------------------------
4 //Copyright (c) 2018, David T. Ashley
5 //
6 //This file is part of "ifsfscan", a program for identifying gross formatting anomalies in source
7 //files (Windows/ASCII text files only).
8 //
9 //This source code and any program in which it is compiled/used is licensed under the MIT License,
10 //reproduced below.
11 //
12 //Permission is hereby granted, free of charge, to any person obtaining a copy of
13 //this software and associated documentation files(the "Software"), to deal in the
14 //Software without restriction, including without limitation the rights to use,
15 //copy, modify, merge, publish, distribute, sublicense, and / or sell copies of the
16 //Software, and to permit persons to whom the Software is furnished to do so,
17 //subject to the following conditions :
18 //
19 //The above copyright notice and this permission notice shall be included in all
20 //copies or substantial portions of the Software.
21 //
22 //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 //SOFTWARE.
29 //-------------------------------------------------------------------------------------------------
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <tchar.h>
34 #include <time.h>
35 #include <windows.h>
36
37 #define FCMIOF_HORIZONTAL_BAR_SEP_CHAR ('-')
38 #define FCMIOF_LINE_LEN (78)
39
40 //The last column allowed for a characters below is Column 82 (or it will be
41 //less than aesthetic).
42 const char * const license_preamble[] =
43 {
44 "ifsfscan, (c) 2018 David T. Ashley (dashley@gmail.com)",
45 "This program's source files, executable files, and all other related files",
46 "(such as Visual Studio project files) are licensed under \"The MIT License\",",
47 "reproduced below."
48 };
49
50 const char * const license_text[] =
51 {
52 "Permission is hereby granted, free of charge, to any person obtaining a copy",
53 "of this software and associated documentation files(the \"Software\"), to deal",
54 "in the Software without restriction, including without limitation the rights",
55 "to use, copy, modify, merge, publish, distribute, sublicense, and / or sell",
56 "copies of the Software, and to permit persons to whom the Software is",
57 "furnished to do so, subject to the following conditions:",
58 "",
59 "The above copyright notice and this permission notice shall be included in",
60 "all copies or substantial portions of the Software.",
61 "",
62 "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
63 "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
64 "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
65 "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
66 "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,",
67 "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE",
68 "SOFTWARE."
69 };
70
71 const char * const prog_desc_text[] =
72 {
73 "ifsfscan (mnemonic: Ill-Formed Source File SCAN) is a program for detecting",
74 "gross formatting errors in source files (Windows/ASCII text files only). The",
75 "errors detected are non-ASCII characters, tab characters, abnormal",
76 "end-of-line characters, and trailing whitespace on lines.",
77 };
78
79 const char * const prog_help_text[] =
80 {
81 "Usage: ifsfscan [filename_or_wildcard [filename_or_wildcard [...]]]",
82 "",
83 "Notes:",
84 " (1) : Wildcards (\"*\", \"?\") are processed by Windows, so Windows is",
85 " the arbiter of which wildcards are accepted and how they expand.",
86 " (2) : This program never writes to a file, so it cannot destroy data",
87 " (except, possibly, by stdout output redirected to a file).",
88 " (3) : This program accepts no options (like \"-help\" or \"-verbose\").",
89 " (4) : This program accepts only Windows line endings (13-10).",
90 " This program is incompatible with *nix and *nix files.",
91 " (5) : This program accepts only the ASCII character set (it will not",
92 " process UTF-8 or any other encodings).",
93 };
94
95 //--------------------------------------------------------------------------------
96 // T E R M I N A T I O N F U N C T I O N S
97 //--------------------------------------------------------------------------------
98 void CCMFATAL_fatal(const char *desc, const char *file, size_t line)
99 {
100 printf("\n");
101 printf("Fatal error. Must terminate execution.\n");
102 printf("File: %s, Line: %zu.\n", file, line);
103 printf("Error description: %s\n", desc);
104 exit(4); //Error code 4 for error termination.
105 }
106
107 //--------------------------------------------------------------------------------
108 // A S S E R T I O N F U N C T I O N S
109 //--------------------------------------------------------------------------------
110 void USERASSERT_assert(int assertion, const char *file, size_t line)
111 {
112 if (! assertion)
113 {
114 printf("\n");
115 printf("Assertion failed. It is necessary to use the source code to diagnose\n");
116 printf("and resolve this error.\n");
117 printf("File: %s, Line: %zu.\n", file, line);
118 exit(4); //Error code 4 for error termination.
119 }
120 }
121
122
123 //--------------------------------------------------------------------------------
124 // S T R I N G A N D C H A R A C T E R A R R A Y F U N C T I O N S
125 //--------------------------------------------------------------------------------
126 int STRING_contains_wildcard(const char *s)
127 {
128 if (strchr(s, '?') != NULL)
129 return(1);
130 else if (strchr(s, '*') != NULL)
131 return(1);
132 else
133 return(0);
134 }
135
136 int STRING_is_longer_than_maxpath(const char *s)
137 {
138 if (_MAX_PATH <= 5)
139 return(1);
140 else if (strlen(s) > (_MAX_PATH - 5))
141 return(1);
142 else
143 return(0);
144 }
145
146 int STRING_contains_terminating_backslash(const char *s)
147 {
148 size_t i;
149
150 i = strlen(s);
151
152 if (i == 0)
153 {
154 return(0);
155 }
156 else
157 {
158 do
159 {
160 i--;
161 if (s[i] == '\\')
162 return(1);
163 else if ((s[i] != ' ') && (s[i] != '\t'))
164 return(0);
165 } while (i != 0);
166 return(0);
167 }
168 }
169
170 const char *STRING_vcinfo(size_t which)
171 {
172 static const char * const vcinfo[] =
173 {
174 "$HeadURL$",
175 "$Date$",
176 "$Revision$",
177 "$Author$",
178 "Project GUID: {f7d7586a-557c-43cb-bec5-b49765d96a5d}",
179 "c_main.c GUID: {f0d952cc-7499-4d5c-9f46-d0b509c5701c}",
180 "ifsfscan.cpp GUID: {2abd4437-101c-49eb-99ac-c1174f55b626}",
181 };
182
183 if (which < (sizeof(vcinfo) / sizeof(vcinfo[0])))
184 return(vcinfo[which]);
185 else
186 return(NULL);
187 }
188
189 //--------------------------------------------------------------------------------
190 // F O R M A T T E D O U T P U T F U N C T I O N S
191 //--------------------------------------------------------------------------------
192 int FCMIOF_get_line_len(void)
193 {
194 return(FCMIOF_LINE_LEN);
195 }
196
197 void FCMIOF_stream_repchar(FILE *s, char c, unsigned n)
198 {
199 USERASSERT_assert(s != NULL, __FILE__, __LINE__);
200
201 while (n--)
202 fprintf(s, "%c", c);
203 }
204
205 void FCMIOF_repchar(char c, unsigned n)
206 {
207 while (n--)
208 printf("%c", c);
209 }
210
211 void FCMIOF_hline(void)
212 {
213 FCMIOF_repchar(FCMIOF_HORIZONTAL_BAR_SEP_CHAR, FCMIOF_LINE_LEN);
214 printf("\n");
215 }
216
217 void FCMIOF_stream_hline(FILE *s)
218 {
219 USERASSERT_assert(s != NULL, __FILE__, __LINE__);
220
221 FCMIOF_stream_repchar(s, FCMIOF_HORIZONTAL_BAR_SEP_CHAR, FCMIOF_LINE_LEN);
222 fprintf(s, "\n");
223 }
224
225 int is_legal_non_eol_character(char c)
226 {
227 if ((c >= 32) && (c <= 126))
228 return(1);
229 else
230 return(0);
231 }
232
233 int is_illegal_non_eol_character(char c)
234 {
235 if (((c < 32) || (c > 126)) && (c != 13) && (c != 10))
236 return(1);
237 else
238 return(0);
239 }
240
241 int is_tab(char c)
242 {
243 if (c == 9)
244 return(1);
245 else
246 return(0);
247 }
248
249 int is_cr(char c)
250 {
251 if (c == 13)
252 return(1);
253 else
254 return(0);
255 }
256
257 int is_lf(char c)
258 {
259 if (c == 10)
260 return(1);
261 else
262 return(0);
263 }
264
265 void emit_human_friendly_llu(unsigned long long arg)
266 {
267 size_t i, len;
268 char buffer[100];
269
270 sprintf_s(buffer, sizeof(buffer)/sizeof(buffer[0]), "%llu", arg);
271 len = strlen(buffer);
272
273 for (i = 0; i < len; i++)
274 {
275 printf("%c", buffer[i]);
276 if (((len-i-1) != 0) && (((len - i - 1) % 3) == 0))
277 printf(",");
278 }
279 }
280
281 void emit_file_pos_3tuple(unsigned long long line, unsigned long long col, unsigned long long offset)
282 {
283 printf(" Line: ");
284 emit_human_friendly_llu(line);
285 printf(", Col: ");
286 emit_human_friendly_llu(col);
287 printf(", Offset: ");
288 emit_human_friendly_llu(offset);
289 printf("\n");
290 }
291
292 void process_opened_handle(FILE *f)
293 {
294 unsigned long long char_no;
295 unsigned long long line_no;
296 unsigned long long col_no;
297 enum {PST_LINE, PST_CR_FOUND, PST_LF_FOUND} state = PST_LINE;
298 int exit_flag = 0;
299 char in_c, prev_c;
300 unsigned char in_uc;
301 int in_i;
302
303 in_i = fgetc(f);
304
305 if (in_i == EOF)
306 {
307 //Zero-length file or error. Because this tool isn't critical, no need to figure out which, exactly.
308 printf(" Zero-length file.\n");
309 return;
310 }
311
312 prev_c = ' ';
313 char_no = 0;
314 line_no = 1;
315 col_no = 1;
316 in_c = in_i & 0xff;
317 in_uc = in_i & 0xff;
318
319 do
320 {
321 //Illegal characters always get flagged.
322 if (is_illegal_non_eol_character(in_c))
323 {
324 emit_file_pos_3tuple(line_no, col_no, char_no);
325 printf(" Illegal character: 0x%02x.\n", ((unsigned)in_uc));
326 }
327
328 //printf("Character: %02x State: %u\n", in_c, (unsigned)state);
329
330 //Run through the state machine, which would look for bad EOL sequences.
331 switch (state)
332 {
333 case PST_LINE:
334 //Processing non-EOL characters.
335 if (is_lf(in_c))
336 {
337 if ((char_no != 0) && (prev_c == ' '))
338 {
339 emit_file_pos_3tuple(line_no, col_no, char_no);
340 printf(" Line contains trailing whitespace.\n");
341 }
342
343 //Line feeds not allowed without preceding carriage return.
344 emit_file_pos_3tuple(line_no, col_no, char_no);
345 printf(" Out of sequence line feed character (0x0a).\n");
346 line_no++;
347 col_no = 1;
348 state = PST_LF_FOUND;
349 }
350 else if (is_cr(in_c))
351 {
352 if ((char_no != 0) && (prev_c == ' '))
353 {
354 emit_file_pos_3tuple(line_no, col_no, char_no);
355 printf(" Line contains trailing whitespace.\n");
356 }
357
358 //Legal
359 state = PST_CR_FOUND;
360 }
361 else
362 {
363 //Ordinary character.
364 col_no++;
365 }
366 break;
367 case PST_CR_FOUND:
368 if (is_lf(in_c))
369 {
370 //Legal
371 line_no++;
372 col_no = 1;
373 state = PST_LF_FOUND;
374 }
375 else if (is_cr(in_c))
376 {
377 //Back-to-back carriage returns not allowed.
378 emit_file_pos_3tuple(line_no, col_no, char_no);
379 printf(" Out of sequence carriage return character (0x0D).\n");
380 col_no++;
381 }
382 else
383 {
384 //Ordinary character. Illegal, because LF must follow CR.
385 emit_file_pos_3tuple(line_no, col_no, char_no);
386 printf(" Carriage return followed by 0x%02x rather than LF.\n", (unsigned)in_uc);
387 col_no++;
388 }
389 break;
390 case PST_LF_FOUND:
391 if (is_lf(in_c))
392 {
393 //Illegal. Back-to-back line feeds not allowed.
394 emit_file_pos_3tuple(line_no, col_no, char_no);
395 printf(" Out of sequence line feed character (0x0A).\n");
396 line_no++;
397 col_no = 1;
398 }
399 else if (is_cr(in_c))
400 {
401 //Legal. Blank lines are fine.
402 col_no++;
403 state = PST_CR_FOUND;
404 }
405 else
406 {
407 //Ordinary character. Legal.
408 col_no++;
409 state = PST_LINE;
410 }
411 break;
412 default:
413 USERASSERT_assert(0, __FILE__, __LINE__);
414 break;
415 }
416
417 in_i = fgetc(f);
418 prev_c = in_c;
419 in_c = in_i & 0xff;
420 in_uc = in_i & 0xff;
421 char_no++;
422 if (in_i == EOF)
423 {
424 if (state != PST_LF_FOUND)
425 {
426 emit_file_pos_3tuple(line_no, col_no, char_no-1);
427 printf(" Final line of file does not have CR/LF sequence.\n");
428 }
429 if ((state == PST_LINE) && (prev_c == ' '))
430 {
431 emit_file_pos_3tuple(line_no, col_no, char_no - 1);
432 printf(" Final line contains trailing whitespace.\n");
433 }
434
435 exit_flag = 1;
436 }
437 } while (!exit_flag);
438 }
439
440 void process_file_by_name(const char *s)
441 {
442 FILE *f;
443
444 printf(" %s\n", s);
445
446 f = fopen(s, "rb");
447
448 if (!f)
449 {
450 printf(" fopen() failed.\n");
451 }
452 else
453 {
454 process_opened_handle(f);
455
456 if (fclose(f))
457 {
458 printf(" fclose() failed.\n");
459 }
460 }
461 }
462
463 void process_filename_or_wildcard(const char *fname_or_wildcard)
464 {
465 HANDLE hFind;
466 WIN32_FIND_DATA FindFileData;
467
468 //Incoming pointer is worthy of an assertion. The OS should not every deliver
469 //a NULL pointer or empty string to us.
470 USERASSERT_assert(fname_or_wildcard != NULL, __FILE__, __LINE__);
471 USERASSERT_assert(strlen(fname_or_wildcard) > 0, __FILE__, __LINE__);
472
473 printf("%s\n", fname_or_wildcard);
474
475 if (STRING_is_longer_than_maxpath(fname_or_wildcard))
476 {
477 printf(" Specified filename or wildcard too long--cannot process.\n");
478 }
479 else if (STRING_contains_terminating_backslash(fname_or_wildcard))
480 {
481 printf(" Specified filename or wildcard contains terminating \"\\\"--cannot process.\n");
482 }
483 else if (STRING_contains_wildcard(fname_or_wildcard))
484 {
485 hFind = FindFirstFile((TCHAR *)fname_or_wildcard, &FindFileData);
486
487 if (hFind == INVALID_HANDLE_VALUE)
488 {
489 printf(" Wildcard does not match existing files or is invalid.\n");
490 }
491 else
492 {
493 char path_drive[_MAX_PATH + 5];
494 char path_dir[_MAX_PATH + 5];
495 char path_fname[_MAX_PATH + 5];
496 char path_ext[_MAX_PATH + 5];
497 char path_full[_MAX_PATH + 5];
498
499 if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
500 {
501 _splitpath(fname_or_wildcard, path_drive, path_dir, path_fname, path_ext);
502
503 strcpy(path_full, path_drive);
504 strcat(path_full, path_dir);
505 strcat(path_full, FindFileData.cFileName);
506
507 process_file_by_name(path_full);
508 }
509
510 while (FindNextFile(hFind, &FindFileData) != 0)
511 {
512 if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
513 {
514 _splitpath(fname_or_wildcard, path_drive, path_dir, path_fname, path_ext);
515
516 strcpy(path_full, path_drive);
517 strcat(path_full, path_dir);
518 strcat(path_full, FindFileData.cFileName);
519
520 process_file_by_name(path_full);
521 }
522 }
523 }
524
525 if (hFind != INVALID_HANDLE_VALUE)
526 {
527 FindClose(hFind);
528 }
529 }
530 else
531 {
532 process_file_by_name(fname_or_wildcard);
533 }
534 }
535
536 void emit_no_par_documentation(void)
537 {
538 size_t i;
539
540 FCMIOF_stream_hline(stdout);
541 for (i = 0; i < (sizeof(prog_desc_text) / sizeof(prog_desc_text[0])); i++)
542 printf("%s\n", prog_desc_text[i]);
543 FCMIOF_stream_hline(stdout);
544 for (i = 0; i < (sizeof(license_preamble) / sizeof(license_preamble[0])); i++)
545 printf("%s\n", license_preamble[i]);
546 FCMIOF_stream_hline(stdout);
547 for (i = 0; i < (sizeof(license_text) / sizeof(license_text[0])); i++)
548 printf("%s\n", license_text[i]);
549 FCMIOF_stream_hline(stdout);
550 printf("Program built on %s at %s.\n", __DATE__, __TIME__);
551 i = 0;
552 while (STRING_vcinfo(i))
553 {
554 printf("%s\n", STRING_vcinfo(i));
555 i++;
556 }
557 FCMIOF_stream_hline(stdout);
558 for (i = 0; i < (sizeof(prog_help_text) / sizeof(prog_help_text[0])); i++)
559 printf("%s\n", prog_help_text[i]);
560 FCMIOF_stream_hline(stdout);
561 }
562
563 void emit_execution_preamble(void)
564 {
565 FCMIOF_stream_hline(stdout);
566 printf("Use \"ifsfscan\" with no parameters to obtain license and help information.\n");
567 FCMIOF_stream_hline(stdout);
568 }
569
570 int c_main(int argc, char **argv)
571 {
572 int i;
573
574 if (argc <= 1)
575 {
576 //This is most likely someone trying to figure out what the program is or does.
577 //Spit everything.
578 emit_no_par_documentation();
579 }
580 else
581 {
582 emit_execution_preamble();
583
584 //Every argument beyond the program name has to be either a file name or
585 //wildcard. Just process them in order.
586 for (i = 1; i < argc; i++)
587 process_filename_or_wildcard(argv[i]);
588
589 FCMIOF_stream_hline(stdout);
590 }
591
592 return 0;
593 }

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision URL Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25