reactos/modules/rosapps/applications/sysutils/utils/stats/stats.c
2018-07-08 10:59:16 +02:00

478 lines
10 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS project statistics
* FILE: stats.c
* PURPOSE: Main program file
* PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
* REVISIONS:
* CSH 01/01-2002 Created
*/
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
typedef struct _EXTENSION_INFO
{
struct _EXTENSION_INFO * Next;
struct _FILE_INFO * StatInfoList;
TCHAR ExtName[16];
TCHAR ExpandedExtName[128];
DWORD nExtensions;
TCHAR Description[256];
DWORD FileCount;
DWORD LineCount;
DWORD EmptyLines;
} EXTENSION_INFO, *PEXTENSION_INFO;
typedef struct _FILE_INFO
{
struct _FILE_INFO * Next;
struct _FILE_INFO * StatInfoListNext;
PEXTENSION_INFO ExtInfo;
TCHAR FileName[MAX_PATH];
DWORD LineCount;
DWORD EmptyLines;
DWORD FunctionCount;
} FILE_INFO, *PFILE_INFO;
HANDLE FileHandle;
PEXTENSION_INFO ExtInfoList;
PFILE_INFO StatInfoList;
BOOLEAN SkipEmptyLines, BeSilent;
#define MAX_OPTIONS 2
TCHAR *Options[MAX_OPTIONS];
VOID
Initialize(VOID)
{
ExtInfoList = NULL;
StatInfoList = NULL;
}
VOID
Cleanup(VOID)
{
PEXTENSION_INFO ExtInfo;
PEXTENSION_INFO NextExtInfo;
ExtInfo = ExtInfoList;
while (ExtInfo != NULL)
{
NextExtInfo = ExtInfo->Next;
HeapFree (GetProcessHeap(), 0, ExtInfo);
ExtInfo = NextExtInfo;
}
}
PEXTENSION_INFO
AddExtension(LPTSTR ExtName,
LPTSTR Description)
{
PEXTENSION_INFO ExtInfo;
PEXTENSION_INFO Info;
TCHAR *t;
DWORD ln;
ExtInfo = (PEXTENSION_INFO) HeapAlloc (GetProcessHeap(), 0, sizeof (EXTENSION_INFO));
if (!ExtInfo)
return NULL;
for(t = ExtName; *t != _T('\0'); t += _tcslen(t) + 1);
ln = (DWORD_PTR)t - (DWORD_PTR)ExtName;
ZeroMemory (ExtInfo, sizeof (EXTENSION_INFO));
memcpy (ExtInfo->ExtName, ExtName, ln);
_tcscpy (ExtInfo->Description, Description);
for(t = ExtInfo->ExtName; *t != _T('\0'); t += _tcslen(t) + 1)
{
if(ExtInfo->nExtensions++ != 0)
_tcscat (ExtInfo->ExpandedExtName, _T(";"));
_tcscat (ExtInfo->ExpandedExtName, _T("*."));
_tcscat (ExtInfo->ExpandedExtName, t);
}
if (ExtInfoList)
{
Info = ExtInfoList;
while (Info->Next != NULL)
{
Info = Info->Next;
}
Info->Next = ExtInfo;
}
else
{
ExtInfoList = ExtInfo;
}
return ExtInfo;
}
PFILE_INFO
AddFile(LPTSTR FileName,
PEXTENSION_INFO ExtInfo)
{
PFILE_INFO StatInfo;
PFILE_INFO Info;
StatInfo = (PFILE_INFO) HeapAlloc (GetProcessHeap(), 0, sizeof (FILE_INFO));
if (!StatInfo)
return NULL;
ZeroMemory (StatInfo, sizeof (FILE_INFO));
_tcscpy (StatInfo->FileName, FileName);
StatInfo->ExtInfo = ExtInfo;
if (ExtInfo->StatInfoList)
{
Info = ExtInfo->StatInfoList;
while (Info->StatInfoListNext != NULL)
{
Info = Info->StatInfoListNext;
}
Info->StatInfoListNext = StatInfo;
}
else
{
ExtInfo->StatInfoList = StatInfo;
}
if (StatInfoList)
{
Info = StatInfoList;
while (Info->Next != NULL)
{
Info = Info->Next;
}
Info->Next = StatInfo;
}
else
{
StatInfoList = StatInfo;
}
return StatInfo;
}
VOID
CleanupAfterFile(VOID)
{
if(FileHandle != INVALID_HANDLE_VALUE)
CloseHandle (FileHandle);
}
BOOL
LoadFile(LPTSTR FileName)
{
LONG FileSize;
FileHandle = CreateFile (FileName, // Create this file
GENERIC_READ, // Open for reading
0, // No sharing
NULL, // No security
OPEN_EXISTING, // Open the file
FILE_ATTRIBUTE_NORMAL, // Normal file
NULL); // No attribute template
if (FileHandle == INVALID_HANDLE_VALUE)
return FALSE;
FileSize = GetFileSize (FileHandle, NULL);
if (FileSize <= 0)
{
CloseHandle (FileHandle);
return FALSE;
}
return TRUE;
}
VOID
ReadLines(PFILE_INFO StatInfo)
{
DWORD ReadBytes, LineLen;
static char FileBuffer[1024];
char LastChar = '\0';
char *Current;
LineLen = 0;
while(ReadFile (FileHandle, FileBuffer, sizeof(FileBuffer), &ReadBytes, NULL) && ReadBytes >= sizeof(char))
{
for(Current = FileBuffer; ReadBytes > 0; ReadBytes -= sizeof(char), Current++)
{
if(*Current == '\n' && LastChar == '\r')
{
LastChar = '\0';
if(!SkipEmptyLines || (LineLen > 0))
StatInfo->LineCount++;
if(LineLen == 0)
StatInfo->EmptyLines++;
LineLen = 0;
continue;
}
LastChar = *Current;
if(SkipEmptyLines && (*Current == ' ' || *Current == '\t'))
{
continue;
}
if(*Current != '\r')
LineLen++;
}
}
StatInfo->LineCount += (LineLen > 0);
StatInfo->EmptyLines += ((LastChar != '\0') && (LineLen == 0));
}
VOID
PrintStatistics(VOID)
{
PEXTENSION_INFO Info;
DWORD TotalFileCount;
DWORD TotalLineCount;
DWORD TotalAvgLF;
DWORD TotalEmptyLines;
TotalFileCount = 0;
TotalLineCount = 0;
TotalEmptyLines = 0;
Info = ExtInfoList;
for (Info = ExtInfoList; Info != NULL; Info = Info->Next)
{
TotalFileCount += Info->FileCount;
TotalLineCount += Info->LineCount;
TotalEmptyLines += Info->EmptyLines;
}
TotalAvgLF = (TotalFileCount ? TotalLineCount / TotalFileCount : 0);
for (Info = ExtInfoList; Info != NULL; Info = Info->Next)
{
DWORD AvgLF;
if (Info->FileCount != 0)
{
AvgLF = (Info->FileCount ? Info->LineCount / Info->FileCount : 0);
}
else
{
AvgLF = 0;
}
_tprintf (_T("\n"));
_tprintf (_T("File extension%c : %s\n"), ((Info->nExtensions > 1) ? _T('s') : _T(' ')), Info->ExpandedExtName);
_tprintf (_T("File ext. description : %s\n"), Info->Description);
_tprintf (_T("Number of files : %lu\n"), Info->FileCount);
_tprintf (_T("Number of lines : %lu\n"), Info->LineCount);
if(SkipEmptyLines)
_tprintf (_T("Number of empty lines : %lu\n"), Info->EmptyLines);
_tprintf (_T("Proportion of lines : %.2f %%\n"), (float)(TotalLineCount ? (((float)Info->LineCount * 100) / (float)TotalLineCount) : 0));
_tprintf (_T("Average no. lines/file : %lu\n"), AvgLF);
}
_tprintf (_T("\n"));
_tprintf (_T("Total number of files : %lu\n"), TotalFileCount);
_tprintf (_T("Total number of lines : %lu\n"), TotalLineCount);
if(SkipEmptyLines)
_tprintf (_T("Total number of empty lines : %lu\n"), TotalEmptyLines);
_tprintf (_T("Average no. lines/file : %lu\n"), TotalAvgLF);
}
BOOL
ProcessFiles(LPTSTR Path)
{
WIN32_FIND_DATA FindFile;
PEXTENSION_INFO Info;
TCHAR SearchPath[256];
TCHAR FileName[256];
TCHAR *Ext;
HANDLE SearchHandle;
BOOL More;
Info = ExtInfoList;
while (Info != NULL)
{
Ext = Info->ExtName;
do
{
ZeroMemory (&FindFile, sizeof (FindFile));
_tcscpy (SearchPath, Path);
_tcscat (SearchPath, _T("\\*."));
_tcscat (SearchPath, Ext);
_tcscpy (FindFile.cFileName, SearchPath);
SearchHandle = FindFirstFile (SearchPath, &FindFile);
if (SearchHandle != INVALID_HANDLE_VALUE)
{
More = TRUE;
while (More)
{
if (!(FindFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
_tcscpy (FileName, Path);
_tcscat (FileName, _T("\\"));
_tcscat (FileName, FindFile.cFileName);
if (LoadFile (FileName))
{
PFILE_INFO StatInfo;
StatInfo = AddFile (FindFile.cFileName, Info);
if (!StatInfo)
{
_tprintf (_T("Not enough free memory.\n"));
return FALSE;
}
ReadLines (StatInfo);
Info->FileCount++;
Info->LineCount += StatInfo->LineCount;
Info->EmptyLines += StatInfo->EmptyLines;
CleanupAfterFile();
}
}
More = FindNextFile (SearchHandle, &FindFile);
}
FindClose (SearchHandle);
}
Ext += _tcslen(Ext) + 1;
} while(*Ext != _T('\0'));
Info = Info->Next;
}
return TRUE;
}
BOOL
ProcessDirectories(LPTSTR Path)
{
WIN32_FIND_DATA FindFile;
TCHAR SearchPath[MAX_PATH];
HANDLE SearchHandle;
BOOL More;
if(!BeSilent)
{
_tprintf (_T("Processing %s ...\n"), Path);
}
_tcscpy (SearchPath, Path);
_tcscat (SearchPath, _T("\\*.*"));
SearchHandle = FindFirstFileEx (SearchPath,
FindExInfoStandard,
&FindFile,
FindExSearchLimitToDirectories,
NULL,
0);
if (SearchHandle != INVALID_HANDLE_VALUE)
{
More = TRUE;
while (More)
{
if ((FindFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
&& (_tcscmp (FindFile.cFileName, _T(".")) != 0)
&& (_tcscmp (FindFile.cFileName, _T("..")) != 0)
&& (_tcscmp (FindFile.cFileName, _T("CVS")) != 0)
&& (_tcscmp (FindFile.cFileName, _T(".svn")) != 0))
{
_tcscpy (SearchPath, Path);
_tcscat (SearchPath, _T("\\"));
_tcscat (SearchPath, FindFile.cFileName);
if (!ProcessDirectories (SearchPath))
return FALSE;
if (!ProcessFiles (SearchPath))
return FALSE;
}
More = FindNextFile (SearchHandle, &FindFile);
}
FindClose (SearchHandle);
}
return TRUE;
}
VOID
Execute(LPTSTR Path)
{
if (!ExtInfoList)
{
_tprintf (_T("No extensions specified.\n"));
return;
}
if (!ProcessDirectories (Path))
{
_tprintf (_T("Failed to process directories.\n"));
return;
}
if (!ProcessFiles (Path))
{
_tprintf (_T("Failed to process files.\n"));
return;
}
PrintStatistics();
}
BOOLEAN
IsOptionSet(TCHAR *Option)
{
int i;
for(i = 0; i < MAX_OPTIONS; i++)
{
if(!Options[i])
continue;
if(!_tcscmp(Options[i], Option))
return TRUE;
}
return FALSE;
}
int _tmain(int argc, _TCHAR * argv[])
{
int a;
_tprintf (_T("ReactOS Project Statistics\n"));
_tprintf (_T("==========================\n\n"));
if (argc < 2 || argc > 2 + MAX_OPTIONS)
{
_tprintf(_T("Usage: stats [-e] [-s] directory\n"));
_tprintf(_T(" -e: don't count empty lines\n"));
_tprintf(_T(" -s: be silent, don't print directories while processing\n"));
return 1;
}
Initialize();
AddExtension (_T("c\0\0"), _T("Ansi C Source files"));
AddExtension (_T("cpp\0cxx\0\0"), _T("C++ Source files"));
AddExtension (_T("h\0\0"), _T("Header files"));
for(a = 1; a < argc - 1; a++)
{
Options[a - 1] = argv[a];
}
SkipEmptyLines = IsOptionSet(_T("-e"));
BeSilent = IsOptionSet(_T("-s"));
Execute (argv[argc - 1]);
Cleanup();
return 0;
}