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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

projs/dtats/trunk/projs/20180112_ifsfscan/c_main.c revision 150 by dashley, Fri Jan 12 06:21:29 2018 UTC projs/dtats/trunk/projs/2018/20180718_ets_ifsfscan/c_main.c revision 212 by dashley, Thu Jul 19 02:25:24 2018 UTC
# Line 1  Line 1 
1  //$Header$  //$Header$
2    //{9d0125ed-73c3-43f6-8435-8f161ab7ea6d}
3  //-------------------------------------------------------------------------------------------------  //-------------------------------------------------------------------------------------------------
4  //This file is part of "ifsfscan", a program for identifying and correcting gross formatting  //Copyright (c) 2018, David T. Ashley
5  //anomalies in source files.  //
6  //-------------------------------------------------------------------------------------------------  //This file is part of "ets_ifsfscan", a program for identifying gross formatting anomalies in
7  //This source code and any program in which it is compiled/used is provided under the MIT License,  //source 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.  //reproduced below.
11  //-------------------------------------------------------------------------------------------------  //
12  //Permission is hereby granted, free of charge, to any person obtaining a copy of  //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  //this software and associated documentation files(the "Software"), to deal in the
14  //Software without restriction, including without limitation the rights to use,  //Software without restriction, including without limitation the rights to use,
# Line 18  Line 21 
21  //  //
22  //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23  //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE  //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  //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,  //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  //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28  //SOFTWARE.  //SOFTWARE.
29  //-------------------------------------------------------------------------------------------------  //-------------------------------------------------------------------------------------------------
30  #include <stdio.h>  #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       "ets_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       "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    };
78    
79    const char * const prog_help_text[] =
80    {
81       "Usage:  ets_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:           {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       };
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)  int c_main(int argc, char **argv)
571  {  {
572       int i;
573    
574     if (argc <= 1)     if (argc <= 1)
575     {     {
576        //This is most likely someone trying to figure out what the program is or does.        //This is  most likely someone trying to figure out what the program is or does.
577        //Treat this the same as a request for help.        //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;     return 0;
 }  
593    }

Legend:
Removed from v.150  
changed lines
  Added in v.212

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25