"Fossies" - the Fresh Open Source Software Archive 
Member "c_count-7.20/c_count.c" (16 Dec 2013, 38298 Bytes) of package /linux/privat/c_count-7.20.tgz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "c_count.c" see the
Fossies "Dox" file reference documentation.
1 /*
2 * Title: c_count.c
3 * Author: T.E.Dickey
4 * Created: 04 Dec 1985
5 * Modified:
6 * 15 Dec 2013, ensure that parse-state returns to "code" after a
7 * comment is completed. Extend parsing for numbers
8 * to improve error-checking.
9 * 14 Dec 2013, minor fix for Quoted(); if the first character of
10 * a quoted string was a blank, it was added to the
11 * blanks category rather than to code.
12 * 24 Aug 2013, ifdef to assume MinGW does globbing.
13 * 16 Jul 2010, fix strict compiler warnings, e.g., with const.
14 * 08 Jan 2006, correct bookkeeping for unterminated blocks.
15 * 17 Jul 2005, show statement numbers at the beginning of a
16 * statement with the -v option, rather than at
17 * the terminating ';'.
18 * 16 Jul 2005, modify treatment of -v option so it buffers data
19 * to allow reporting the current line, rather than
20 * the previous line. Originally a debugging option,
21 * it is useful in itself for making formatted
22 * listings.
23 * 26 Jun 2005, add -n option. Correct treatment of "#" in
24 * quotes (could be confused with preprocessor).
25 * Correct treatment of blanks in quoted string in
26 * preprocessor statement (was counted in statement).
27 * Modify check for SCCS tag to allow it to be past
28 * the beginning of a string. Improve parsing so
29 * numbers are not treated as tokens (for average
30 * length computation). Add parsing for \x escapes.
31 * 22 Nov 2002, Convert to ANSI C, indent'd. Fix a bug in -q/-d
32 * logic. Parse filename after #include as a string.
33 * 27 Feb 2001, expand wildcards on WIN32 with _setargv().
34 * Handle ^M^J line-endings for MSDOS, etc.
35 * 11 Jan 1999, add check for files w/o trailing newline.
36 * 02 Jul 1998, add -w option, to set threshold for too-long
37 * identifiers. Implement check for mismatched
38 * braces.
39 * 08 Oct 1997, integrate -b option better, showing block and
40 * level number if -v option is repeated.
41 * 04 Oct 1997, add top-level block and blocklevel counts.
42 * 25 Apr 1997, correct missing transition in comment-parsing
43 * state that caused incorrect line-count. Modify
44 * display of line/statement #'s to be more readable.
45 * 18 Dec 1996, allow '$' in tokens. Correct handling of C++
46 * comments (were always treated as inline because
47 * of incorrect state processing in inFile()).
48 * 13 May 1995, split-off from td_lib.
49 * 28 Jul 1994, show totals even for empty file.
50 * 17 Jul 1994, renamed from 'lincnt', for clearer meaning.
51 * 23 Sep 1993, gcc warnings
52 * 16 Oct 1991, header-label for spreadsheet had "STATEMENTS" and
53 * "LINES" interchanged (fixed). Also, converted to
54 * ANSI.
55 * 23 May 1991, apollo sr10.3 cpp complains about endif-tags
56 * 30 Aug 1990, history could be RCS or CMS (corrected message)
57 * 30 Aug 1990, added 'filter_history()' procedure, which filters
58 * out the history-comments generated by RCS or
59 * DEC/CMS from the total for "normal" comments. Only
60 * "normal" comments are shown in the comment:code
61 * ratio. If specific statistics are requested (e.g.,
62 * "-s") and the "-t" option is set, coerce "-p" as
63 * well.
64 * 29 Aug 1990, corrected format of 'show_flag()'
65 * 21 May 1990, corrected final (per-file) call on 'Summary()'
66 * 05 May 1990, rewrote, adding options to make this behave like
67 * "a.count".
68 * 14 May 1990, added "-t" (spreadsheet/table) option
69 * 10 May 1990, ported to VAX/VMS 5.3 (expand wildcards, added "-o"
70 * option). Made usage-message more verbose
71 * 17 Oct 1989, assume "//" begins C++ inline comments
72 * 05 Oct 1989, lint (apollo SR10 "string" defs)
73 * 21 Jul 1989, permit use of "-" to indicate standard input.
74 * 01 Jun 1988, added token-length statistic
75 * 01 Jul 1987, test for junky files (i.e., non-ascii characters,
76 * nested comments, non-graphic characters in
77 * quotes), added '-v' verbose mode to show running
78 * totals & status per-file. Added '-q' option to
79 * handle the case of unbalanced '"' in a macro
80 * (e.g., for a common printf-prefix).
81 * 07 Nov 1986, added tested for unbalanced quote, comment so we
82 * can get reasonable counts for ddn-driver, etc.
83 * Also fixed printf-formats for xenix-port.
84 * 23 Apr 1986, treat standard-input as a list of names, not code.
85 * 28 Jan 1986, make final 'exit()' with return code.
86 *
87 * Function: Count lines and statements in one or more C program files,
88 * giving statistics on percentage commented.
89 *
90 * The file(s) are specified as command line arguments. If no
91 * arguments are given, then the file is read from standard input.
92 *
93 * TODO: Make RCS-history filtering work with C++ comments.
94 *
95 * TODO: Make RCS-history filtering smart enough to handle blank lines
96 * in the history-comments (as opposed to blank lines between
97 * successive revisions).
98 */
99
100 #include "system.h"
101 #include "patchlev.h"
102
103 #ifndef NO_IDENT
104 static const char Id[] = "$Id: c_count.c,v 7.62 2013/12/16 01:36:13 tom Exp $";
105 #endif
106
107 #include <stdio.h>
108 #include <ctype.h>
109
110 #if HAVE_STDLIB_H
111 #include <stdlib.h>
112 #endif
113
114 #if HAVE_MALLOC_H
115 #include <malloc.h>
116 #endif
117
118 #if HAVE_STRING_H
119 #include <string.h>
120 #else
121 #include <strings.h>
122 #endif
123
124 #if HAVE_GETOPT_H
125 #include <getopt.h>
126 #else
127 # if !HAVE_GETOPT_HEADER
128 extern int getopt(int argc, char **argv, char *opts);
129 extern int optind;
130 extern char *optarg;
131 # endif
132 #endif
133
134 #if !HAVE_STRCHR /* normally in <string.h> */
135 #define strchr index
136 #endif
137
138 #define UCH(c) ((unsigned char)(c))
139 #ifndef isascii
140 #define isascii(c) (UCH(c) < 128)
141 #endif
142
143 #ifndef EXIT_SUCCESS /* normally in <stdlib.h> */
144 #define EXIT_SUCCESS 0
145 #define EXIT_FAILURE 0
146 #endif
147
148 #if defined(SYS_MSDOS) || defined(WIN32)
149 #define FOPEN_MODE "rb"
150 #else
151 #define FOPEN_MODE "r"
152 #endif
153
154 #define ESC_OCTAL 10 /* # of octal digits permissible in escape */
155 #define ESC_HEX 8 /* # of hex digits permissible in escape */
156
157 #ifndef isoctal
158 #define isoctal(c) ((c) >= '0' && (c) <= '7')
159 #endif
160
161 #define isCurly(c) ((c) == '{' || (c) == '}')
162
163 #define PRINTF (void)printf
164 #define DEBUG if (debug) PRINTF
165
166 #define TOKEN(c) ((c) == '_' || (c) == '$' || isalpha(c))
167 #define TOKEN2(c) (TOKEN(c) || isdigit(c))
168
169 #define NUMBER(c) (isdigit(c) || (c) == '.')
170 #define NUMBER2(c) (NUMBER(c) || strchr("+-eEpPlLuUxX", c) != 0)
171
172 #define LVL_WEIGHT(n) if (opt_blok) One.lvl_weights += (n) * (One.nesting_lvl+1)
173
174 #if defined(WIN32) && (defined(__MINGW32__) || defined(__MINGW64__))
175 int _dowildard = -1;
176 #endif
177
178 /*
179 * To make the verbose listing look nice (without doing tab-conversion), we
180 * want to have the columns for statements, lines, error flags add up to
181 * a multiple of 8.
182 */
183 #define DIGITS_LINES 6
184 #define DIGITS_STMTS 5
185 #define DIGITS_FLAGS ((DIGITS_LINES + 1 + DIGITS_STMTS + 8) % 8)
186
187 /* do not count curly-braces as part of statements, for numbering purposes */
188 #define CHARS_STMTS(p) ((p)->chars_code - (p)->chars_curly)
189
190 static int inFile(void);
191 static int Comment(int cpp);
192 static int Escape(void);
193 static int Quoted(int mark);
194
195 static FILE *File;
196 static char **quotvec;
197
198 static int literal; /* true when permitting literal-tab */
199
200 typedef struct {
201 /* character-counts */
202 long chars_total; /* # of characters */
203 long chars_blank;
204 long chars_code;
205 long chars_ignore;
206 long chars_notes;
207 long chars_rlogs;
208 long chars_prepro;
209 long chars_curly;
210 /* line-counts */
211 long lines_total; /* # of lines */
212 long lines_blank;
213 long lines_code;
214 long lines_inline; /* in-line comments */
215 long lines_notes; /* all other comments */
216 long lines_rlogs; /* RCS history comments */
217 long lines_prepro;
218 /* flags */
219 long flags_unquo; /* lines with unterminated quotes */
220 long flags_uncmt; /* unterminated/nested comments */
221 long flags_unasc; /* illegal characters found */
222 long flags_unlvl; /* unterminated blocks */
223 long flags_2long; /* too-long identifiers */
224 /* statement-counts */
225 long stmts_total;
226 long stmts_latch;
227 /* miscellaneous */
228 long nesting_lvl; /* {} nesting level */
229 long top_lvl_blk; /* top-level blocks */
230 long max_blk_lvl; /* maximum {} level */
231 long lvl_weights; /* count(code * (level+1)) */
232 long words_total; /* # of tokens */
233 long words_length; /* total length of tokens */
234 } STATS;
235
236 static STATS All, One; /* total, per-file stats */
237
238 static STATS Old; /* previous data for summarize() */
239
240 typedef enum {
241 tNone = 0,
242 tChar,
243 tNumber,
244 tToken
245 } TSTATE;
246 typedef enum {
247 pCode,
248 pComment,
249 pCppComment,
250 pPreprocessor
251 } PSTATE;
252 static PSTATE pstate;
253
254 #define IsComment(s) ((s) == pComment || (s) == pCppComment)
255
256 static int within_stmt; /* nonzero within statements */
257
258 static int verbose = FALSE; /* TRUE iff we echo file, as processed */
259 static int quotdef = 0; /* number of tokens we treat as '"' */
260 static int jargon = FALSE;
261 static int per_file = FALSE;
262 static int debug = FALSE;
263 static int opt_all = -1;
264 static int opt_blok = FALSE; /* "-b" option */
265 static int opt_line = FALSE; /* "-l" option */
266 static int opt_char = FALSE; /* "-c" option */
267 static int opt_name = FALSE; /* "-i" option */
268 static int opt_stat = FALSE; /* "-s" option */
269 static int opt_summary = TRUE; /* "-n" option */
270 static int spreadsheet = FALSE;
271 static int cms_history = FALSE;
272 static int files_total = 0;
273 static int limit_name = 32;
274 static int read_last;
275 static int wrote_last;
276 static int newsum; /* TRUE iff we need a summary */
277
278 /* buffer line for verbose-mode */
279 static char *big_line = 0;
280 static size_t big_used = 0;
281 static size_t big_size = 0;
282
283 static const char *comma = ",";
284 static const char *dashes = "----------------";
285
286 /************************************************************************
287 * local procedures *
288 ************************************************************************/
289 #if PRINT_ROUNDS_DOWN
290 /*
291 * When I compared output on Apollo SR10.1 with SunOS 4.1.1, I found that
292 * sometimes the SunOS printf would round down (e.g., 0.05% would be rendered
293 * as 0.0%). This code is used to round up so that I'd get the same numbers on
294 * different machines.
295 */
296 static double
297 RoundUp(double value, double parts)
298 {
299 long temp = value * parts * 10.0;
300 if ((temp % 10) == 5)
301 temp++;
302 return (temp / (parts * 10.0));
303 }
304 #else
305 #define RoundUp(value,parts) value
306 #endif
307
308 static void
309 new_summary(void)
310 {
311 if (!spreadsheet)
312 PRINTF("\n");
313 }
314
315 static void
316 per_cent(const char *text, long num, long den)
317 {
318 double value;
319 if (spreadsheet) {
320 PRINTF("%ld%s", num, comma);
321 return;
322 }
323 if (num == 0 || den == 0)
324 value = 0.0;
325 else
326 value = RoundUp(((double) num * 100.0) / (double) den, 10.0);
327 PRINTF("%6ld\t%-24s%6.1f %%\n", num, text, value);
328 }
329
330 static void
331 show_a_flag(const char *text, long flag)
332 {
333 if (spreadsheet)
334 PRINTF("%ld%s", flag, comma);
335 else if (flag)
336 PRINTF("%6ld\t%s\n", flag, text);
337 }
338
339 static void
340 ratio(const char *text, long num, long den)
341 {
342 if (den == 0)
343 den = 1;
344 if (spreadsheet) {
345 PRINTF("%.2f%s", (float) num / (float) den, comma);
346 return;
347 }
348 PRINTF("%6.2f\tratio of %s\n", (float) num / (float) den, text);
349 }
350
351 static void
352 summarize_lines(STATS * p)
353 {
354 long den = p->lines_total;
355
356 new_summary();
357 per_cent("lines had comments",
358 p->lines_notes + p->lines_inline, den);
359 if (p->lines_rlogs || spreadsheet)
360 per_cent("lines had history", p->lines_rlogs, den);
361 per_cent("comments are inline", p->lines_inline, -den);
362 per_cent("lines were blank", p->lines_blank, den);
363 per_cent("lines for preprocessor", p->lines_prepro, den);
364 per_cent("lines containing code", p->lines_code, den);
365 per_cent(jargon ?
366 "total lines (PSS)" :
367 "total lines",
368 p->lines_notes + p->lines_rlogs + p->lines_blank +
369 p->lines_prepro + p->lines_code,
370 den);
371 }
372
373 static void
374 summarize_chars(STATS * p)
375 {
376 long den = p->chars_total;
377
378 new_summary();
379 per_cent("comment-chars", p->chars_notes, den);
380 if (p->chars_rlogs || spreadsheet)
381 per_cent("history-chars", p->chars_rlogs, den);
382 per_cent("nontext-comment-chars", p->chars_ignore, den);
383 per_cent("whitespace-chars", p->chars_blank, den);
384 per_cent("preprocessor-chars", p->chars_prepro, den);
385 per_cent("statement-chars", p->chars_code, den);
386 per_cent("total characters",
387 p->chars_blank +
388 p->chars_notes + p->chars_rlogs + p->chars_ignore +
389 p->chars_prepro + p->chars_code,
390 den);
391 }
392
393 static void
394 summarize_names(STATS * p)
395 {
396 new_summary();
397 if (spreadsheet) {
398 PRINTF("%ld%s%ld%s",
399 p->words_total, comma,
400 p->words_length, comma);
401 return;
402 }
403 if (p->words_total)
404 PRINTF("%6ld\ttokens, average length %.2f\n",
405 p->words_total,
406 (1.0 * (double) p->words_length) / (double) p->words_total);
407 }
408
409 static void
410 summarize_stats(STATS * p)
411 {
412 new_summary();
413 ratio("comment:code", p->chars_notes, p->chars_prepro + p->chars_code);
414 show_a_flag("?:illegal characters found", p->flags_unasc);
415 show_a_flag("\":lines with unterminated quotes", p->flags_unquo);
416 show_a_flag("*:unterminated/nested comments", p->flags_uncmt);
417 show_a_flag("+:unterminated blocks", p->flags_unlvl);
418 show_a_flag(">:too-long identifiers", p->flags_2long);
419 }
420
421 static void
422 summarize_bloks(STATS * p)
423 {
424 new_summary();
425 if (spreadsheet) {
426 PRINTF("%ld%s%ld%s",
427 p->top_lvl_blk, comma,
428 p->max_blk_lvl, comma);
429 ratio("blocklevel:code", p->lvl_weights, p->chars_code);
430 return;
431 }
432 PRINTF("%6ld\ttop-level blocks/statements\n",
433 p->top_lvl_blk);
434 PRINTF("%6ld\tmaximum blocklevel\n",
435 p->max_blk_lvl + 1);
436 ratio("blocklevel:code", p->lvl_weights, p->chars_code);
437 }
438
439 static void
440 show_totals(STATS * p)
441 {
442 if (opt_line)
443 summarize_lines(p);
444 if (opt_char)
445 summarize_chars(p);
446 if (opt_name)
447 summarize_names(p);
448 if (opt_stat)
449 summarize_stats(p);
450 if (opt_blok)
451 summarize_bloks(p);
452 }
453
454 static void
455 summarize(STATS * p, int mark, int name)
456 {
457 char errors[80];
458 int c = 0;
459
460 newsum = FALSE;
461 if (spreadsheet) {
462 PRINTF("%ld%s%ld%s",
463 p->lines_total, comma,
464 p->stmts_total, comma);
465 } else {
466 int showed_stmts = FALSE;
467
468 /* show line- and statement-numbers */
469 PRINTF("%*ld ",
470 DIGITS_LINES, p->lines_total + (mark && !name));
471 if ((p->stmts_total != Old.stmts_total) || !mark || name) {
472 PRINTF("%*ld", DIGITS_STMTS, p->stmts_total);
473 showed_stmts = TRUE;
474 } else {
475 PRINTF("%*s", DIGITS_STMTS, " ");
476 }
477
478 /* show block-level */
479 if (verbose > 1 || (opt_blok && !opt_all)) {
480 if ((name || !mark)
481 || (mark && (p->top_lvl_blk != Old.top_lvl_blk)))
482 PRINTF(" %5ld ", p->top_lvl_blk);
483 else
484 PRINTF("%8s", " ");
485 if (name || !mark)
486 PRINTF(" %5ld ", p->max_blk_lvl + 1);
487 else if (mark && (p->nesting_lvl != Old.nesting_lvl))
488 PRINTF(" %5ld ", p->nesting_lvl + 1);
489 else
490 PRINTF("%8s", " ");
491 }
492
493 /* show single-character flags */
494 if (p->flags_unasc != Old.flags_unasc)
495 errors[c++] = '?';
496 if (p->flags_unquo != Old.flags_unquo)
497 errors[c++] = '"';
498 if (p->flags_uncmt != Old.flags_uncmt)
499 errors[c++] = '*';
500 if (p->flags_unlvl != 0)
501 errors[c++] = '+';
502 if (p->flags_2long != Old.flags_2long)
503 errors[c++] = '>';
504
505 while (c < DIGITS_FLAGS - 2) {
506 errors[c++] = ' ';
507 }
508 /* If there is room, mark the last flag showing if the line contained
509 * code or a preprocessor-line. Normally this shows continuation
510 * lines.
511 */
512 if (!showed_stmts && CHARS_STMTS(p) != CHARS_STMTS(&Old)) {
513 errors[c++] = '.';
514 } else {
515 errors[c++] = ' ';
516 }
517
518 if (c > DIGITS_FLAGS)
519 c = DIGITS_FLAGS;
520 errors[c] = EOS;
521 PRINTF("%s%c",
522 errors,
523 (mark ? '|' : ' '));
524 }
525 Old = *p;
526 }
527
528 static void
529 Summary(int mark)
530 {
531 if (newsum)
532 summarize(&One, mark, FALSE);
533 }
534
535 #define ADD(m) All.m += One.m; One.m = 0
536
537 static void
538 add_totals(void)
539 {
540 ADD(chars_total);
541 ADD(chars_blank);
542 ADD(chars_code);
543 ADD(chars_ignore);
544 ADD(chars_notes);
545 ADD(chars_rlogs);
546 ADD(chars_prepro);
547 ADD(chars_curly);
548
549 ADD(lines_total);
550 ADD(lines_blank);
551 ADD(lines_code);
552 ADD(lines_inline);
553 ADD(lines_notes);
554 ADD(lines_rlogs);
555 ADD(lines_prepro);
556
557 ADD(flags_unquo);
558 ADD(flags_uncmt);
559 ADD(flags_unasc);
560 ADD(flags_unlvl);
561 ADD(flags_2long);
562
563 ADD(stmts_total);
564
565 ADD(words_total);
566 ADD(words_length);
567
568 ADD(top_lvl_blk);
569 ADD(lvl_weights);
570
571 if (All.max_blk_lvl < One.max_blk_lvl)
572 All.max_blk_lvl = One.max_blk_lvl;
573 One.max_blk_lvl = 0;
574
575 if (One.nesting_lvl > 0)
576 All.flags_unlvl += One.nesting_lvl;
577 One.nesting_lvl = 0;
578
579 files_total++;
580 }
581
582 /*
583 * Treat an included-filename as a string.
584 */
585 static int
586 IncludeFile(int c)
587 {
588 while (c == ' ' || c == '\t')
589 c = inFile();
590 if (c == '"')
591 c = Quoted(c);
592 else if (c == '<')
593 c = Quoted('>');
594 return c;
595 }
596
597 static int
598 guessToken(int c)
599 {
600 int result;
601 if (TOKEN(c)) {
602 result = tToken;
603 } else if (NUMBER(c)) {
604 result = tNumber;
605 } else {
606 result = tChar;
607 }
608 return result;
609 }
610
611 /*
612 * If '-q' option is in effect, append to the token-buffer until a token is
613 * complete. Test the completed token to see if it matches any of the strings
614 * we have equated to '"'. If so, process a string from that point. Note that
615 * there are two special cases which fortuitously work out:
616 * (a) in the "#define" statement, the whitespace-character immediately
617 * after the token is ignored.
618 * (b) in the usage of the token, the whitespace-character following the
619 * token is ignored.
620 * To provide for the special case of one define providing an alternate name
621 * for another, we do the lookup/string processing only if a quote-macro could
622 * be expanded there, e.g., if it is followed by a space or tab.
623 */
624 static int
625 Token(int c)
626 {
627 static char *bfr;
628 static size_t used = 0;
629 static size_t have = 0;
630 static TSTATE tstate = tNone;
631 static char exponents[] = "eE";
632 static char suffixes[] = "uUlL";
633
634 PSTATE bstate = pstate;
635 int j = 0;
636
637 if (bfr == 0)
638 bfr = malloc(have = 80);
639
640 if (tstate == tNone) {
641 tstate = guessToken(c);
642 }
643
644 switch (tstate) {
645 case tToken:
646 {
647 int word_length = 0;
648 One.words_total++;
649 do {
650 if (used + 2 >= have)
651 bfr = realloc(bfr, have *= 2);
652 bfr[used++] = (char) c;
653 word_length++;
654 c = inFile();
655 } while (TOKEN2(c));
656 bfr[used] = EOS;
657
658 DEBUG("name\t%s\n", bfr);
659
660 One.words_length += word_length;
661 if (word_length >= limit_name)
662 One.flags_2long++;
663
664 if (pstate == pPreprocessor && !strcmp(bfr, "include")) {
665 c = IncludeFile(c);
666 } else if (quotdef) {
667 if (c == ' ' || c == '\t' || c == '(') {
668 for (j = 0; j < quotdef; j++) {
669 if (!strcmp(quotvec[j], bfr)) {
670 c = Quoted('"');
671 DEBUG("**%c**\n", c);
672 break;
673 }
674 }
675 }
676 }
677 }
678 break;
679 case tNumber:
680 {
681 int digits = 0;
682 int lead_0 = 0;
683 int baseis = 0;
684 int gotdot = 0;
685 int gotexp = 0;
686 int gotend = 0;
687
688 used = 0;
689 if (isdigit(c)) {
690 ++digits;
691 } else if (c == '.') {
692 gotdot = 1;
693 }
694 if (c == '0') {
695 lead_0 = 1;
696 }
697
698 for (;;) {
699 if (used + 4 >= have)
700 bfr = realloc(bfr, have = (2 * (used + 2)));
701 bfr[used++] = (char) c;
702 c = inFile();
703 if (c <= 0) {
704 break;
705 } else if (gotend) {
706 if ((strchr) (suffixes, c) == 0) {
707 break;
708 }
709 } else if (gotdot || (baseis == 0 && digits && !isdigit(c)
710 && (strchr) (exponents, c) != 0)) {
711 if (gotexp == 1) {
712 if (c == '+' || c == '-' || isdigit(c)) {
713 gotexp = 2;
714 } else {
715 break;
716 }
717 } else if (gotexp == 2) {
718 if ((strchr) (suffixes, c)) {
719 gotexp = 3;
720 } else if (!isdigit(c)) {
721 break;
722 }
723 } else if (gotexp == 3) {
724 if ((strchr) (suffixes, c) == 0) {
725 break;
726 }
727 } else if ((strchr) (exponents, c) != 0) {
728 gotdot = 1;
729 gotexp = 1;
730 } else if (isdigit(c)) {
731 ++digits;
732 } else {
733 break;
734 }
735 } else if (c == '.') {
736 gotdot = 1;
737 } else if (lead_0 && used == 1) {
738 if (c == 'x' || c == 'X') {
739 baseis = 16;
740 } else if (isoctal(c)) {
741 baseis = 8;
742 } else {
743 break;
744 }
745 } else if ((strchr) (suffixes, c) != 0) {
746 if (digits) {
747 gotend = 1;
748 } else {
749 break;
750 }
751 } else if (baseis == 16 && isxdigit(c)) {
752 ++digits;
753 } else if (baseis == 8 && isoctal(c)) {
754 ++digits;
755 } else if (isdigit(c)) {
756 ++digits;
757 } else {
758 break;
759 }
760 }
761 bfr[used] = EOS;
762
763 DEBUG("number\t%s\n", bfr);
764 }
765 break;
766 case tNone:
767 /* FALLTHRU */
768 case tChar: /* punctuation */
769 if (debug) {
770 if (isgraph(c) && strchr("{};/\\", c) == 0)
771 DEBUG("char\t%c\n", c);
772 }
773 c = inFile();
774 break;
775 }
776 used = 0;
777
778 if ((c == '\n') && pstate == pCode && bstate != pCode)
779 DEBUG("\n");
780
781 #ifdef NO_LEAKS2
782 if (bfr != 0) {
783 free(bfr);
784 bfr = 0;
785 have = 0;
786 }
787 #endif
788 tstate = tNone;
789 return (c);
790 }
791
792 /*
793 * Count things related to the given character (-1 resets us to the beginning).
794 */
795 static void
796 countChar(int ch)
797 {
798 static int is_blank; /* true til we get nonblank on line */
799 static int had_note;
800 static int had_code;
801 static int cnt_code;
802 static PSTATE bstate;
803
804 if (ch < 0) {
805 within_stmt = 0;
806 bstate = pCode;
807 memset(&Old, 0, sizeof(Old));
808 had_note =
809 had_code = FALSE;
810 cnt_code = 0;
811 read_last = EOS;
812 is_blank = TRUE;
813 } else {
814 newsum = TRUE;
815 if (!isascii(ch) || (!isprint(ch) && !isspace(ch))) {
816 ch = '?'; /* protect/flag this */
817 One.flags_unasc++;
818 }
819
820 /* If requested, show the file. But avoid showing a
821 * carriage return unless it is embedded in the line.
822 */
823 if (verbose) {
824 if (big_used + 4 >= big_size) {
825 big_line = realloc(big_line, big_size *= 2);
826 if (big_line == 0) {
827 perror("realloc");
828 exit(EXIT_FAILURE);
829 }
830 }
831 if (wrote_last == '\r' && ch != '\n') {
832 big_line[big_used - 1] = '^';
833 big_line[big_used++] = 'M';
834 }
835 big_line[big_used++] = (char) ch;
836 big_line[big_used] = EOS;
837 if (ch == '\n') {
838 Summary(TRUE);
839 fputs(big_line, stdout);
840 big_line[big_used = 0] = EOS;
841 }
842 }
843 wrote_last = ch;
844
845 One.chars_total++;
846
847 if (ch == '#' && is_blank && !literal) {
848 bstate = pPreprocessor;
849 pstate = pPreprocessor;
850 } else if (ch == '\n') {
851 if (cnt_code) {
852 had_code += !IsComment(pstate);
853 if (IsComment(pstate))
854 had_note = TRUE;
855 cnt_code = 0;
856 }
857 One.lines_total++;
858 if (is_blank) {
859 One.lines_blank++;
860 } else {
861 if (pstate == pCppComment
862 && bstate == pPreprocessor) {
863 One.lines_prepro++;
864 One.lines_inline++;
865 pstate = pPreprocessor;
866 } else if (pstate == pPreprocessor
867 || bstate == pPreprocessor) {
868 One.lines_prepro++;
869 if (had_note) {
870 One.lines_inline++;
871 }
872 } else if (had_code) {
873 One.lines_code++;
874 if (had_note) {
875 One.lines_inline++;
876 }
877 } else if (had_note || IsComment(pstate)) {
878 One.lines_notes++;
879 }
880 had_code =
881 had_note = FALSE;
882 }
883 is_blank = TRUE;
884 if (pstate == pPreprocessor && read_last != '\\') {
885 bstate = pCode;
886 pstate = pCode;
887 }
888 }
889
890 if (isspace(ch)) {
891 if (cnt_code) {
892 had_code += (pstate == pCode);
893 cnt_code = 0;
894 }
895 if (literal) {
896 if (pstate == pCode) {
897 One.chars_code++;
898 LVL_WEIGHT(1);
899 } else {
900 One.chars_prepro++;
901 }
902 } else {
903 One.chars_blank++;
904 }
905 } else {
906 is_blank = FALSE;
907 switch (pstate) {
908 case pComment:
909 /* FALLTHRU */
910 case pCppComment:
911 if (cnt_code) {
912 had_code += (cnt_code > 2);
913 cnt_code = 0;
914 }
915 had_note = TRUE;
916 if (isalnum(ch))
917 One.chars_notes++;
918 else
919 One.chars_ignore++;
920 break;
921 case pPreprocessor:
922 One.chars_prepro++;
923 break;
924 case pCode:
925 if (!isspace(ch)) {
926 if (within_stmt) {
927 within_stmt++;
928 } else if (!isCurly(ch) && !literal) {
929 within_stmt = TRUE;
930 One.stmts_total++;
931 }
932 }
933 cnt_code++;
934 One.chars_code++;
935 LVL_WEIGHT(1);
936 break;
937 }
938 }
939 read_last = ch;
940 }
941 }
942
943 /*
944 * Process a single file:
945 */
946 static void
947 doFile(char *name)
948 {
949 int c;
950 int topblock = FALSE;
951
952 #if !SYS_UNIX && !defined(WIN32)
953 if (has_wildcard(name)) { /* expand wildcards? */
954 char expanded[BUFSIZ];
955 int count = 0;
956 (void) strcpy(expanded, name);
957 while (expand_wildcard(expanded, !count++))
958 doFile(expanded);
959 return;
960 }
961 /* trim trailing blanks */
962 for (c = strlen(name); c > 0; c--) {
963 if (isspace(name[--c]))
964 name[c] = EOS;
965 else
966 break;
967 }
968 #endif
969
970 pstate = pCode;
971 if (!strcmp(name, "-"))
972 File = stdin;
973 else if (!(File = fopen(name, FOPEN_MODE)))
974 return;
975
976 newsum = TRUE;
977 cms_history = FALSE;
978 wrote_last = -1;
979 c = inFile();
980
981 while (c != EOF) {
982 switch (c) {
983 case '/':
984 c = Token(EOS);
985 if (c == '*') {
986 c = Comment(FALSE);
987 } else if (c == '/') {
988 c = Comment(TRUE);
989 pstate = pCode;
990 } else {
991 DEBUG("char\t%c\n", c);
992 }
993 break;
994 case '"':
995 case '\'':
996 c = Quoted(c);
997 break;
998 case '{':
999 DEBUG("char\t%c\n", c);
1000 if (pstate == pCode) {
1001 One.chars_curly++;
1002 One.nesting_lvl++;
1003 if (One.nesting_lvl >
1004 One.max_blk_lvl)
1005 One.max_blk_lvl = One.nesting_lvl;
1006 }
1007 c = Token(c);
1008 break;
1009 case '}':
1010 DEBUG("char\t%c\n", c);
1011 if (pstate == pCode) {
1012 One.chars_curly++;
1013 One.nesting_lvl--;
1014 if (One.nesting_lvl == 0) {
1015 topblock = TRUE;
1016 } else if (One.nesting_lvl < 0) {
1017 One.flags_unlvl += One.nesting_lvl;
1018 One.nesting_lvl = 0;
1019 }
1020 }
1021 c = Token(c);
1022 break;
1023 case ';':
1024 DEBUG("char\t%c\n", c);
1025 within_stmt = 0;
1026 if (pstate == pCode) {
1027 if (One.nesting_lvl == 0) {
1028 One.top_lvl_blk++;
1029 }
1030 topblock = FALSE;
1031 }
1032 c = Token(c);
1033 break;
1034 case ',':
1035 DEBUG("char\t%c\n", c);
1036 topblock = FALSE;
1037 c = Token(c);
1038 break;
1039 default:
1040 if (pstate == pCode) {
1041 if (One.nesting_lvl == 0
1042 && topblock) {
1043 One.top_lvl_blk++;
1044 }
1045 topblock = FALSE;
1046 }
1047 c = Token(c);
1048 break;
1049 }
1050 }
1051 (void) Token(EOS);
1052 (void) fclose(File);
1053
1054 if (wrote_last != '\n') {
1055 (void) countChar('\n'); /* fake a newline */
1056 One.chars_total--;
1057 One.chars_blank--;
1058 }
1059
1060 Old.stmts_total = 0; /* force # of statements to display */
1061
1062 if (per_file && spreadsheet) {
1063 show_totals(&One);
1064 } else if (!per_file) {
1065 summarize(&One, TRUE, TRUE);
1066 }
1067 PRINTF("%s\n", name);
1068 if (per_file && !spreadsheet) {
1069 show_totals(&One);
1070 PRINTF("\n");
1071 }
1072 add_totals();
1073 }
1074
1075 /*
1076 * Scan over a quoted string. Except for the special (automatic) case of
1077 * "@(#)"-sccs strings, flag all nonprinting characters which are found in
1078 * the string in 'unasc'. Also, flag places where we have a newline in a
1079 * string (perhaps because the leading quote was in a macro!).
1080 */
1081 static int
1082 Quoted(int mark)
1083 {
1084 static const char *sccs_tag = "@(#)";
1085 const char *p = sccs_tag; /* permit literal tab here only! */
1086 int c;
1087
1088 DEBUG("string\t%c", (mark == '>') ? '<' : mark);
1089 literal = TRUE;
1090 c = inFile();
1091 while (c != EOF) {
1092 if (c == '\n') { /* this is legal, but not likely */
1093 One.flags_unquo++; /* ...assume balance is in macro */
1094 break;
1095 } else {
1096 if (!isprint(c)) {
1097 if (c != '\t' || *p != '\0')
1098 One.flags_unasc++;
1099 }
1100 if (*p != '\0') {
1101 if (c != *p++)
1102 p = sccs_tag;
1103 }
1104 if (c == mark) {
1105 DEBUG("%c", c);
1106 literal = FALSE;
1107 c = inFile();
1108 break;
1109 } else if (c == '\\') {
1110 c = Escape();
1111 } else {
1112 DEBUG("%c", c);
1113 c = inFile();
1114 }
1115 }
1116 }
1117 literal = FALSE;
1118 DEBUG("\n");
1119 return (c);
1120 }
1121
1122 /*
1123 * Scan over an '\' escape sequence, returning the first character after it.
1124 * If we start with an octal digit, we may read up to ESC_OCTAL of these in a
1125 * row.
1126 */
1127 static int
1128 Escape(void)
1129 {
1130 int c = inFile();
1131 int digits = 0;
1132
1133 if (c != EOF) {
1134 if (isoctal(c)) {
1135 while (isoctal(c) && digits < ESC_OCTAL) {
1136 digits++;
1137 if ((c = inFile()) == EOF)
1138 return (c);
1139 }
1140 } else if (c == 'x') {
1141 c = inFile();
1142 while (isxdigit(c) && digits < ESC_HEX) {
1143 digits++;
1144 if ((c = inFile()) == EOF)
1145 return (c);
1146 }
1147 }
1148 if (!digits)
1149 c = inFile();
1150 }
1151 return (c);
1152 }
1153
1154 /*
1155 * Identify comments which we disregard because they were generated by the
1156 * RCS history mechanism.
1157 */
1158 static void
1159 Disregard(char *lo, char *hi)
1160 {
1161 while (lo <= hi) {
1162 if (isalnum(UCH(*lo))) {
1163 One.chars_notes -= 1;
1164 One.chars_rlogs += 1;
1165 }
1166 lo++;
1167 }
1168 One.lines_notes -= 1;
1169 One.lines_rlogs += 1;
1170 }
1171
1172 /*
1173 * Compare strings ignoring blanks (TD_LIB)
1174 */
1175 #define SKIP(p) while (isspace(UCH(*p))) p++;
1176
1177 static int
1178 strbcmp(char *a, char *b)
1179 {
1180 int cmp;
1181
1182 while (*a && *b) {
1183 if (isspace(UCH(*a)) && isspace(UCH(*b))) {
1184 SKIP(a);
1185 SKIP(b);
1186 } else if ((cmp = (*a++ - *b++)) != EOS)
1187 return (cmp);
1188 }
1189 SKIP(a);
1190 SKIP(b);
1191 return (*a - *b);
1192 }
1193
1194 /*
1195 * Read characters within a comment, keeping track to find RCS or DEC/CMS
1196 * history comments.
1197 *
1198 * RCS history comments consist of an RCS identifier "Log", followed by zero
1199 * or more groups of lines beginning with the keyword "Revision". Each line in
1200 * the group is prefixed by the same string (the RCS "comment" prefix). The
1201 * groups are separated by a line containing only the comment-prefix.
1202 *
1203 * DEC/CMS history comments consist of one or more comment lines between
1204 * comments containing the string shown below. These comments (due to the
1205 * nature of CMS's history substitution) must be self-contained on a line,
1206 * unlike the RCS history.
1207 */
1208 static int
1209 filter_history(int first)
1210 {
1211 typedef enum {
1212 unknown,
1213 cms,
1214 rlog,
1215 revision,
1216 contents
1217 } HSTATE;
1218 static const char *CMS_ = "DEC/CMS REPLACEMENT HISTORY,";
1219 static HSTATE hstate = unknown;
1220 static char buffer[BUFSIZ];
1221 static char prefix[BUFSIZ];
1222 static size_t len;
1223
1224 char *s, *d, *t;
1225
1226 int c = inFile();
1227
1228 if (first) {
1229 buffer[len = 0] = EOS;
1230 prefix[0] = EOS;
1231 hstate = cms_history ? cms : unknown;
1232 } else if (len < sizeof(buffer)) {
1233 buffer[len++] = (char) c;
1234 buffer[len] = EOS;
1235 }
1236 if (c == '\n') {
1237 /* try to find CMS bracketing comment */
1238 len = strlen(CMS_);
1239 for (s = buffer; *s; s++) {
1240 if (!strncmp(s, CMS_, len)) {
1241 cms_history = !cms_history;
1242 hstate = cms;
1243 break;
1244 }
1245 }
1246
1247 /* ignore all comments within DEC/CMS bracketing */
1248 if (hstate == cms) {
1249 Disregard(buffer, buffer + strlen(buffer));
1250
1251 /* try to find RCS identifier "Log" */
1252 } else if (hstate == unknown) {
1253 s = buffer;
1254 while ((d = strchr(s, '$')) != NULL) {
1255 s = d + 1;
1256 if (!strncmp(d, "$Log", (size_t) 4)
1257 && (t = strchr(s, '$')) != 0) {
1258 hstate = rlog;
1259 Disregard(d, t);
1260 break;
1261 }
1262 }
1263 /* try to find "Revision" after comment-prefix */
1264 } else if (hstate == rlog) {
1265 s = buffer;
1266 while ((d = strchr(s, 'R')) != NULL) {
1267 s = d + 1;
1268 if (!strncmp(d, "Revision", (size_t) 8)) {
1269 size_t len2 = (size_t) (d - buffer);
1270 strcpy(prefix, buffer)[len2] = EOS;
1271 hstate = revision;
1272 s += strlen(s);
1273 Disregard(d, s - 2);
1274 }
1275 }
1276 } else if (hstate == revision
1277 || hstate == contents) {
1278 if (!strncmp(buffer, prefix, (size_t) strlen(prefix))) {
1279 d = buffer + strlen(prefix);
1280 s = d + strlen(d);
1281 hstate = (*d == '\n') ? rlog : contents;
1282 Disregard(d, s - 2);
1283 } else if (!strbcmp(buffer, prefix)) {
1284 hstate = rlog; /* assume user trimmed spaces */
1285 } else
1286 hstate = unknown;
1287 }
1288 buffer[len = 0] = EOS;
1289 }
1290 return (c);
1291 }
1292
1293 /*
1294 * Entered immediately after reading '/','*', scan over a comment, returning
1295 * the first character after the comment. Flags both unterminated comments
1296 * and nested comments with 'uncmt'.
1297 */
1298 static int
1299 Comment(int c_plus_plus)
1300 {
1301 int c;
1302 int d = 0;
1303 PSTATE save_st = pstate;
1304
1305 if (within_stmt == 2) {
1306 One.stmts_total--;
1307 within_stmt = 0;
1308 }
1309
1310 if (pstate == pCode) {
1311 One.chars_code -= 2;
1312 LVL_WEIGHT(-2);
1313 } else {
1314 One.chars_prepro -= 2;
1315 }
1316 One.chars_ignore += 2; /* ignore the comment-delimiter */
1317
1318 pstate = (c_plus_plus
1319 ? pCppComment
1320 : pComment);
1321 c = filter_history(TRUE);
1322 while (c != EOF) {
1323 if (c_plus_plus) {
1324 if (c == '\n') {
1325 if (save_st == pPreprocessor)
1326 DEBUG("\n");
1327 pstate = save_st;
1328 return (c);
1329 }
1330 c = filter_history(FALSE);
1331 } else {
1332 if (c == '*') {
1333 c = filter_history(FALSE);
1334 if (c == '/') {
1335 pstate = save_st;
1336 c = inFile();
1337 if (c == '\n' && save_st == pPreprocessor)
1338 DEBUG("\n");
1339 return c;
1340 }
1341 } else {
1342 c = filter_history(FALSE);
1343 if (c == '*' && d == '/')
1344 One.flags_uncmt++;
1345 }
1346 }
1347 d = c;
1348 }
1349 One.flags_uncmt++;
1350 return (c); /* Unterminated comment! */
1351 }
1352
1353 /*
1354 * Return the next character from the current file, using the global file
1355 * pointer.
1356 */
1357 static int
1358 inFile(void)
1359 {
1360 int c = fgetc(File);
1361
1362 if (feof(File) || ferror(File)) {
1363 c = EOF;
1364 if (read_last != '\n')
1365 One.flags_unasc++;
1366 } else {
1367 c &= 0xff; /* protect against sign-extension bug */
1368 }
1369 if (One.chars_total == 0) {
1370 countChar(-1);
1371 }
1372
1373 if (c != EOF) {
1374 countChar(c);
1375 }
1376 return (c);
1377 }
1378
1379 #define OPTIONS "\
1380 b\
1381 c\
1382 d\
1383 i\
1384 j\
1385 l\
1386 n\
1387 o:\
1388 p\
1389 q:\
1390 s\
1391 t\
1392 V\
1393 v\
1394 w:\
1395 "
1396
1397 static void
1398 usage(void)
1399 {
1400 static const char *tbl[] =
1401 {
1402 "Usage: c_count [options] [files]"
1403 ,""
1404 ,"If no files are specified as arguments, a list of filenames is read from the"
1405 ,"standard input. The special name \"-\" denotes a file which is read from the"
1406 ,"standard input."
1407 ,""
1408 ,"Options:"
1409 ," -b block-statistics"
1410 ," -c character-statistics"
1411 ," -d debug (shows tokens as they are parsed)"
1412 ," -i identifier-statistics"
1413 ," -j annotate summary in technical format"
1414 ," -l line-statistics"
1415 ," -n do not print summary-statistics"
1416 ," -o file specify alternative output-file"
1417 ," -p per-file statistics"
1418 ," -q DEFINE tells c_count that the given name is an unbalanced quote"
1419 ," -s specialized statistics"
1420 ," -t generate output for spreadsheet"
1421 ," -V print the version"
1422 ," -v verbose (shows lines as they are counted)"
1423 ," -w LEN set threshold for too-long identifiers (default 32)"
1424 };
1425 unsigned j;
1426 for (j = 0; j < sizeof(tbl) / sizeof(tbl[0]); j++)
1427 (void) fprintf(stderr, "%s\n", tbl[j]);
1428 (void) exit(EXIT_FAILURE);
1429 }
1430
1431 /************************************************************************
1432 * main procedure *
1433 ************************************************************************/
1434
1435 int
1436 main(int argc, char **argv)
1437 {
1438 int j;
1439 char name[BUFSIZ];
1440
1441 #if defined(WIN32) && !(defined(__MINGW32__) || defined(__MINGW64__))
1442 _setargv(&argc, &argv);
1443 #endif
1444 quotvec = typeCalloc(char *, (size_t) argc);
1445 while ((j = getopt(argc, argv, OPTIONS)) != EOF) {
1446 switch (j) {
1447 case 'b':
1448 opt_all = FALSE;
1449 opt_blok = TRUE;
1450 break;
1451 case 'c':
1452 opt_all = FALSE;
1453 opt_char = TRUE;
1454 break;
1455 case 'd':
1456 debug = TRUE;
1457 break;
1458 case 'i':
1459 opt_all = FALSE;
1460 opt_name = TRUE;
1461 break;
1462 case 'j':
1463 jargon = TRUE;
1464 break;
1465 case 'l':
1466 opt_all = FALSE;
1467 opt_line = TRUE;
1468 break;
1469 case 'n':
1470 opt_summary = FALSE;
1471 break;
1472 case 'o':
1473 if (!freopen(optarg, "w", stdout))
1474 usage();
1475 break;
1476 case 'p':
1477 per_file = TRUE;
1478 break;
1479 case 'q':
1480 quotvec[quotdef++] = optarg;
1481 break;
1482 case 's':
1483 opt_all = FALSE;
1484 opt_stat = TRUE;
1485 break;
1486 case 't':
1487 spreadsheet = TRUE;
1488 break;
1489 case 'V':
1490 PRINTF("c_count version %d.%d\n", RELEASE, PATCHLEVEL);
1491 return EXIT_SUCCESS;
1492 case 'v':
1493 verbose++;
1494 if (big_line == 0)
1495 big_line = malloc(big_size = 1024);
1496 break;
1497 case 'w':
1498 limit_name = atoi(optarg);
1499 break;
1500 default:
1501 usage();
1502 }
1503 }
1504
1505 if (opt_all == -1)
1506 opt_blok = opt_line = opt_char = opt_name = opt_stat = TRUE;
1507 else if (spreadsheet)
1508 per_file = TRUE;
1509
1510 if (spreadsheet) {
1511 if (per_file) {
1512 if (opt_line)
1513 PRINTF("%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
1514 "L-COMMENT", comma,
1515 "L-HISTORY", comma,
1516 "L-INLINE", comma,
1517 "L-BLANK", comma,
1518 "L-CPP", comma,
1519 "L-CODE", comma,
1520 "L-TOTAL", comma);
1521 if (opt_char)
1522 PRINTF("%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
1523 "C-COMMENT", comma,
1524 "C-HISTORY", comma,
1525 "C-IGNORE", comma,
1526 "C-BLANK", comma,
1527 "C-CPP", comma,
1528 "C-CODE", comma,
1529 "C-TOTAL", comma);
1530 if (opt_name)
1531 PRINTF("%s%s%s%s",
1532 "W-TOTAL", comma,
1533 "W-LENGTH", comma);
1534 if (opt_stat)
1535 PRINTF("%s%s%s%s%s%s%s%s%s%s%s%s",
1536 "CODE:COMMENT", comma,
1537 "ILLEGAL-CHARS", comma,
1538 "ILLEGAL-QUOTES", comma,
1539 "ILLEGAL-COMMENTS", comma,
1540 "ILLEGAL-BLOCKS", comma,
1541 "ILLEGAL-NAMES", comma);
1542 if (opt_blok)
1543 PRINTF("%s%s%s%s%s%s",
1544 "TOP-BLOCKS", comma,
1545 "MAX-NESTING", comma,
1546 "AVG-NESTING", comma);
1547
1548 } else {
1549 PRINTF("LINES%sSTATEMENTS%s", comma, comma);
1550 }
1551 PRINTF("FILENAME\n");
1552 }
1553
1554 if (optind < argc) {
1555 for (j = optind; j < argc; j++)
1556 doFile(argv[j]);
1557 } else {
1558 while (fgets(name, (int) sizeof(name) - 1, stdin)) {
1559 size_t len = strlen(name);
1560 if (len != 0 && name[--len] == '\n')
1561 name[len] = EOS;
1562 doFile(name);
1563 }
1564 }
1565
1566 if (!spreadsheet && files_total && opt_summary) {
1567 if (verbose > 1 || (opt_blok && !opt_all))
1568 PRINTF("%s", dashes);
1569 if (!per_file) {
1570 PRINTF("%s\n", dashes);
1571 summarize(&All, FALSE, FALSE);
1572 PRINTF("%s",
1573 jargon ?
1574 "physical source statements/logical source statements" :
1575 "total lines/statements");
1576 if (opt_blok && !opt_all)
1577 PRINTF(", top-level blocks, maximum nesting");
1578 PRINTF("\n");
1579 } else {
1580 PRINTF("Grand total\n");
1581 PRINTF("%s\n", dashes);
1582 }
1583 show_totals(&All);
1584 PRINTF("\n");
1585 }
1586 #ifdef NO_LEAKS
1587 if (big_line != 0)
1588 free(big_line);
1589 free(quotvec);
1590 #endif
1591
1592 return EXIT_SUCCESS;
1593 }