/***************************************************************************/
/* NOTE: */
/* This document is copyright (c) by Oz Solomonovich. All non-commercial */
/* use is allowed, as long as this document is not altered in any way, and */
/* due credit is given. */
/***************************************************************************/
#include "stdafx.h"
#include <shlwapi.h>
#include "LineCount.h"
#include "Config.h"
#include "Parser.h"
#include "ProgressDlg.h"
int FindComment(CString sLine, LPCTSTR sComment, bool bIgnoreStr = false);
proj_stats *CountLines(WWhizInterface* pWWI)
{
WWhizFile * pwwFile;
CString sName, sFullName, cExt, cStr, sProjPath;
int i, c, pos, pos2;
char * linebuf[2048];
file_info * pfi;
CStringList StrList;
CWnd *pWndPercent, *pWndFileName;
MSG msg;
int files = 0;
bool bIsComment = false;
// extensions: semi-colon delimited string -> string list
cStr = cfg_sExtList;
for (;;)
{
pos = cStr.Find(';');
if (pos >= 0)
{
StrList.AddTail(cStr.Left(pos));
cStr = cStr.Mid(pos + 1);
}
else
{
StrList.AddTail(cStr);
break;
}
}
pWWI->RefreshFileList();
if (cfg_bActiveProjOnly && pWWI->GetCurrentProject() == NULL)
{
AfxMessageBox("No active project to count.",
MB_OK | MB_ICONEXCLAMATION);
return NULL;
}
WWhizFileList& wwFileList = cfg_bActiveProjOnly?
pWWI->GetCurrentProject()->GetFileList() : pWWI->GetFileList();
c = wwFileList.GetCount();
if (c == 0)
{
AfxMessageBox("No files to count!", MB_OK | MB_ICONEXCLAMATION);
return NULL;
}
proj_stats *pStats = new proj_stats;
CProgressDlg dlgProg;
dlgProg.Create(dlgProg.IDD);
sProjPath = pWWI->GetCurrentProject()->GetName();
dlgProg.m_ProgressCtrl.SetRange(0, c - 1);
pWndPercent = dlgProg.GetDlgItem(IDC_PERCENT);
pWndFileName = dlgProg.GetDlgItem(IDC_CURFILE);
dlgProg.ShowWindow(SW_SHOW);
dlgProg.RedrawWindow();
pfi = new file_info[c + 1];
for (i = 0; i < c; i++)
{
ifstream ifs;
pwwFile = wwFileList.Get(i);
cExt = pwwFile->GetExt();
cExt.MakeUpper();
if (dlgProg.IsWindowVisible() == FALSE)
goto out;
// update status info
dlgProg.m_ProgressCtrl.SetPos(i);
cStr.Format("%d of %d (%d%%)", i + 1, c, ((i + 1) * 100 / c));
pWndPercent->SetWindowText(cStr);
pWndFileName->SetWindowText("");
if (StrList.Find(cExt))
{
sFullName = pwwFile->GetFullName();
sName = pwwFile->GetTitle() + "." + pwwFile->GetExt();
PathRelativePathTo(pfi[files].sPath.GetBuffer(MAX_PATH),
sProjPath, FILE_ATTRIBUTE_NORMAL, pwwFile->GetPath(),
FILE_ATTRIBUTE_DIRECTORY);
pfi[files].sPath.ReleaseBuffer();
if (pfi[files].sPath.Left(2) == ".\\")
pfi[files].sPath = pfi[files].sPath.Mid(2);
pWndFileName->SetWindowText(pfi[files].sPath + sName);
ifs.open(sFullName, ios::in|ios::nocreate);
if (ifs.good())
{
pfi[files].pWWFile = pwwFile;
pfi[files].sName = sName;
int& lines = pfi[files].lines;
int& code = pfi[files].codelines;
int& blanks = pfi[files].blanklines;
int& comments = pfi[files].commentlines;
int& mixed = pfi[files].mixedlines;
bool bIsCodeLine, bIsCommentLine;
lines = code = blanks = comments = mixed = 0;
while (!ifs.eof())
{
ifs.getline((char *)linebuf, sizeof(linebuf));
lines++;
cStr = (char *)linebuf;
bIsCodeLine = bIsCommentLine = false;
process:
cStr.TrimLeft();
cStr.TrimRight();
if (cStr.IsEmpty())
{
if (bIsCodeLine || bIsCommentLine)
{
if (bIsCodeLine)
{
code++;
if (bIsCommentLine)
mixed++;
}
if (bIsCommentLine)
comments++;
}
else
{
if (!cfg_bProcessBlanks)
{
if (bIsComment)
comments++;
else
code++;
}
else
blanks++;
}
}
else
{
if (bIsComment)
{
bIsCommentLine = true;
if ((pos = FindComment(cStr, "*/", true)) >= 0)
{
bIsComment = false;
cStr = cStr.Mid(pos + 2);
}
else
{
cStr.Empty();
}
goto process;
}
else
{
pos2 = FindComment(cStr, "//");
if ((pos = FindComment(cStr, "/*")) >= 0 &&
(pos2 < 0 || pos < pos2))
{
if (pos > 0)
{
bIsCodeLine = true;
}
bIsCommentLine = true;
bIsComment = true;
goto process;
}
else
{
if (pos2 >= 0)
{
if (pos2 > 0)
{
bIsCodeLine = true;
}
bIsCommentLine = true;
cStr.Empty();
goto process;
}
else
{
cStr.Empty();
bIsCodeLine = true;
goto process;
}
}
}
}
}
ifs.close();
files++;
}
}
// yeild control to the dialog
while (::PeekMessage(&msg, dlgProg.m_hWnd, 0, 0, PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
pfi[files].pWWFile = NULL;
pfi[files].sName.Empty();
pStats->iCount = files;
pStats->pfi = pfi;
while (pfi->pWWFile)
{
pStats->mapExtCount[pfi->pWWFile->GetExt()]++;
pfi++;
}
dlgProg.ShowWindow(SW_HIDE);
return pStats;
out:
dlgProg.DestroyWindow();
delete [] pfi;
delete pStats;
return NULL;
}
int FindComment(CString sLine, LPCTSTR sComment, bool bIgnoreStr)
{
if (!cfg_bProcessCPPComments)
return -1;
if (!bIgnoreStr)
{
int pos, len = sLine.GetLength();
bool bIsStr = false;
for (pos = 0; pos < len; pos++)
{
if (sLine[pos] == '\\')
{
sLine.SetAt(pos, '!');
if (pos + 1 < len)
sLine.SetAt(++pos , '!');
}
else
if (sLine[pos] == '"')
bIsStr = !bIsStr;
else
if (bIsStr)
sLine.SetAt(pos, '!');
}
}
return sLine.Find(sComment);
}