"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 }