/[dtapublic]/projs/dtats/trunk/projs/2018/20180718_ets_ifsfscan/c_main.c
ViewVC logotype

Annotation of /projs/dtats/trunk/projs/2018/20180718_ets_ifsfscan/c_main.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 212 - (hide annotations) (download)
Thu Jul 19 02:25:24 2018 UTC (4 years, 10 months ago) by dashley
File MIME type: text/plain
File size: 17421 byte(s)
Rename ifsfscan to ets_ifsfscan.
1 dashley 150 //$Header$
2 dashley 212 //{9d0125ed-73c3-43f6-8435-8f161ab7ea6d}
3 dashley 150 //-------------------------------------------------------------------------------------------------
4 dashley 193 //Copyright (c) 2018, David T. Ashley
5     //
6 dashley 212 //This file is part of "ets_ifsfscan", a program for identifying gross formatting anomalies in
7     //source files (Windows/ASCII text files only).
8 dashley 193 //
9     //This source code and any program in which it is compiled/used is licensed under the MIT License,
10 dashley 150 //reproduced below.
11 dashley 193 //
12 dashley 150 //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 dashley 193 //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 dashley 150 //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 dashley 151 #include <stdlib.h>
32 dashley 193 #include <string.h>
33 dashley 197 #include <tchar.h>
34 dashley 193 #include <time.h>
35 dashley 197 #include <windows.h>
36 dashley 150
37 dashley 193 #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 dashley 151 {
44 dashley 212 "ets_ifsfscan, (c) 2018 David T. Ashley (dashley@gmail.com)",
45 dashley 193 "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 dashley 202 "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 dashley 151 };
70    
71 dashley 193 const char * const prog_desc_text[] =
72 dashley 151 {
73 dashley 212 "ets_ifsfscan (mnemonic: Ill-Formed Source File SCAN) is a program for",
74     "detecting gross formatting errors in source files (Windows/ASCII text",
75     "files only). The errors detected are non-ASCII characters, tab characters,",
76     "abnormal end-of-line characters, and trailing whitespace on lines."
77 dashley 151 };
78    
79 dashley 193 const char * const prog_help_text[] =
80 dashley 151 {
81 dashley 212 "Usage: ets_ifsfscan [filename_or_wildcard [filename_or_wildcard [...]]]",
82 dashley 202 "",
83     "Notes:",
84 dashley 193 " (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 dashley 202 " (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 dashley 193 " (5) : This program accepts only the ASCII character set (it will not",
92     " process UTF-8 or any other encodings).",
93 dashley 151 };
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 dashley 193 void CCMFATAL_fatal(const char *desc, const char *file, size_t line)
99 dashley 151 {
100 dashley 193 printf("\n");
101 dashley 202 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 dashley 151 }
106    
107     //--------------------------------------------------------------------------------
108     // A S S E R T I O N F U N C T I O N S
109     //--------------------------------------------------------------------------------
110 dashley 193 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 dashley 151
122    
123 dashley 193 //--------------------------------------------------------------------------------
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 dashley 151
136 dashley 193 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 dashley 151
146 dashley 193 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 dashley 197 "$HeadURL$",
175 dashley 195 "$Date$",
176 dashley 193 "$Revision$",
177 dashley 197 "$Author$",
178 dashley 212 "Project GUID: {beb3d945-d010-41c4-b7b5-7d4b84203408}",
179     "c_main.c GUID: {9d0125ed-73c3-43f6-8435-8f161ab7ea6d}",
180     "ets_ifsfscan.cpp GUID: {aa3777d0-4cd3-46e5-b84e-03f01957cc71}",
181 dashley 193 };
182    
183 dashley 197 if (which < (sizeof(vcinfo) / sizeof(vcinfo[0])))
184     return(vcinfo[which]);
185     else
186     return(NULL);
187 dashley 193 }
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 dashley 151 int FCMIOF_get_line_len(void)
193     {
194 dashley 202 return(FCMIOF_LINE_LEN);
195 dashley 151 }
196    
197     void FCMIOF_stream_repchar(FILE *s, char c, unsigned n)
198     {
199 dashley 193 USERASSERT_assert(s != NULL, __FILE__, __LINE__);
200 dashley 151
201 dashley 202 while (n--)
202     fprintf(s, "%c", c);
203 dashley 151 }
204    
205     void FCMIOF_repchar(char c, unsigned n)
206     {
207 dashley 202 while (n--)
208     printf("%c", c);
209 dashley 151 }
210    
211     void FCMIOF_hline(void)
212     {
213 dashley 202 FCMIOF_repchar(FCMIOF_HORIZONTAL_BAR_SEP_CHAR, FCMIOF_LINE_LEN);
214     printf("\n");
215 dashley 151 }
216    
217     void FCMIOF_stream_hline(FILE *s)
218     {
219 dashley 193 USERASSERT_assert(s != NULL, __FILE__, __LINE__);
220 dashley 151
221 dashley 202 FCMIOF_stream_repchar(s, FCMIOF_HORIZONTAL_BAR_SEP_CHAR, FCMIOF_LINE_LEN);
222     fprintf(s, "\n");
223 dashley 151 }
224    
225 dashley 197 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 dashley 199 size_t i, len;
268     char buffer[100];
269    
270 dashley 210 sprintf_s(buffer, sizeof(buffer)/sizeof(buffer[0]), "%llu", arg);
271 dashley 199 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 dashley 197 }
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 dashley 202
303 dashley 197 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 dashley 199 in_c = in_i & 0xff;
317     in_uc = in_i & 0xff;
318 dashley 197
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 dashley 199 //printf("Character: %02x State: %u\n", in_c, (unsigned)state);
329    
330 dashley 197 //Run through the state machine, which would look for bad EOL sequences.
331     switch (state)
332     {
333     case PST_LINE:
334 dashley 199 //Processing non-EOL characters.
335 dashley 197 if (is_lf(in_c))
336     {
337 dashley 199 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 dashley 197 //Line feeds not allowed without preceding carriage return.
344     emit_file_pos_3tuple(line_no, col_no, char_no);
345 dashley 199 printf(" Out of sequence line feed character (0x0a).\n");
346 dashley 197 line_no++;
347     col_no = 1;
348     state = PST_LF_FOUND;
349     }
350     else if (is_cr(in_c))
351     {
352 dashley 199 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 dashley 197 //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 dashley 199 printf(" Out of sequence carriage return character (0x0D).\n");
380 dashley 197 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 dashley 199 state = PST_CR_FOUND;
404 dashley 197 }
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 dashley 199 prev_c = in_c;
419     in_c = in_i & 0xff;
420     in_uc = in_i & 0xff;
421 dashley 197 char_no++;
422     if (in_i == EOF)
423 dashley 199 {
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 dashley 197 exit_flag = 1;
436 dashley 199 }
437 dashley 197 } 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 dashley 193 void process_filename_or_wildcard(const char *fname_or_wildcard)
464     {
465 dashley 197 HANDLE hFind;
466     WIN32_FIND_DATA FindFileData;
467    
468 dashley 193 //Incoming pointer is worthy of an assertion. The OS should not every deliver
469     //a NULL pointer or empty string to us.
470 dashley 197 USERASSERT_assert(fname_or_wildcard != NULL, __FILE__, __LINE__);
471 dashley 193 USERASSERT_assert(strlen(fname_or_wildcard) > 0, __FILE__, __LINE__);
472 dashley 151
473 dashley 197 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 dashley 193 }
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 dashley 197 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 dashley 193 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 dashley 150 int c_main(int argc, char **argv)
571     {
572 dashley 193 int i;
573    
574 dashley 150 if (argc <= 1)
575     {
576 dashley 193 //This is most likely someone trying to figure out what the program is or does.
577     //Spit everything.
578     emit_no_par_documentation();
579 dashley 150 }
580 dashley 193 else
581     {
582     emit_execution_preamble();
583 dashley 150
584 dashley 193 //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 dashley 150 return 0;
593 dashley 202 }

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