remove whitespace from end of lines
[reactos.git] / reactos / apps / utils / stats / stats.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS project statistics
4 * FILE: stats.c
5 * PURPOSE: Main program file
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * REVISIONS:
8 * CSH 01/01-2002 Created
9 */
10 #include <windows.h>
11 #include <tchar.h>
12 #include <stdio.h>
13
14 typedef struct _EXTENSION_INFO
15 {
16 struct _EXTENSION_INFO * Next;
17 struct _FILE_INFO * StatInfoList;
18 TCHAR ExtName[16];
19 TCHAR ExpandedExtName[128];
20 DWORD nExtensions;
21 TCHAR Description[256];
22 DWORD FileCount;
23 DWORD LineCount;
24 DWORD EmptyLines;
25 } EXTENSION_INFO, *PEXTENSION_INFO;
26
27 typedef struct _FILE_INFO
28 {
29 struct _FILE_INFO * Next;
30 struct _FILE_INFO * StatInfoListNext;
31 PEXTENSION_INFO ExtInfo;
32 TCHAR FileName[MAX_PATH];
33 DWORD LineCount;
34 DWORD EmptyLines;
35 DWORD FunctionCount;
36 } FILE_INFO, *PFILE_INFO;
37
38 HANDLE FileHandle;
39 PEXTENSION_INFO ExtInfoList;
40 PFILE_INFO StatInfoList;
41 BOOLEAN SkipEmptyLines, BeSilent;
42
43 #define MAX_OPTIONS 2
44 TCHAR *Options[MAX_OPTIONS];
45
46
47 VOID
48 Initialize(VOID)
49 {
50 ExtInfoList = NULL;
51 StatInfoList = NULL;
52 }
53
54
55 VOID
56 Cleanup(VOID)
57 {
58 PEXTENSION_INFO ExtInfo;
59 PEXTENSION_INFO NextExtInfo;
60
61 ExtInfo = ExtInfoList;
62 while (ExtInfo != NULL)
63 {
64 NextExtInfo = ExtInfo->Next;
65 HeapFree (GetProcessHeap(), 0, ExtInfo);
66 ExtInfo = NextExtInfo;
67 }
68 }
69
70
71 PEXTENSION_INFO
72 AddExtension(LPTSTR ExtName,
73 LPTSTR Description)
74 {
75 PEXTENSION_INFO ExtInfo;
76 PEXTENSION_INFO Info;
77 TCHAR *t;
78 DWORD ln;
79
80 ExtInfo = (PEXTENSION_INFO) HeapAlloc (GetProcessHeap(), 0, sizeof (EXTENSION_INFO));
81 if (!ExtInfo)
82 return NULL;
83
84 for(t = ExtName; *t != _T('\0'); t += _tcslen(t) + 1);
85 ln = (DWORD)t - (DWORD)ExtName;
86
87 ZeroMemory (ExtInfo, sizeof (EXTENSION_INFO));
88 memcpy (ExtInfo->ExtName, ExtName, ln);
89 _tcscpy (ExtInfo->Description, Description);
90
91 for(t = ExtInfo->ExtName; *t != _T('\0'); t += _tcslen(t) + 1)
92 {
93 if(ExtInfo->nExtensions++ != 0)
94 _tcscat (ExtInfo->ExpandedExtName, _T(";"));
95 _tcscat (ExtInfo->ExpandedExtName, _T("*."));
96 _tcscat (ExtInfo->ExpandedExtName, t);
97 }
98
99 if (ExtInfoList)
100 {
101 Info = ExtInfoList;
102 while (Info->Next != NULL)
103 {
104 Info = Info->Next;
105 }
106 Info->Next = ExtInfo;
107 }
108 else
109 {
110 ExtInfoList = ExtInfo;
111 }
112
113 return ExtInfo;
114 }
115
116
117 PFILE_INFO
118 AddFile(LPTSTR FileName,
119 PEXTENSION_INFO ExtInfo)
120 {
121 PFILE_INFO StatInfo;
122 PFILE_INFO Info;
123
124 StatInfo = (PFILE_INFO) HeapAlloc (GetProcessHeap(), 0, sizeof (FILE_INFO));
125 if (!StatInfo)
126 return NULL;
127 ZeroMemory (StatInfo, sizeof (FILE_INFO));
128 _tcscpy (StatInfo->FileName, FileName);
129 StatInfo->ExtInfo = ExtInfo;
130
131 if (ExtInfo->StatInfoList)
132 {
133 Info = ExtInfo->StatInfoList;
134 while (Info->StatInfoListNext != NULL)
135 {
136 Info = Info->StatInfoListNext;
137 }
138 Info->StatInfoListNext = StatInfo;
139 }
140 else
141 {
142 ExtInfo->StatInfoList = StatInfo;
143 }
144
145 if (StatInfoList)
146 {
147 Info = StatInfoList;
148 while (Info->Next != NULL)
149 {
150 Info = Info->Next;
151 }
152 Info->Next = StatInfo;
153 }
154 else
155 {
156 StatInfoList = StatInfo;
157 }
158
159 return StatInfo;
160 }
161
162
163 VOID
164 CleanupAfterFile(VOID)
165 {
166 if(FileHandle != INVALID_HANDLE_VALUE)
167 CloseHandle (FileHandle);
168 }
169
170
171 BOOL
172 LoadFile(LPTSTR FileName)
173 {
174 LONG FileSize;
175
176 FileHandle = CreateFile (FileName, // Create this file
177 GENERIC_READ, // Open for reading
178 0, // No sharing
179 NULL, // No security
180 OPEN_EXISTING, // Open the file
181 FILE_ATTRIBUTE_NORMAL, // Normal file
182 NULL); // No attribute template
183 if (FileHandle == INVALID_HANDLE_VALUE)
184 return FALSE;
185
186 FileSize = GetFileSize (FileHandle, NULL);
187 if (FileSize <= 0)
188 {
189 CloseHandle (FileHandle);
190 return FALSE;
191 }
192
193 return TRUE;
194 }
195
196
197 VOID
198 ReadLines(PFILE_INFO StatInfo)
199 {
200 DWORD ReadBytes, LineLen;
201 static char FileBuffer[1024];
202 char LastChar = '\0';
203 char *Current;
204
205 LineLen = 0;
206 while(ReadFile (FileHandle, FileBuffer, sizeof(FileBuffer), &ReadBytes, NULL) && ReadBytes >= sizeof(char))
207 {
208 for(Current = FileBuffer; ReadBytes > 0; ReadBytes -= sizeof(char), Current++)
209 {
210 if(*Current == '\n' && LastChar == '\r')
211 {
212 LastChar = '\0';
213 if(!SkipEmptyLines || (LineLen > 0))
214 StatInfo->LineCount++;
215 if(LineLen == 0)
216 StatInfo->EmptyLines++;
217 LineLen = 0;
218 continue;
219 }
220 LastChar = *Current;
221 if(SkipEmptyLines && (*Current == ' ' || *Current == '\t'))
222 {
223 continue;
224 }
225 if(*Current != '\r')
226 LineLen++;
227 }
228 }
229
230 StatInfo->LineCount += (LineLen > 0);
231 StatInfo->EmptyLines += ((LastChar != '\0') && (LineLen == 0));
232 }
233
234
235 VOID
236 PrintStatistics(VOID)
237 {
238 PEXTENSION_INFO Info;
239 DWORD TotalFileCount;
240 DWORD TotalLineCount;
241 DWORD TotalAvgLF;
242 DWORD TotalEmptyLines;
243
244 TotalFileCount = 0;
245 TotalLineCount = 0;
246 TotalEmptyLines = 0;
247 Info = ExtInfoList;
248
249 for (Info = ExtInfoList; Info != NULL; Info = Info->Next)
250 {
251 TotalFileCount += Info->FileCount;
252 TotalLineCount += Info->LineCount;
253 TotalEmptyLines += Info->EmptyLines;
254 }
255
256 TotalAvgLF = (TotalFileCount ? TotalLineCount / TotalFileCount : 0);
257
258 for (Info = ExtInfoList; Info != NULL; Info = Info->Next)
259 {
260 DWORD AvgLF;
261
262 if (Info->FileCount != 0)
263 {
264 AvgLF = (Info->FileCount ? Info->LineCount / Info->FileCount : 0);
265 }
266 else
267 {
268 AvgLF = 0;
269 }
270
271 _tprintf (_T("\n"));
272 _tprintf (_T("File extension%c : %s\n"), ((Info->nExtensions > 1) ? _T('s') : _T(' ')), Info->ExpandedExtName);
273 _tprintf (_T("File ext. description : %s\n"), Info->Description);
274 _tprintf (_T("Number of files : %lu\n"), Info->FileCount);
275 _tprintf (_T("Number of lines : %lu\n"), Info->LineCount);
276 if(SkipEmptyLines)
277 _tprintf (_T("Number of empty lines : %lu\n"), Info->EmptyLines);
278 _tprintf (_T("Proportion of lines : %.2f %%\n"), (float)(TotalLineCount ? (((float)Info->LineCount * 100) / (float)TotalLineCount) : 0));
279 _tprintf (_T("Average no. lines/file : %lu\n"), AvgLF);
280 }
281
282 _tprintf (_T("\n"));
283 _tprintf (_T("Total number of files : %lu\n"), TotalFileCount);
284 _tprintf (_T("Total number of lines : %lu\n"), TotalLineCount);
285 if(SkipEmptyLines)
286 _tprintf (_T("Total number of empty lines : %lu\n"), TotalEmptyLines);
287 _tprintf (_T("Average no. lines/file : %lu\n"), TotalAvgLF);
288 }
289
290
291 BOOL
292 ProcessFiles(LPTSTR Path)
293 {
294 WIN32_FIND_DATA FindFile;
295 PEXTENSION_INFO Info;
296 TCHAR SearchPath[256];
297 TCHAR FileName[256];
298 TCHAR *Ext;
299 HANDLE SearchHandle;
300 BOOL More;
301
302 Info = ExtInfoList;
303 while (Info != NULL)
304 {
305 Ext = Info->ExtName;
306 do
307 {
308 ZeroMemory (&FindFile, sizeof (FindFile));
309 _tcscpy (SearchPath, Path);
310 _tcscat (SearchPath, _T("\\*."));
311 _tcscat (SearchPath, Ext);
312 _tcscpy (FindFile.cFileName, SearchPath);
313 SearchHandle = FindFirstFile (SearchPath, &FindFile);
314 if (SearchHandle != INVALID_HANDLE_VALUE)
315 {
316 More = TRUE;
317 while (More)
318 {
319 if (!(FindFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
320 {
321 _tcscpy (FileName, Path);
322 _tcscat (FileName, _T("\\"));
323 _tcscat (FileName, FindFile.cFileName);
324
325 if (LoadFile (FileName))
326 {
327 PFILE_INFO StatInfo;
328
329 StatInfo = AddFile (FindFile.cFileName, Info);
330 if (!StatInfo)
331 {
332 _tprintf (_T("Not enough free memory.\n"));
333 return FALSE;
334 }
335
336 ReadLines (StatInfo);
337
338 Info->FileCount++;
339 Info->LineCount += StatInfo->LineCount;
340 Info->EmptyLines += StatInfo->EmptyLines;
341
342 CleanupAfterFile();
343 }
344 }
345 More = FindNextFile (SearchHandle, &FindFile);
346 }
347 FindClose (SearchHandle);
348 }
349 Ext += _tcslen(Ext) + 1;
350 } while(*Ext != _T('\0'));
351 Info = Info->Next;
352 }
353 return TRUE;
354 }
355
356
357 BOOL
358 ProcessDirectories(LPTSTR Path)
359 {
360 WIN32_FIND_DATA FindFile;
361 TCHAR SearchPath[MAX_PATH];
362 HANDLE SearchHandle;
363 BOOL More;
364
365 if(!BeSilent)
366 {
367 _tprintf (_T("Processing %s ...\n"), Path);
368 }
369
370 _tcscpy (SearchPath, Path);
371 _tcscat (SearchPath, _T("\\*.*"));
372
373 SearchHandle = FindFirstFileEx (SearchPath,
374 FindExInfoStandard,
375 &FindFile,
376 FindExSearchLimitToDirectories,
377 NULL,
378 0);
379 if (SearchHandle != INVALID_HANDLE_VALUE)
380 {
381 More = TRUE;
382 while (More)
383 {
384 if ((FindFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
385 && (_tcscmp (FindFile.cFileName, _T(".")) != 0)
386 && (_tcscmp (FindFile.cFileName, _T("..")) != 0)
387 && (_tcscmp (FindFile.cFileName, _T("CVS")) != 0))
388 {
389 _tcscpy (SearchPath, Path);
390 _tcscat (SearchPath, _T("\\"));
391 _tcscat (SearchPath, FindFile.cFileName);
392 if (!ProcessDirectories (SearchPath))
393 return FALSE;
394 if (!ProcessFiles (SearchPath))
395 return FALSE;
396 }
397 More = FindNextFile (SearchHandle, &FindFile);
398 }
399 FindClose (SearchHandle);
400 }
401 return TRUE;
402 }
403
404
405 VOID
406 Execute(LPTSTR Path)
407 {
408 if (!ExtInfoList)
409 {
410 _tprintf (_T("No extensions specified.\n"));
411 return;
412 }
413
414 if (!ProcessDirectories (Path))
415 {
416 _tprintf (_T("Failed to process directories.\n"));
417 return;
418 }
419
420 if (!ProcessFiles (Path))
421 {
422 _tprintf (_T("Failed to process files.\n"));
423 return;
424 }
425
426 PrintStatistics();
427 }
428
429 BOOLEAN
430 IsOptionSet(TCHAR *Option)
431 {
432 int i;
433 for(i = 0; i < MAX_OPTIONS; i++)
434 {
435 if(!Options[i])
436 continue;
437
438 if(!_tcscmp(Options[i], Option))
439 return TRUE;
440 }
441 return FALSE;
442 }
443
444
445 int
446 main (int argc, char * argv [])
447 {
448 int a;
449 #if UNICODE
450 TCHAR Path[MAX_PATH + 1];
451 #endif
452
453 _tprintf (_T("ReactOS Project Statistics\n"));
454 _tprintf (_T("==========================\n\n"));
455
456 if (argc < 2 || argc > 2 + MAX_OPTIONS)
457 {
458 _tprintf(_T("Usage: stats [-e] [-s] directory\n"));
459 _tprintf(_T(" -e: don't count empty lines\n"));
460 _tprintf(_T(" -s: be silent, don't print directories while processing\n"));
461 return 1;
462 }
463
464 Initialize();
465 AddExtension (_T("c\0\0"), _T("Ansi C Source files"));
466 AddExtension (_T("cpp\0cxx\0\0"), _T("C++ Source files"));
467 AddExtension (_T("h\0\0"), _T("Header files"));
468
469 for(a = 1; a < argc - 1; a++)
470 {
471 #if UNICODE
472 int len = lstrlenA(argv[a]);
473 TCHAR *str = (TCHAR*)HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
474 if(MultiByteToWideChar(CP_ACP, 0, argv[a], -1, str, len + 1) > 0)
475 Options[a - 1] = str;
476 else
477 Options[a - 1] = NULL;
478 #else
479 Options[a - 1] = argv[a];
480 #endif
481 }
482
483 SkipEmptyLines = IsOptionSet(_T("-e"));
484 BeSilent = IsOptionSet(_T("-s"));
485
486 #if UNICODE
487 ZeroMemory(Path, sizeof(Path));
488 if(MultiByteToWideChar(CP_ACP, 0, argv[argc - 1], -1, Path, MAX_PATH) > 0)
489 Execute (Path);
490 #else
491 Execute (argv[argc - 1]);
492 #endif
493 Cleanup();
494
495 return 0;
496 }