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