[comp.sources.wanted] Need Non-commentary Sources Line Counter

dpb@sunCv.tellabs.com (Darryl Baker) (11/21/90)

	I need a program, shell script, awk program, etc... that
	will count lines of C source code that are not exclusively
	comments. I thought I saw a couple of these go by but I looked
	at the indexes for comp.sources.misc and comp.sources.unix and
	couldn't find one in there. Does anyone have one? Can you point
	one out in one of the archived groups? Thanks.
-- 
   __                      _      __
  /  )                    //     /  )       /
 /  / __.  __  __  __  , //     /--<  __.  /_  _  __    Darryl Baker
/__/_(_/|_/ (_/ (_/ (_/_</_    /___/_(_/|_/ <_</_/ (_   dpb@TELLABS.COM

kent@sparky.IMD.Sterling.COM (Kent Landfield) (11/26/90)

In article <4603@tellab5.tellabs.com> Darryl writes:
>
>	I need a program, shell script, awk program, etc... that
>	will count lines of C source code that are not exclusively
>	comments. I thought I saw a couple of these go by but I looked
>	at the indexes for comp.sources.misc and comp.sources.unix and
>	couldn't find one in there. Does anyone have one? Can you point
>	one out in one of the archived groups? Thanks.
>-- 
>   __                      _      __
>  /  )                    //     /  )       /
> /  / __.  __  __  __  , //     /--<  __.  /_  _  __    Darryl Baker
>/__/_(_/|_/ (_/ (_/ (_/_</_    /___/_(_/|_/ <_</_/ (_   dpb@TELLABS.COM


This might be what you saw/want/need... :-)

---
Kent Landfield                       INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD               UUCP:     uunet!sparky!kent
1404 Ft. Crook Rd. South             Phone:    (402) 291-8300 
Bellevue, NE. 68005-2969             FAX:      (402) 291-4362
	
-------------------- cut,save,make,use ------------------
#ifndef lint
static char sccsid[] = "@(#)comments.c	1.4 11/23/90";
#endif
/*
**                                                                
**  Subsystem:   Source Configuration Management Tools
**  File Name:   comments.c               
**                                                        
** This software is Copyright (c) 1988, 1989, 1990 by Kent Landfield.
**
** Permission is hereby granted to copy, distribute or otherwise 
** use any part of this package as long as you do not try to make 
** money from it or pretend that you wrote it.  This copyright 
** notice must be maintained in any copy made.
**
** Use of this software constitutes acceptance for use in an AS IS 
** condition. There are NO warranties with regard to this software.  
** In no event shall the author be liable for any damages whatsoever 
** arising out of or in connection with the use or performance of this 
** software.  Any use of this software is at the user's own risk.
**
**  If you make modifications to this software that you feel 
**  increases it usefulness for the rest of the community, please 
**  email the changes, enhancements, bug fixes as well as any and 
**  all ideas to me. This software is going to be maintained and 
**  enhanced as deemed necessary by the community.
**		
**		Kent Landfield
**		kent@sparky.IMD.Sterling.COM
**
**  History:
**	Creation: Tue Feb 21 08:52:35 CST 1989 due to necessity.
**	
**
**	  NAME
**	       comments	- extract comment and non-blank	lines from C source
**	       files
**
**	  SYNOPSIS
**	       comments	[-c  [-v] [-tfilename] ] [-j] [-{Rrs}] file
**
**	  DESCRIPTION
**	       Comments is a general purpose tool for dealing with
**	       comment lines in C files.  comments also identifies
**	       Embedded comment and Delimiter mismatch lines while
**	       displaying comment lines of a file.
**
**	       The output from comments	goes to	standard out.
**
**	       If no options are used the comment lines	are output as they
**	       appear in the file or taken from	standard in.
**
**	       The meanings of the options are:
**
**	       -c   Produce code/comment/total statistics. The information
**		    is displayed in 2 rows.  The first row contains the
**		    number of characters of code, comment, and total.  The
**		    second row contains	the number of lines with code, com-
**		    ments, and total.  The second row also includes the
**		    name of the	file being processed.
**
**		    Special characters (ie. form and line feeds) are
**		    included only in total counts therefore the	number of
**		    characters of code plus comments do	not equal the total
**		    number of characters.  This	prevents blank lines from
**		    affecting the counts.
**
**		    A line with	both code and a	comment	counts as one code
**		    line and one comment line.	Therefore the number of
**		    lines with code plus lines with comment may	be greater
**		    than the total lines.
**
**	       -j   Left justify the comments printed.
**
**	       -R   Strip comments removing all	blank lines.
**
**	       -r   Strip comments removing multiple blank lines.
**
**	       -s   Strip comments leaving all blank lines.
**
**	       -tfilename
**		    The	filename to display with -c if taken from standard
**		    input.  This option	must be	used in	conjunction with
**		    the	-c option for it to be active.
**
**	       -v   Print out the header for count option.  Three columns
**		    from the -c	option are labeled:
**
**			  Code	   Comment    Total
**
**		    This option	must be	used in	conjunction with the -c
**		    option for it to be	active.
**
**	       file File to be processed if not	from standard input.
**
*/

#include <stdio.h>
#include <ctype.h>

#define EMBED_ERROR(a) (void) printf("Embeded comment,line %ld\n",a);
#define DELIM_ERROR(a) (void) printf("Delimiter mismatch, line %ld\n",a);

char *progname;             /* executable name */
char *flname;               /* input file name */

int strip = 0;              /* strip comments flag                   */
int remove_multiple = 0;    /* strip and remove multiple blank lines */
int remove_all = 0;         /* strip and remove all blank lines      */
int countit = 0;            /* count code/comment/total flag         */
int verbose = 0;            /* verbose output flag for header lines  */
int justify = 0;            /* left justify comments flag            */

main(argc, argv)
int argc;
char **argv;
{
   register int i;
   FILE *fp;

   flname = "STDIN";       /* assume input as standard input   */
   progname = argv[0];     /* save the program executable name */

   while (argc > 1 && argv[1][0] == '-') {
      switch (argv[1][1]) {
      case 'R':    /* strip comments removing all blank lines */
         remove_all = 1;
         strip = 1;
         countit = 0;
         break;
      case 'r':    /* strip comments removing multiple blank lines */
         remove_multiple = 1;     
      case 's':    /* strip comments leaving all blank lines */
         strip = 1;
         countit = 0;
         break;
      case 'c':    /* produce code/comment/total statistics */
         strip = 0;
         countit = 1;
         break;
      case 'v':    /* print out the header for count function */
         verbose = 1;
         break;
      case 'j':    /* left justify the comments printed */
         justify = 1;
         break;
      case 't':    /* the filename if taken from standard input */
         flname = argv[2];
         argc--, argv++;
         break;
      default:
         (void) fprintf(stderr,"%s: unknown arg %s\n",progname,argv[1]);
         return(1);
      }
      argc--, argv++;
   }
   if (argc == 1)             /* input coming from standard input */
      comments(stdin);
   else {
      for (i = 1; i < argc; i++) {
         flname = argv[i];
         if ((fp = fopen(flname, "r")) == NULL) {
            (void) fprintf(stderr, "%s: can't open %s\n",
                argv[0], flname);
            return(1);

         }
         else {
            comments(fp);
            (void) fclose(fp);
         }
      }
   }
   return(0);
}

comments(stream)
FILE *stream;
{
   register char *bp;            /* current character pointer             */
   register long lineno = 0L;    /* current line counter                  */
   char buf[BUFSIZ];             /* input buffer, contains record read    */
   int end_comment = 0;          /* boolean indicating comment just ended */
   int print_line = 0;           /* boolean indicating line is to print   */
   int incomment = 0;            /* boolean indicating if in a comment    */
   int inliteral = 0;            /* boolean indicating if in a literal    */
   int last_char = ' ';          /* holds the last item bp pointed at     */

   /*
   ** Determine if the strip or count function is requested to be 
   ** applied to the stream. If so, call the appropriate function
   ** and return to main.  
   */

   if (strip) {
      strip_comments(stream);
      return;
   }
   else if (countit) {
      count(stream);
      return;
   }

   /*
   ** Comments only will be printed.
   */

   while (fgets(buf, sizeof(buf), stream) != NULL) {
      bp = buf;
      lineno++;

      for (; *bp != '\n'; bp++) {
         if (*bp == '/' && !inliteral) {
             if (*(bp+1) == '*') {     /* begin comment */
                if (!incomment)        /* am I already in one */
                   incomment = 1;      /* no - indicate i am now */
                else                   /* yes - print an error */
                   EMBED_ERROR(lineno);
             }
             else if (last_char == '*') { /* end comment */
                if (incomment) {       /* I should be in a comment */
                   incomment = 0;      /* turn off comment flag    */
                   end_comment = 1;    /* indicate turnoff         */
                   print_line = 1;     /* indicate print needed.   */
                }
                else                   /* not in a comment - error */
                   DELIM_ERROR(lineno);
             }
         }
         else if (*bp == '"' && !incomment &&       /* turn on and  */
            (last_char != '\'' && *(bp+1) != '\'')) /*   off the    */
            inliteral = !inliteral;                 /* literal flag */

         if (!incomment && !end_comment) 
             *bp = ' ';                /* blank out non-comments    */
         last_char = *bp;              /* store the last char value */
         end_comment = 0;
       }

       /* newline found, loop terminated */

       if (incomment || print_line) {  /* printable comment line    */
           bp = buf;                   /* point at start of record  */
           if (justify) {              /* if justify, strip leading */
              while(*bp == ' ' || *bp == '\t') /* tabs and spaces   */
                  bp++;
           }
           (void) fputs(bp, stdout);   /* print the comment line    */
           print_line = 0;             /* turn off the print flag   */
       }
    }
   return;
}

strip_comments(stream)
FILE *stream;
{
   register char *bp;            /* current character pointer             */
   register long lineno = 0L;    /* current line counter                  */
   char buf[BUFSIZ];             /* input buffer, contains record read    */
   int incomment = 0;            /* boolean indicating if in a comment    */
   int inliteral = 0;            /* boolean indicating if in a literal    */
   int data_inline = 0;          /* boolean indicating if data in line    */
   int last_line_blank = 0;      /* boolean indicating if last line blank */
   int last_char = ' ';          /* holds the last item bp pointed at     */

   /*
   ** Strip comments from the stream and print according the
   ** the options set by the user.
   */

   while (fgets(buf, sizeof(buf), stream) != NULL) {
      bp = buf;
      lineno++;

      for (; *bp != '\n'; bp++) {
         if (*bp == '/' && !inliteral) {
             if (*(bp+1) == '*') {     /* begin comment              */
                if (!incomment)        /* am I already in one        */
                   incomment = 1;      /* no - indicate i am now     */
                else                   /* yes - print an error       */
                   EMBED_ERROR(lineno);
             }
             else if (last_char == '*') {  /* end comment            */
                if (incomment) {       /* I should be in a comment   */
                   incomment = 0;      /* turn off comment flag      */
                   *bp = ' ';          /* blank out the comment      */
                }
                else                   /* not in a comment - error   */
                   DELIM_ERROR(lineno);
             }
         }
         else if (*bp == '"' && !incomment &&        /* turn on and  */
            (last_char != '\'' && *(bp+1) != '\''))  /*   off the    */
            inliteral = !inliteral;                  /* literal flag */

         last_char = *bp;                /* store last char value    */
         if (incomment)
             *bp = ' ';                  /* blank out the comment    */
       }

       /* newline found, loop terminated */

       if (!incomment) {                /* time to check for data    */
          bp = buf;
          data_inline = 0;
          while (*bp && !data_inline) { /* determine if a blank line */
            if (*bp != '\t' && *bp != ' ' && *bp != '\n' && *bp != '\f')
                data_inline = 1;
             bp++;
          }
          if (data_inline) {             /* was real data found ?        */
             (void) fputs(buf, stdout);  /*   print out the line         */
             last_line_blank = 0;        /* indicate last line not blank */
          }
          else if (!remove_all) {        /* did user want blank lines ?  */
              if (!remove_multiple)      /* squash multiple blank lines? */
                 (void) fputs(buf, stdout);  /* no print the line        */
              else  if (!last_line_blank) {  /* yes - was last blank ?   */
                 (void) fputs(buf, stdout);  /*     print the line       */
                 last_line_blank = 1;        /* indicate last line blank */
              }
          }
       }
    }
}

count(stream) 
    FILE *stream;
{
   register char *bp;            /* current character pointer             */
   register long code_chars = 0L; /* number of characters of code         */
   register long code_lines = 0L; /* number of lines of code              */
   register long comment_chars = 0L; /* number of characters of comments  */
   register long comment_lines = 0L; /* # of lines containing comments    */
   register long chars = 0L;     /* total number of characters            */
   register long lineno = 0L;    /* total number of lines                 */
   char buf[BUFSIZ];             /* input buffer, contains record read    */
   int end_comment = 0;          /* boolean indicating comment just ended */
   int incomment = 0;            /* boolean indicating if in a comment    */
   int inliteral = 0;            /* boolean indicating if in a literal    */
   int had_comment = 0;          /* boolean indicating line had a comment */
   int had_data = 0;             /* boolean indicating line had data on it*/
   int last_char = ' ';          /* holds the last item bp pointed at     */

    while (fgets(buf, sizeof(buf), stream) != NULL) {
        lineno++;
        for (bp = buf; *bp != '\n'; bp++) {
             chars++;

             /* check for valid data on line */

             if (*bp != '/' && *bp != '*' && !incomment &&
                 *bp != '\t' && *bp != ' ' && *bp != '\f')
                had_data = 1;             /* set data on line flag */

             /* encountered a possible comment delimiter */

             if (*bp == '/' && !inliteral && 
                (*(bp+1) == '*' || last_char == '*')) { 
                incomment = !incomment;
                if (!incomment) {
                   had_comment = 1;    /* indicate comment on line */
                   end_comment = 1;    /* indicate comment stopped */
                   comment_chars++;    /* count last '/'           */
                }
             }

             /* encountered a possible literal delimiter */

             else if (*bp == '"' && !incomment && 
                (last_char != '\'' && *(bp+1) != '\''))
                inliteral = !inliteral;

             last_char = *bp;

             if (incomment)
                 comment_chars++;         
             else if (!end_comment)
                 code_chars++;
             end_comment = 0;
        }
  
        /* newline found - loop terminated */
        chars++;          /* add the newline character to the total */
#ifdef COUNT_NEWLINES
        if (incomment)
            comment_chars++;         
        else
            code_chars++;
#endif

        if (incomment || had_comment)
            comment_lines++;

        if (had_data)
            code_lines++;

        had_comment = 0;
        had_data = 0;
    }
    if (verbose)
       (void) printf(" Code     Comment   Total\n");

    (void) printf("%5ld%10ld%10ld\n%5ld%10ld%10ld      %s\n",
                 code_chars, comment_chars, chars,
                 code_lines, comment_lines, lineno, flname);
}
/* this is the last line of the file */