/[dtapublic]/projs/emts/trunk/src/emts_standalone/emts_ifsfscan/c_main.c
ViewVC logotype

Annotation of /projs/emts/trunk/src/emts_standalone/emts_ifsfscan/c_main.c

Parent Directory Parent Directory | Revision Log Revision Log


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