1 /* ml_count: given a list of ML files on the command line,
2    count the SLOC in each one.  SLOC = physical, non-comment lines.
3 
4 This is part of SLOCCount, a toolsuite that counts source lines of code (SLOC).
5 Copyright (C) 2001-2004 David A. Wheeler and Michal Moskal
6 
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 
21 To contact David A. Wheeler, see his website at:
22  http://www.dwheeler.com.
23 Michal Moskal may be contacted at malekith at pld-linux.org.
24 
25    Based on c_count.c by:
26    (C) Copyright 2000 David A. Wheeler
27    Michal Moskal rewrote sloc_count() function, to support ML.
28 
29    Usage: Use in one of the following ways:
30      ml_count                      # As filter
31      ml_count [-f file] [list_of_files]
32        file: file with a list of files to count (if "-", read list from stdin)
33        list_of_files: list of files to count
34 
35    Michal Moskal states "It was easier to get string escaping and comment
36    nesting right in C then in Perl. It would be even easier in OCaml... ;-)"
37 */
38 
39 #include <stdio.h>
40 #include <string.h>
41 #include <ctype.h>
42 #include <stdlib.h>
43 
44 /* Globals */
45 long total_sloc;
46 
peek(FILE * stream)47 int peek(FILE *stream) {
48  int c = getc(stream);
49  ungetc(c, stream);
50  return c;
51 }
52 
ispeek(int c,FILE * stream)53 int ispeek(int c, FILE *stream) {
54  if (c == peek(stream)) {return 1;}
55  return 0;
56 }
57 
58 long line_number;
59 
getachar(FILE * stream)60 int getachar(FILE *stream) {
61 /* Like getchar(), but keep track of line number. */
62  static int last_char_was_newline = 0;
63  int c;
64 
65  c = getc(stream);
66  if (last_char_was_newline) line_number++;
67  if (c == '\n') last_char_was_newline=1;
68  else           last_char_was_newline=0;
69  return c;
70 }
71 
72 
sloc_count(char * filename,FILE * stream)73 long sloc_count(char *filename, FILE *stream) {
74  /* Count the sloc in the program in stdin. */
75  long sloc = 0;
76 
77  int sawchar = 0;                /* Did you see a character on this line? */
78  int c;
79 
80  int comment_lev = 0;		/* Level of comment nesting. */
81  int in_string = 0;		/* 0 or 1 */
82 
83 
84  while ((c = getachar(stream)) != EOF) {
85    switch (c) {
86    case '"':
87      in_string = !in_string;
88      break;
89 
90    case '(':
91      if (!in_string && ispeek('*', stream)) {
92        comment_lev++;
93        getachar(stream);	/* skip '*' */
94      }
95      break;
96 
97    case '*':
98      if (comment_lev && !in_string && ispeek(')', stream)) {
99        comment_lev--;
100        getachar(stream);	/* skip ')' */
101        continue /* while */;
102      }
103      break;
104 
105    case '\\':
106      /* Ignore next character if in string.  But don't ignore newlines. */
107      if (in_string && !ispeek('\n', stream))
108        getachar(stream);
109      break;
110 
111    case ' ':
112    case '\t':
113      /* just ignore blanks */
114      continue /* while */;
115 
116    case '\n':
117      if (sawchar) {
118        sloc++;
119        sawchar = 0;
120      }
121      continue /* while */;
122 
123    default:
124      break;
125    }
126 
127    if (comment_lev == 0)
128      sawchar = 1;
129  }
130 
131  /* We're done with the file.  Handle EOF-without-EOL. */
132  if (sawchar) sloc++;
133 
134  if (comment_lev) {
135      fprintf(stderr, "ml_count ERROR - terminated in comment in %s\n", filename);
136  } else if (in_string) {
137      fprintf(stderr, "ml_count ERROR - terminated in string in %s\n", filename);
138  }
139 
140  return sloc;
141 }
142 
143 
count_file(char * filename)144 void count_file(char *filename) {
145   long sloc;
146   FILE *stream;
147 
148   stream = fopen(filename, "r");
149   line_number = 1;
150   sloc = sloc_count(filename, stream);
151   total_sloc += sloc;
152   printf("%ld %s\n", sloc, filename);
153   fclose(stream);
154 }
155 
read_a_line(FILE * file)156 char *read_a_line(FILE *file) {
157  /* Read a line in, and return a malloc'ed buffer with the line contents.
158     Any newline at the end is stripped.
159     If there's nothing left to read, returns NULL. */
160 
161  /* We'll create a monstrously long buffer to make life easy for us: */
162  char buffer[10000];
163  char *returnval;
164  char *newlinepos;
165 
166  returnval = fgets(buffer, sizeof(buffer), file);
167  if (returnval) {
168    newlinepos = buffer + strlen(buffer) - 1;
169    if (*newlinepos == '\n') {*newlinepos = '\0';};
170    return strdup(buffer);
171  } else {
172    return NULL;
173  }
174 }
175 
176 
main(int argc,char * argv[])177 int main(int argc, char *argv[]) {
178  long sloc;
179  int i;
180  FILE *file_list;
181  char *s;
182 
183  total_sloc = 0;
184  line_number = 1;
185 
186  if (argc <= 1) {
187    sloc = sloc_count("-", stdin);
188    printf("%ld %s\n", sloc, "-");
189    total_sloc += sloc;
190  } else if ((argc == 3) && (!strcmp(argv[1], "-f"))) {
191    if (!strcmp (argv[2], "-")) {
192      file_list = stdin;
193    } else {
194      file_list = fopen(argv[2], "r");
195    }
196    if (file_list) {
197      while ((s = read_a_line(file_list))) {
198        count_file(s);
199        free(s);
200      }
201    }
202  } else {
203    for (i=1; i < argc; i++) { count_file(argv[i]); }
204  }
205  printf("Total:\n");
206  printf("%ld\n", total_sloc);
207  return 0; /* Report success */
208 }
209 
210