commit 4dab7093da2df1212183c4787a21fe7d718e691b Author: cl0ne Date: Mon Apr 15 18:04:02 2019 +0300 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c435da3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# common +.DS_Store +[Tt]humbs.db +[Dd]esktop.ini +*.bak +*~ +.~*# +.directory +*.lnk + +# build and user files +*.user +ipch/ +*.opendb +*.opensdf +*.sdf +*.ncb +*.suo +*.log +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +.vs/ \ No newline at end of file diff --git a/Counting Rules/Counting_Standards_Ada.pdf b/Counting Rules/Counting_Standards_Ada.pdf new file mode 100644 index 0000000..79323f3 Binary files /dev/null and b/Counting Rules/Counting_Standards_Ada.pdf differ diff --git a/Counting Rules/Counting_Standards_Assembly.pdf b/Counting Rules/Counting_Standards_Assembly.pdf new file mode 100644 index 0000000..c28fa59 Binary files /dev/null and b/Counting Rules/Counting_Standards_Assembly.pdf differ diff --git a/Counting Rules/Counting_Standards_Bash_Shell_Script.pdf b/Counting Rules/Counting_Standards_Bash_Shell_Script.pdf new file mode 100644 index 0000000..7f4bb6e Binary files /dev/null and b/Counting Rules/Counting_Standards_Bash_Shell_Script.pdf differ diff --git a/Counting Rules/Counting_Standards_CFScript.pdf b/Counting Rules/Counting_Standards_CFScript.pdf new file mode 100644 index 0000000..e44e398 Binary files /dev/null and b/Counting Rules/Counting_Standards_CFScript.pdf differ diff --git a/Counting Rules/Counting_Standards_COBOL.pdf b/Counting Rules/Counting_Standards_COBOL.pdf new file mode 100644 index 0000000..2532b6a Binary files /dev/null and b/Counting Rules/Counting_Standards_COBOL.pdf differ diff --git a/Counting Rules/Counting_Standards_C_Cpp.pdf b/Counting Rules/Counting_Standards_C_Cpp.pdf new file mode 100644 index 0000000..0367bc8 Binary files /dev/null and b/Counting Rules/Counting_Standards_C_Cpp.pdf differ diff --git a/Counting Rules/Counting_Standards_C_Sharp.pdf b/Counting Rules/Counting_Standards_C_Sharp.pdf new file mode 100644 index 0000000..26ecee6 Binary files /dev/null and b/Counting Rules/Counting_Standards_C_Sharp.pdf differ diff --git a/Counting Rules/Counting_Standards_C_Shell.pdf b/Counting Rules/Counting_Standards_C_Shell.pdf new file mode 100644 index 0000000..5695646 Binary files /dev/null and b/Counting Rules/Counting_Standards_C_Shell.pdf differ diff --git a/Counting Rules/Counting_Standards_Cascading_Style_Sheets.pdf b/Counting Rules/Counting_Standards_Cascading_Style_Sheets.pdf new file mode 100644 index 0000000..6f38212 Binary files /dev/null and b/Counting Rules/Counting_Standards_Cascading_Style_Sheets.pdf differ diff --git a/Counting Rules/Counting_Standards_Cold_Fusion.pdf b/Counting Rules/Counting_Standards_Cold_Fusion.pdf new file mode 100644 index 0000000..ac88ba4 Binary files /dev/null and b/Counting Rules/Counting_Standards_Cold_Fusion.pdf differ diff --git a/Counting Rules/Counting_Standards_DOS_Batch.pdf b/Counting Rules/Counting_Standards_DOS_Batch.pdf new file mode 100644 index 0000000..ae7088c Binary files /dev/null and b/Counting Rules/Counting_Standards_DOS_Batch.pdf differ diff --git a/Counting Rules/Counting_Standards_Fortran.pdf b/Counting Rules/Counting_Standards_Fortran.pdf new file mode 100644 index 0000000..e38ce11 Binary files /dev/null and b/Counting Rules/Counting_Standards_Fortran.pdf differ diff --git a/Counting Rules/Counting_Standards_HTML_XML.pdf b/Counting Rules/Counting_Standards_HTML_XML.pdf new file mode 100644 index 0000000..51a4057 Binary files /dev/null and b/Counting Rules/Counting_Standards_HTML_XML.pdf differ diff --git a/Counting Rules/Counting_Standards_IDL.pdf b/Counting Rules/Counting_Standards_IDL.pdf new file mode 100644 index 0000000..55b8aff Binary files /dev/null and b/Counting Rules/Counting_Standards_IDL.pdf differ diff --git a/Counting Rules/Counting_Standards_Java.pdf b/Counting Rules/Counting_Standards_Java.pdf new file mode 100644 index 0000000..0a47d3a Binary files /dev/null and b/Counting Rules/Counting_Standards_Java.pdf differ diff --git a/Counting Rules/Counting_Standards_MATLAB.pdf b/Counting Rules/Counting_Standards_MATLAB.pdf new file mode 100644 index 0000000..3880d32 Binary files /dev/null and b/Counting Rules/Counting_Standards_MATLAB.pdf differ diff --git a/Counting Rules/Counting_Standards_Makefile.pdf b/Counting Rules/Counting_Standards_Makefile.pdf new file mode 100644 index 0000000..8692d4e Binary files /dev/null and b/Counting Rules/Counting_Standards_Makefile.pdf differ diff --git a/Counting Rules/Counting_Standards_ObjC.pdf b/Counting Rules/Counting_Standards_ObjC.pdf new file mode 100644 index 0000000..deb9116 Binary files /dev/null and b/Counting Rules/Counting_Standards_ObjC.pdf differ diff --git a/Counting Rules/Counting_Standards_PHP.pdf b/Counting Rules/Counting_Standards_PHP.pdf new file mode 100644 index 0000000..474e1d9 Binary files /dev/null and b/Counting Rules/Counting_Standards_PHP.pdf differ diff --git a/Counting Rules/Counting_Standards_Pascal.pdf b/Counting Rules/Counting_Standards_Pascal.pdf new file mode 100644 index 0000000..285bdd5 Binary files /dev/null and b/Counting Rules/Counting_Standards_Pascal.pdf differ diff --git a/Counting Rules/Counting_Standards_Perl.pdf b/Counting Rules/Counting_Standards_Perl.pdf new file mode 100644 index 0000000..06bdcbe Binary files /dev/null and b/Counting Rules/Counting_Standards_Perl.pdf differ diff --git a/Counting Rules/Counting_Standards_Python.pdf b/Counting Rules/Counting_Standards_Python.pdf new file mode 100644 index 0000000..9f0de07 Binary files /dev/null and b/Counting Rules/Counting_Standards_Python.pdf differ diff --git a/Counting Rules/Counting_Standards_Ruby.pdf b/Counting Rules/Counting_Standards_Ruby.pdf new file mode 100644 index 0000000..f8a3350 Binary files /dev/null and b/Counting Rules/Counting_Standards_Ruby.pdf differ diff --git a/Counting Rules/Counting_Standards_SQL.pdf b/Counting Rules/Counting_Standards_SQL.pdf new file mode 100644 index 0000000..e2df026 Binary files /dev/null and b/Counting Rules/Counting_Standards_SQL.pdf differ diff --git a/Counting Rules/Counting_Standards_Scala.pdf b/Counting Rules/Counting_Standards_Scala.pdf new file mode 100644 index 0000000..7e1907c Binary files /dev/null and b/Counting Rules/Counting_Standards_Scala.pdf differ diff --git a/Counting Rules/Counting_Standards_VHDL.pdf b/Counting Rules/Counting_Standards_VHDL.pdf new file mode 100644 index 0000000..ff3a186 Binary files /dev/null and b/Counting Rules/Counting_Standards_VHDL.pdf differ diff --git a/Counting Rules/Counting_Standards_Verilog.pdf b/Counting Rules/Counting_Standards_Verilog.pdf new file mode 100644 index 0000000..e9dec6b Binary files /dev/null and b/Counting Rules/Counting_Standards_Verilog.pdf differ diff --git a/Counting Rules/Counting_Standards_Visual_Basic.pdf b/Counting Rules/Counting_Standards_Visual_Basic.pdf new file mode 100644 index 0000000..e72a838 Binary files /dev/null and b/Counting Rules/Counting_Standards_Visual_Basic.pdf differ diff --git a/Counting Rules/Counting_Standards_X-Midas.pdf b/Counting Rules/Counting_Standards_X-Midas.pdf new file mode 100644 index 0000000..68c7501 Binary files /dev/null and b/Counting Rules/Counting_Standards_X-Midas.pdf differ diff --git a/Counting Rules/Cyclomatic_Complexity_Standard.pdf b/Counting Rules/Cyclomatic_Complexity_Standard.pdf new file mode 100644 index 0000000..0ddfc45 Binary files /dev/null and b/Counting Rules/Cyclomatic_Complexity_Standard.pdf differ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..802e6a7 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +#UNAME := $(shell uname) +#ifeq ($(UNAME), SunOS) +all: + mkdir -p tmp + cp src/* tmp + for file in $(shell ls tmp) ; do \ + sed -i -e 's///g' tmp/$$file ; \ + done + mkdir -p bin + g++ -Wall -std=c++0x -o bin/UCC -DUNIX tmp/*.cpp + rm -rf tmp +#endif +#notSunOS: + #mkdir -p bin + #g++ -Wall -o bin/UCC -DUNIX src/*.cpp +clean: + -rm -f bin/UCC bin/UCC.exe + -rm -rf tmp + -rmdir bin + diff --git a/Makefile_cygwin b/Makefile_cygwin new file mode 100644 index 0000000..385cb16 --- /dev/null +++ b/Makefile_cygwin @@ -0,0 +1,20 @@ +#UNAME := $(shell uname) +#ifeq ($(UNAME), SunOS) +all: + mkdir -p tmp + cp src/* tmp + for file in $(shell ls tmp) ; do \ + sed -i -e 's///g' tmp/$$file ; \ + done + mkdir -p bin + g++ -Wall -o bin/UCC -DUNIX -DCYGWIN tmp/*.cpp + rm -rf tmp +#endif +#notSunOS: + #mkdir -p bin + #g++ -Wall -o bin/UCC -DUNIX src/*.cpp +clean: + -rm -f bin/UCC bin/UCC.exe + -rm -rf tmp + -rmdir bin + diff --git a/README.md b/README.md new file mode 100644 index 0000000..4e3b801 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# Unified Code Counter +The Unified Code Counter (UCC) is a comprehensive software lines of code counter produced by the USC Center for Systems and Software Engineering. +It is available to the general public as open source code and can be compiled with any ANSI standard C++ compiler. + +**Homepage:** [http://csse.usc.edu/ucc_new/wordpress/](http://csse.usc.edu/ucc_new/wordpress/) + +## Repository info +This repo contains a copy of latest released [version 2018.07](http://csse.usc.edu/ucc_new/wordpress/2018/07/26/ucc-version-2018-07/) sources: +> Finally! Our next, and last, UCC release! We have decided to focus on UCC-Java, so that it has all the features UCC currently does. Hence, I have decided to make this release available to everyone. + +[Release notes](UCC_release_notes_v.2018.07.pdf), [user manual](UCC_user_manual_v.2018.07.pdf) are available as PDFs. Also you can find line count and cyclomatic complexity metric calculation rules in `Counting Rules` subdirectory. + +The tool successfully compiles under Linux, yet it's somewhat buggy and doesn't provide complete support for modern languages' standards. diff --git a/UCC.2010.vcxproj b/UCC.2010.vcxproj new file mode 100644 index 0000000..aa41073 --- /dev/null +++ b/UCC.2010.vcxproj @@ -0,0 +1,208 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + UCC + {FA47D54A-CAC6-4A69-B2CB-F3C4F9C37B0B} + Win32Proj + + + + Application + MultiByte + v140 + + + Application + MultiByte + v140 + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + bin\ + bin/Debug\ + true + bin\ + bin/Release\ + false + + + + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + + + Level3 + EditAndContinue + 4996;%(DisableSpecificWarnings) + + + $(OutDir)UCC.exe + true + $(OutDir)UCC.pdb + Console + false + + + MachineX86 + + + + + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreaded + + + Level3 + ProgramDatabase + + + $(OutDir)UCC.exe + true + Console + true + true + false + + + MachineX86 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/UCC.2010.vcxproj.filters b/UCC.2010.vcxproj.filters new file mode 100644 index 0000000..0d95550 --- /dev/null +++ b/UCC.2010.vcxproj.filters @@ -0,0 +1,339 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Source Files + + + \ No newline at end of file diff --git a/UCC.2015.sln b/UCC.2015.sln new file mode 100644 index 0000000..ae1c422 --- /dev/null +++ b/UCC.2015.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UCC", "UCC.2015.vcxproj", "{FA47D54A-CAC6-4A69-B2CB-F3C4F9C37B0B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FA47D54A-CAC6-4A69-B2CB-F3C4F9C37B0B}.Debug|x86.ActiveCfg = Debug|Win32 + {FA47D54A-CAC6-4A69-B2CB-F3C4F9C37B0B}.Debug|x86.Build.0 = Debug|Win32 + {FA47D54A-CAC6-4A69-B2CB-F3C4F9C37B0B}.Release|x86.ActiveCfg = Release|Win32 + {FA47D54A-CAC6-4A69-B2CB-F3C4F9C37B0B}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/UCC.2015.vcxproj b/UCC.2015.vcxproj new file mode 100644 index 0000000..1c78ed6 --- /dev/null +++ b/UCC.2015.vcxproj @@ -0,0 +1,220 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + UCC + {FA47D54A-CAC6-4A69-B2CB-F3C4F9C37B0B} + Win32Proj + 10.0.14393.0 + + + + Application + MultiByte + v141 + + + Application + MultiByte + v141 + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + bin\ + bin/Debug\ + true + bin\ + bin/Release\ + false + + + UCC + + + + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + + + Level3 + EditAndContinue + 4996;%(DisableSpecificWarnings) + + + $(OutDir)UCC.exe + true + $(OutDir)UCC.pdb + Console + false + + + MachineX86 + + + + + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreaded + NotUsing + Level3 + ProgramDatabase + + + $(OutDir)UCC.exe + true + Console + true + true + false + + + MachineX86 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UCC.2015.vcxproj.filters b/UCC.2015.vcxproj.filters new file mode 100644 index 0000000..10fa921 --- /dev/null +++ b/UCC.2015.vcxproj.filters @@ -0,0 +1,354 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Source Files + + + Source Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Source Files + + + + + + + + + diff --git a/UCC_release_notes_v.2018.07.pdf b/UCC_release_notes_v.2018.07.pdf new file mode 100644 index 0000000..fc20c2a Binary files /dev/null and b/UCC_release_notes_v.2018.07.pdf differ diff --git a/UCC_user_manual_v.2018.07.pdf b/UCC_user_manual_v.2018.07.pdf new file mode 100644 index 0000000..90bf07b Binary files /dev/null and b/UCC_user_manual_v.2018.07.pdf differ diff --git a/contributors.txt b/contributors.txt new file mode 100644 index 0000000..b9b0429 --- /dev/null +++ b/contributors.txt @@ -0,0 +1,40 @@ +Below is the list of major contributors listed in anti-chronological order. + +Dr. Barry Boehm, Marilee Wheaton, and A Winsor Brown had the idea of creating a tool that would provide a standard for code counting, which led to UCC. + +All UCC versions: +* University of Southern California CSCI 590 with Center for Systems and Software Engineering DR students that worked on the UCC project have contributed significantly, and are all greatly appreciated for their high-quality work, dedication, desire to learn, and motivation to improve UCC as a product. +* The Aerospace Corporation and Integrated Applications Incorporated, thank you for your IV&V efforts! + +2009 - Present +* Marilyn A. Sperka, thank you for being a valuable member of the UCC team, training everyone on the team, answering questions, and providing valuable insight. + +2014 - Present +* Randy Maxwell, thank you for making major performance improvements to UCC, modifying the algorithms to consume less RAM, implementing the Scala parser, and implementing UCC to be multithreaded! Additionally, for your independent testing and helping CSCI 590 students implement and fix defects. + +2017 +* Paul Cymerman and Joseph VanDyke, thank you for the suggestions for improvement and implementing the initial Maintainability Index output option! + +2009 - 2014 +* Ryan Pfeifer, thank you for your major contributions for making UCC the product it is today! + +2013.04 +* Robert Christensen, thank you for making a major modification and performance improvement to the Differencing algorithm! + +2011.10 +* Shunpike Baba, thank you for contributing a defect fix! + +2009 - 2011 +* Joni Kim, thank you for your extensive testing and quality assurance efforts! + +2000 - 2011 +* Vu Nguyen, thank you for your major contributions for making UCC the product it is today, and managing the USC students! + +2008-2009 +* Mike Lee, thank you for leading the early UCC project efforts! + +2002 +* Paul Cymerman, Betsy Legg, and Sarah Capellari for coding up the first Diff Tool add-on to UCC! + + + diff --git a/gui/GAsciiDialog.cpp b/gui/GAsciiDialog.cpp new file mode 100644 index 0000000..eb31e85 --- /dev/null +++ b/gui/GAsciiDialog.cpp @@ -0,0 +1,69 @@ +//! GAsciiDialog class methods. +/*! +* \file GAsciiDialog.cpp +* +* This file contains the GAsciiDialog class methods. +*/ + +#include +#include +#include +#include "GAsciiDialog.h" + +/*! +* Constructs a GAsciiDialog object. +* +* \param filePath results file or directory +* \param parent parent widget +* \param f window flags +*/ +GAsciiDialog::GAsciiDialog(const QString &filePath, QWidget *parent, Qt::WindowFlags f) + : QDialog(parent, f) +{ + ui.setupUi(this); + + outputDir = ""; + ui.cboResultsSelect->clear(); + ui.txtResults->clear(); + + QFont font = ui.txtResults->font(); + font.setStyleHint(QFont::Monospace); + font.setFamily("Courier"); + ui.txtResults->setFont(font); + + if (!filePath.isEmpty()) + { + QFileInfo fi(filePath); + if (fi.isDir()) + { + outputDir = filePath; + QDir dir(filePath); + dir.setNameFilters(QStringList() << tr("*outfile*.txt") << tr("MatchedPairs.txt") << tr("*DuplicatePairs.txt") << tr("error_log_*.txt")); + QStringList fileList = dir.entryList(); + for (int i = 0; i < fileList.count(); i++) + ui.cboResultsSelect->addItem(fileList.at(i)); + } + else if (fi.isFile()) + { + outputDir = fi.absolutePath(); + ui.cboResultsSelect->addItem(fi.fileName()); + } + } +} + +/*! +* Selects a file to display. +* +* \param fileName file name +*/ +void GAsciiDialog::on_cboResultsSelect_currentIndexChanged(const QString &fileName) +{ + ui.txtResults->clear(); + QFile file(outputDir + "/" + fileName); + if (file.open(QIODevice::ReadOnly)) + { + QString text = file.readAll(); + file.close(); + ui.txtResults->setText(text); + } +} diff --git a/gui/GAsciiDialog.h b/gui/GAsciiDialog.h new file mode 100644 index 0000000..e07e0fd --- /dev/null +++ b/gui/GAsciiDialog.h @@ -0,0 +1,37 @@ +//! GAsciiDialog class definitions. +/*! +* \file GAsciiDialog.h +* +* This file contains the GAsciiDialog class definition. +*/ + +#ifndef GASCIIDIALOG_H +#define GASCIIDIALOG_H + +#include +#include "ui_GAsciiDialog.h" + +//! ASCII dialog. +/*! +* \class GAsciiDialog +* +* Defines an ASCII dialog. +*/ +class GAsciiDialog : public QDialog +{ + Q_OBJECT + +public: + GAsciiDialog(const QString &filePath, QWidget *parent = 0, Qt::WindowFlags f = 0); + ~GAsciiDialog(){}; + +private slots: + void on_cboResultsSelect_currentIndexChanged(const QString &fileName); + +private: + Ui::GAsciiDialogClass ui; + + QString outputDir; //!< Output directory defined by filePath +}; + +#endif // GASCIIDIALOG_H diff --git a/gui/GAsciiDialog.ui b/gui/GAsciiDialog.ui new file mode 100644 index 0000000..04fd727 --- /dev/null +++ b/gui/GAsciiDialog.ui @@ -0,0 +1,100 @@ + + + GAsciiDialogClass + + + + 0 + 0 + 800 + 500 + + + + UCC Results + + + + :/images/gucc.png:/images/gucc.png + + + true + + + + + + + + + QTextEdit::NoWrap + + + true + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Close + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + btnClose + clicked() + GAsciiDialogClass + accept() + + + 418 + 482 + + + 510 + 470 + + + + + + closeWindow() + + diff --git a/gui/GExtensionDialog.cpp b/gui/GExtensionDialog.cpp new file mode 100644 index 0000000..2a984cb --- /dev/null +++ b/gui/GExtensionDialog.cpp @@ -0,0 +1,384 @@ +//! GExtensionDialog class methods. +/*! +* \file GExtensionDialog.cpp +* +* This file contains the GExtensionDialog class methods. +*/ + +#include +#include +#include +#include +#include "GExtensionDialog.h" +#include //Modification: 2018.04 +#include "QFileInfo" //Modification: 2018.04 +#include "GMainWindow.h" //Modification: 2018.04 + +/*! +* Constructs a GExtensionDialog object. +* +* \param extensionMapDefault map of default language extensions +* \param extensionMapCustom map of custom language extensions +* \param parent parent widget +* \param f window flags +*/ +GExtensionDialog::GExtensionDialog(QMap *extensionMapDefault, QMap *extensionMapCustom, + QWidget *parent, Qt::WindowFlags f) + : QDialog(parent, f) +{ + ui.setupUi(this); + + extMapDefault = extensionMapDefault; + extMapCustom = extensionMapCustom; + customChanged = false; + extChanged = false; + + QListWidgetItem *lwItem; + QFont font; + foreach (QString name, extMapDefault->keys()) + { + ui.lwLanguages->addItem(name); + if (extMapCustom->contains(name)) + { + lwItem = ui.lwLanguages->item(ui.lwLanguages->count() - 1); + font = lwItem->font(); + font.setBold(true); + lwItem->setFont(font); + } + } + + //Modification: 2018.04 Integration changes + bool flag = ((GMainWindow*)parent)->checkButtonClicked; + if(flag){ + ui.pushButton_2->hide(); + } + + ui.tblExtensions->horizontalHeader()->setStretchLastSection(true); + connect(ui.lwLanguages, SIGNAL(itemClicked(QListWidgetItem *)), this, SLOT(languageSelected(QListWidgetItem *))); + connect(ui.tblExtensions, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(itemCustomContextMenuRequested(const QPoint &))); +} + +/*! +* Destroys a GExtensionDialog object. +*/ +GExtensionDialog::~GExtensionDialog() +{ +} + +/*! +* Updates list of extensions when a language is selected. +* +* \param lwItem selected list item +*/ +void GExtensionDialog::languageSelected(QListWidgetItem *lwItem) +{ + if (lwItem) + { + if (extChanged) + { + this->updateCustomExtensions(); + extChanged = false; + } + + QTableWidgetItem *tblItem; + //QStringList *extList; warning + QStringList *extList=NULL; + QString selLang = lwItem->text(); + int i, cnt = 0; + + for (i = ui.tblExtensions->rowCount() - 1; i >= 0; i--) + delete(ui.tblExtensions->takeItem(i, 0)); + + if (extMapCustom->contains(selLang)) + extList = extMapCustom->value(selLang, 0); + else if (extMapDefault->contains(selLang)) + extList = extMapDefault->value(selLang, 0); + + ui.tblExtensions->setRowCount(extList->count()); + if (extList->count() > 0) + { + for (i = 0; i < extList->count(); i++) + { + if (!extList->at(i).isEmpty()) + { + tblItem = new QTableWidgetItem(extList->at(cnt)); + tblItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + ui.tblExtensions->setItem(cnt, 0, tblItem); + cnt++; + } + } + } + } +} + +/*! +* Updates list of custom extensions. +*/ +void GExtensionDialog::updateCustomExtensions() +{ + if (extChanged) + { + QList lwItems = ui.lwLanguages->selectedItems(); + QListWidgetItem *lwItem; + QStringList *extList; + QString selLang; + int i; + + if (lwItems.count() > 0) + { + lwItem = lwItems.first(); + selLang = lwItem->text(); + if (extMapCustom->contains(selLang)) + extList = extMapCustom->value(selLang); + else + { + extList = new QStringList(); + extMapCustom->insert(selLang, extList); + } + extList->clear(); + + for (i = 0; i < ui.tblExtensions->rowCount(); i++) + extList->append(ui.tblExtensions->item(i, 0)->text()); + } + extChanged = false; + } +} + +/*! +* Loads a custom context menu for the extension table. +* +* \param pos location of mouse selection +*/ +void GExtensionDialog::itemCustomContextMenuRequested(const QPoint &pos) +{ + QMenu menu; + QAction *aItem = 0; + aItem = menu.addAction("Revert Selected Language Extensions", this, SLOT(revertSelLangExt())); + aItem = menu.addAction("Revert All Language Extensions", this, SLOT(revertAllLangExt())); + menu.exec(this->mapToGlobal(pos)); + //Warning fix 11.25.16 + (void)aItem; +} + +/*! +* Reverts list of custom extensions to defaults for selected language. +*/ +void GExtensionDialog::revertSelLangExt() +{ + QList lwItems = ui.lwLanguages->selectedItems(); + QListWidgetItem *lwItem; + QFont font; + QStringList *extList; + QString langName; + + if (lwItems.count() < 1) + return; + + lwItem = lwItems.first(); + langName = lwItem->text(); + if (extMapCustom->contains(langName)) + { + font = lwItem->font(); + font.setBold(false); + lwItem->setFont(font); + + extList = extMapCustom->value(langName); + delete(extList); + extMapCustom->remove(langName); + customChanged = true; + extChanged = false; + + this->languageSelected(lwItem); + } +} + +/*! +* Reverts all custom extensions to defaults. +*/ +void GExtensionDialog::revertAllLangExt() +{ + QList lwItems = ui.lwLanguages->selectedItems(); + QListWidgetItem *lwItem; + QFont font; + QList extLists; + int i; + + if (extMapCustom->count() < 1) + return; + + for (i = 0; i < ui.lwLanguages->count(); i++) + { + lwItem = ui.lwLanguages->item(i); + font = lwItem->font(); + font.setBold(false); + lwItem->setFont(font); + } + + extLists = extMapCustom->values(); + for (i = extLists.count() - 1; i >= 0; i--) + delete(extLists.takeAt(i)); + extMapCustom->clear(); + + if (lwItems.count() > 0) + { + lwItem = lwItems.first(); + this->languageSelected(lwItem); + } + customChanged = true; + extChanged = false; +} + +/*! +* Adds a new extension to the list. +*/ +void GExtensionDialog::on_btnAddExt_clicked() +{ + QList tblItems = ui.tblExtensions->selectedItems(); + QList lwItems = ui.lwLanguages->selectedItems(); + QTableWidgetItem *tblItem; + QListWidgetItem *lwItem; + QFont font; + QString ext; + int i, row; + bool ok; + + if (lwItems.count() < 1) + { + QMessageBox::warning(this, tr("Add Error"), tr("Unable to add an extension without a selected language.")); + return; + } + lwItem = lwItems.first(); + ext = QInputDialog::getText(this, tr("Add Extension"), tr("Please enter an extension for %1").arg(lwItem->text()), + QLineEdit::Normal, "", &ok); + if (ok && !ext.isEmpty()) + { + if (!ext.startsWith(".")) + { + QMessageBox::warning(this, tr("Add Error"), tr("Unable to add extension for %1. Extension must begin with a '.' character.")); + return; + } + for (i = 0; i < ui.tblExtensions->rowCount(); i++) + { + if (ui.tblExtensions->item(i, 0)->text() == ext) + break; + } + if (i < ui.tblExtensions->rowCount()) + { + QMessageBox::warning(this, tr("Add Error"), tr("Unable to add extension for %1. Extension already exists.")); + return; + } + + if (tblItems.count() > 0) + { + tblItem = tblItems.first(); + row = tblItem->row() + 1; + } + else + row = ui.tblExtensions->rowCount(); + ui.tblExtensions->insertRow(row); + + tblItem = new QTableWidgetItem(ext); + tblItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + ui.tblExtensions->setItem(row, 0, tblItem); + + font = lwItem->font(); + font.setBold(true); + lwItem->setFont(font); + + customChanged = true; + extChanged = true; + + this->updateCustomExtensions(); + extChanged = false; + } +} + +/*! +* Removes the selected extension(s). +*/ +void GExtensionDialog::on_btnRemoveExt_clicked() +{ + QList tblItems = ui.tblExtensions->selectedItems(); + QList lwItems = ui.lwLanguages->selectedItems(); + QListWidgetItem *lwItem; + QFont font; + int i, row; + + if (lwItems.count() < 1 || tblItems.count() < 1) + return; + + lwItem = lwItems.first(); + font = lwItem->font(); + font.setBold(true); + lwItem->setFont(font); + + for (i = 0; i < tblItems.count(); i++) + { + row = tblItems.at(i)->row(); + delete(ui.tblExtensions->takeItem(row, 0)); + ui.tblExtensions->removeRow(row); + customChanged = true; + extChanged = true; + this->updateCustomExtensions(); + extChanged = false; + } +} + +/*! +* Closes the dialog +*/ +void GExtensionDialog::on_btnClose_clicked() +{ + this->close(); +} + +/*! +* Processes a close event sent to the form. +*/ +void GExtensionDialog::closeEvent(QCloseEvent *) +{ + if (customChanged) + { + ((GMainWindow*)(parent()))->writeExtensionsFile(); + customChanged = false; + this->accept(); + } + else + this->reject(); +} + +//Modification: 2018.04 USC starts +void GExtensionDialog::on_pushButton_clicked() +{ + if (customChanged) + { + ((GMainWindow*)(parent()))->writeExtensionsFile(); + customChanged = false; + this->accept(); + } + else + this->reject(); +} + +void GExtensionDialog::on_pushButton_2_clicked() +{ + QString extensionFile="extensions.txt"; + extensionFile = QFileDialog::getSaveFileName(this, + tr("Extension File"), extensionFile, tr("Extension File (*.txt)"), 0, QFileDialog::DontConfirmOverwrite); + if(extensionFile == "") + return; + QFileInfo fi(extensionFile); + + if (!fi.exists()) + {QFile file(extensionFile); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + { + QMessageBox::warning(this, tr("File Error"), tr("Unable to open extension file: ") + extensionFile + "\n" + file.errorString() ); + return; + } + file.close(); + ((GMainWindow*)(parent()))->ui.txtExtensionFile->setText(extensionFile); + } + this->close(); +} +//Modification: 2018.04 USC ends diff --git a/gui/GExtensionDialog.h b/gui/GExtensionDialog.h new file mode 100644 index 0000000..4e885f6 --- /dev/null +++ b/gui/GExtensionDialog.h @@ -0,0 +1,60 @@ +//! GExtensionDialog class definitions. +/*! +* \file GExtensionDialog.h +* +* This file contains the GExtensionDialog class definition. +*/ + +#ifndef GEXTENSIONDIALOG_H +#define GEXTENSIONDIALOG_H + +#include +#include +#include "ui_GExtensionDialog.h" +#include "ui_GMainWindow.h" //Modification: 2018.04 + +//! Extension dialog. +/*! +* \class GExtensionDialog +* +* Defines an extension dialog. +*/ +class GExtensionDialog : public QDialog +{ + Q_OBJECT + +public: + GExtensionDialog(QMap *extensionMapDefault, QMap *extensionMapCustom, + QWidget *parent = 0, Qt::WindowFlags f = 0); + ~GExtensionDialog(); + +signals: + void updateExtensions(); + +private slots: + void languageSelected(QListWidgetItem *lwItem); + void itemCustomContextMenuRequested(const QPoint &pos); + void revertSelLangExt(); + void revertAllLangExt(); + void on_btnAddExt_clicked(); + void on_btnRemoveExt_clicked(); + void on_btnClose_clicked(); + void closeEvent(QCloseEvent *); + void on_pushButton_clicked(); //Modification: 2018.04 + void on_pushButton_2_clicked(); //Modification: 2018.04 + + +private: + Ui::GExtensionDialogClass ui; //Modification: 2018.04 + Ui::GMainWindowClass ui_main; //Modification: 2018.04 + bool checkButtonClick; //Modification: 2018.04 + + QMap *extMapDefault; //!< Default extension map pointer + QMap *extMapCustom; //!< Custom extension map pointer + bool customChanged; //!< Flag indicating that custom extensions have changed + bool extChanged; //!< Flag indicating that unsaved extensions have changed for the current language + + void updateCustomExtensions(); +}; + +#endif // GEXTENSIONDIALOG_H diff --git a/gui/GExtensionDialog.ui b/gui/GExtensionDialog.ui new file mode 100644 index 0000000..6079f8f --- /dev/null +++ b/gui/GExtensionDialog.ui @@ -0,0 +1,235 @@ + + + GExtensionDialogClass + + + + 0 + 0 + 500 + 389 + + + + Language File Extensions + + + + :/images/gucc.png:/images/gucc.png + + + true + + + true + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + + + + + Languages: + + + + + + + + + + + + + + Extensions: + + + + + + + Qt::CustomContextMenu + + + true + + + 1 + + + false + + + false + + + + Extensions + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p>Add language extension.</p></body></html> + + + + + + + :/images/plus_sign.png:/images/plus_sign.png + + + + 22 + 22 + + + + + + + + true + + + <html><head/><body><p>Remove language extension(s).</p></body></html> + + + + + + + :/images/minus_sign.png:/images/minus_sign.png + + + + 22 + 22 + + + + + + + + true + + + <html><head/><body><p>Close dialog.</p></body></html> + + + + + + + :/images/close.png:/images/close.png + + + + 22 + 22 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Save + + + + + + + Save As + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + diff --git a/gui/GFileOpenMenu.cpp b/gui/GFileOpenMenu.cpp new file mode 100644 index 0000000..b278753 --- /dev/null +++ b/gui/GFileOpenMenu.cpp @@ -0,0 +1,32 @@ +//! GFileOpenMenu class methods. +/*! +* \file GFileOpenMenu.cpp +* +* This file contains the GFileOpenMenu class methods. +*/ + +#include +#include "GFileOpenMenu.h" + +/*! +* Constructs a GFileOpenMenu object. +* +* \param parent parent widget +* \param f window flags +*/ +GFileOpenMenu::GFileOpenMenu(QWidget *parent, Qt::WindowFlags f) + : QDialog(parent, f) +{ + ui.setupUi(this); + + ui.txtFileLines->clear(); + + QString qs = QFileDialog::getOpenFileName(this, tr("Add a file"), "", tr("All Files (*.*)")); + QFile file(qs); + if (file.open(QIODevice::ReadOnly)) + { + QString text = file.readAll(); + file.close(); + ui.txtFileLines->setText(text); + } +} diff --git a/gui/GFileOpenMenu.h b/gui/GFileOpenMenu.h new file mode 100644 index 0000000..ff5162f --- /dev/null +++ b/gui/GFileOpenMenu.h @@ -0,0 +1,32 @@ +//! GFileOpenMenu class definitions. +/*! +* \file GFileOpenMenu.h +* +* This file contains the GFileOpenMenu class definition. +*/ + +#ifndef GFILEOPENMENU_H +#define GFILEOPENMENU_H + +#include +#include "ui_GFileOpenMenu.h" + +//! File open menu dialog. +/*! +* \class GFileOpenMenu +* +* Defines a file open menu dialog. +*/ +class GFileOpenMenu : public QDialog +{ + Q_OBJECT + +public: + GFileOpenMenu(QWidget *parent = 0, Qt::WindowFlags f = 0); + ~GFileOpenMenu(){}; + +private: + Ui::GFileOpenMenuClass ui; +}; + +#endif // GFILEOPENMENU_H diff --git a/gui/GFileOpenMenu.ui b/gui/GFileOpenMenu.ui new file mode 100644 index 0000000..63e8fba --- /dev/null +++ b/gui/GFileOpenMenu.ui @@ -0,0 +1,50 @@ + + + GFileOpenMenuClass + + + + 0 + 0 + 800 + 600 + + + + Open File + + + + :/images/gucc.png:/images/gucc.png + + + + + 10 + 10 + 781 + 581 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + diff --git a/gui/GMainWindow.cpp b/gui/GMainWindow.cpp new file mode 100644 index 0000000..24eb62b --- /dev/null +++ b/gui/GMainWindow.cpp @@ -0,0 +1,1898 @@ +//! GMainWindow class methods. +/*! +* \file GMainWindow.cpp +* +* This file contains the GMainWindow class methods. +*/ + +#include +#include +#include +#include +#include +#include +#include "../src/UCCGlobals.h" +#include "../src/CUtil.h" +#include "../src/DiffTool.h" +#include "../src/MainObject.h" +#include "GMainWindow.h" +#include "GUtil.h" +#include "GSideBySideDialog.h" +#include "QFileInfo" +#include "GMainWindow.h" + + +#define MAX_ARGUMENTS 20 +#define ARGUMENT_LENGTH 1024 + +#ifdef Q_OS_WIN +#define strcpy(dest, src) strcpy_s(dest, ARGUMENT_LENGTH, src) +#endif + +/*! +* Constructs a GMainWindow object. +* +* \param parent parent widget +* \param f window flags +*/ +GMainWindow::GMainWindow(QWidget *parent, Qt::WindowFlags f) + : QMainWindow(parent, f) +{ + ui.setupUi(this); + customChanged = false; + defaultDirSet = false; + execCanceled = false; + checkButtonClicked = true; + +#ifdef NO_WEB_SUPPORT + /* Modification: 2016.01; USC + If there is no Qt Web support (such as mingw in 5.7) we must disable + the visual differencing feature + */ + ui.chkVisualDiffResult->setDisabled(true); +#endif + + ui.splitter->setSizes(QList() << 400 << 0 << 100); + + ui.lblFilesB->hide(); + ui.lwFileListB->hide(); + ui.lblAddFileB->hide(); + ui.lblRemoveFileB->hide(); + ui.lblAddFolderB->hide(); + ui.btnAddFileB->hide(); + ui.btnRemoveFileB->hide(); + ui.btnAddFolderB->hide(); + //Grey out functional differencing on initial screen + ui.chkFuncDifferencing->setEnabled(false); this->getDefaultExtensions(); + this->extensionsUpdated(); + + ui.txtModThreshold->setValidator(new QIntValidator(0, 100, ui.txtModThreshold)); + ui.txtDupThreshold->setValidator(new QIntValidator(0, 100, ui.txtModThreshold)); + ui.txtTruncThreshold->setValidator(new QIntValidator(1, INT_MAX, ui.txtModThreshold)); + ui.txtRamLimit->setValidator(new QIntValidator(1,5120, ui.txtRamLimit)); + ui.txtThreads->setValidator(new QIntValidator( MIN_UCC_THREAD_COUNT, MAX_UCC_THREAD_COUNT, ui.txtThreads)); + + bgrpOutputFormat.addButton(ui.rdoCSVOutput, 0); + bgrpOutputFormat.addButton(ui.rdoAsciiOutput, 1); + bgrpOutputFormat.addButton(ui.rdoLegacyOutput, 2); + bgrpOutputFormat.setExclusive(true); + ui.rdoCSVOutput->setChecked(true); + +#if defined(Q_OS_WIN) + ui.chkProcLinks->setChecked(false); + ui.chkProcLinks->setEnabled(false); +#endif + + ui.txtOutputDir->setText(QDir::toNativeSeparators(QDir::homePath())); + + this->parsePreferencesFile(); + ui.chkExtensionFile->setChecked(false); + ui.txtExtensionFile->setText(""); + + progressBar = new QProgressBar(); + progressBar->setRange(0, 100); + progressBar->setTextVisible(false); + progressBar->setVisible(false); // initially hidden + ui.statusBar->addPermanentWidget(progressBar); + setAcceptDrops(true); + + connect(ui.lwFileListA, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(listACustomContextMenuRequested(const QPoint &))); + connect(ui.lwFileListB, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(listBCustomContextMenuRequested(const QPoint &))); +} + +/*! +* Destroys a GMainWindow object. +*/ +GMainWindow::~GMainWindow() +{ + QMapIterator itd(extensionMapDefault); + while (itd.hasNext()) + { + itd.next(); + delete(itd.value()); + } + extensionMapDefault.clear(); + QMapIterator itc(extensionMapCustom); + while (itc.hasNext()) + { + itc.next(); + delete(itc.value()); + } + extensionMapCustom.clear(); +} + +/*! +* Start the count and/or comparison operation. +* +* This is roughly equal to the UCC main.cpp procedure +* so do similar calls to be sure UCC is setup OK. +*/ +void GMainWindow::on_btnStart_clicked() +{ + ClearSummaryMsgCounts(); // Modification: 2015.12 + Init_StackDump(); // Modification: 2015.12 + CUtil::InitToLower(); // Initialize to lower helper array + + // Erase any messages from earlier runs + ui.txtMessages->clear(); + + QFile file; + QStringList argList; + QString text, fileName; + int i; + + //Qwebviewer + QString filePathA, filePathB; + + if ((ui.lwFileListA->count() < 1) && (!ui.chkDifferencing->isChecked() || ui.lwFileListB->count() < 1)) //|| (ui.lwFileListA->selectedItems()).length() < 1) + { + QMessageBox::warning(this, tr("At Least 1 Selection Needed"), tr("No files/folders are selected for processing.")); + return; + } + + if ( ( ui.chkDifferencing->isChecked() ) + && ( ( 0 == ui.lwFileListA->count() ) || ( 0 == ui.lwFileListB->count() ) ) ) + { + QMessageBox::warning(this, tr("2 Baselines for Differencing Needed"), tr("There are not enough files/folders selected to find Differences between.\nPlease select some more.")); + return; + } + + QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); + ui.statusBar->showMessage(tr("Creating file list(s)...")); + execCanceled = false; + this->executionRunning(true); + + // Now that UCC is running without any more User input for now, + // capture the start of the total running time + InitTimes(); + + // Change these as you wish + bool show_any_times = true; + bool show_total_only = false; + + // Check for this Option first as it uses Help feature from UCC + if ( ui.chkExtensionFile->isChecked() ) + { + if ( !ui.txtExtensionFile->text().isEmpty() ) + { + // write out extension file if changed + if (customChanged) + { + if (!this->writeExtensionsFile()) + { + ui.statusBar->clearMessage(); + return; + } + } + argList.append("-extfile"); + argList.append(ui.txtExtensionFile->text()); + } + /* + else + { + // No Extension file specs given so run UCC to get Help in a pop up + QString qMsg; + if ( 0 == qMsg.size() ) + { + MainObject mainObject; + mainObject.ConnectUserIF(this); + string msg; + mainObject.ShowUsage( "-extfile", false, &msg ); + qMsg = msg.c_str(); + } + + // Pop up and give the User a look at the Help text + QMessageBox * pMsgBox = new QMessageBox( QMessageBox::Information, "About -extfile Option", "Working ... hit OK!", QMessageBox::Ok, + NULL, + Qt::Dialog ); + pMsgBox->setText( qMsg ); + pMsgBox->show(); + + delete pMsgBox; + + return; + } + */ + } + + // process file list + if (ui.chkDifferencing->isChecked()) + { + fileName = FILE_LIST_A; + if (!ui.txtOutputDir->text().isEmpty()) + fileName = ui.txtOutputDir->text() + "/" + fileName; + file.setFileName(fileName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + { + QFileDevice::FileError fileError = file.error(); + if ( QFileDevice::OpenError == file.error() ) + { + // We get this when the Path does not exist, so make it. + QString path = file.fileName(); + QString dir = path.section( "/", 0, -2 ); + QDir qDir; + bool dirMade = qDir.mkdir( dir ); + if ( !dirMade ) + { + ui.statusBar->clearMessage(); + QApplication::restoreOverrideCursor(); + QMessageBox::warning( this, tr("Unable to Create Directory"), dir ); + this->executionRunning( false ); + return; + } + + // Now we should Write the file OK to the new directory + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + fileError = file.error(); + else + fileError = QFileDevice::NoError; + } + + if ( QFileDevice::NoError != fileError ) + { + ui.statusBar->clearMessage(); + QApplication::restoreOverrideCursor(); + QString text_msg = tr("Unable to open ") + QDir::toNativeSeparators(fileName) + tr(" for writing.\n"); + string numStr; + IntToStr( (int)file.error(), numStr ); + text_msg += numStr.c_str(); + text_msg += " " + file.errorString(); + QMessageBox::warning( this, tr("File Error"), text_msg ); + this->executionRunning(false); + return; + } + } + QTextStream tsa(&file); + for (i = 0; i < ui.lwFileListA->count(); ++i) + { + text = ui.lwFileListA->item(i)->text(); + //Qwebviewer + filePathA = text; + tsa << text << endl; + } + file.close(); + + fileName = FILE_LIST_B; + if (!ui.txtOutputDir->text().isEmpty()) + fileName = ui.txtOutputDir->text() + "/" + fileName; + file.setFileName(fileName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + { + ui.statusBar->clearMessage(); + QApplication::restoreOverrideCursor(); + QString text_msg = tr("Unable to open ") + QDir::toNativeSeparators(fileName) + tr(" for writing.\n"); + string numStr; + IntToStr( (int)file.error(), numStr ); + text_msg += numStr.c_str(); + text_msg += " " + file.errorString(); + QMessageBox::warning( this, tr("File Error"), text_msg ); + this->executionRunning(false); + return; + } + QTextStream tsb(&file); + for (i = 0; i < ui.lwFileListB->count(); ++i) + { + text = ui.lwFileListB->item(i)->text(); + //Qwebviewer + filePathB = text; + tsb << text << endl; + } + file.close(); + } + else + { + fileName = "fileList.txt"; + + /* save fileList to the current path instead of the output path */ + + file.setFileName(fileName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + { + ui.statusBar->clearMessage(); + QApplication::restoreOverrideCursor(); + QString text_msg = tr("Unable to open ") + QDir::toNativeSeparators(fileName) + tr(" for writing.\n"); + text_msg += file.errorString(); + QMessageBox::warning( this, tr("File Error"), text_msg ); + this->executionRunning(false); + return; + } + QTextStream ts(&file); + for (i = 0; i < ui.lwFileListA->count(); i++) + { + text = ui.lwFileListA->item(i)->text(); + ts << text << endl; + } + file.close(); + } + + ui.statusBar->showMessage(tr("Processing program options...")); + QApplication::processEvents(); + if (execCanceled) + { + ui.statusBar->clearMessage(); + QApplication::restoreOverrideCursor(); + this->executionRunning(false); + return; + } + + // add application name and output directory to arguments + argList.insert(begin(argList),QApplication::applicationName()); + if (ui.txtOutputDir->text().isEmpty()) + { + ui.statusBar->clearMessage(); + QApplication::restoreOverrideCursor(); + QMessageBox::warning(this, tr("Selection Error"), tr("A valid Output Directory must be specified.")); + this->executionRunning(false); + return; + } + QFileInfo fio(ui.txtOutputDir->text()); + if (!fio.isDir()) + { + // Create the Directory the User gave + QString dir = ui.txtOutputDir->text(); + QDir qDir; + bool dirMade = qDir.mkdir( dir ); + if ( !dirMade ) + { + ui.statusBar->clearMessage(); + QApplication::restoreOverrideCursor(); + QMessageBox::warning( this, tr("Unable to Create Directory"), dir ); + this->executionRunning(false); + return; + } + } + argList.append("-outdir"); + argList.append(ui.txtOutputDir->text()); + + if (ui.cbxNoHeader->isChecked()) + argList.append("-noheader"); + // process program options + if (ui.chkDifferencing->isChecked()) + { + argList.append("-d"); + if (ui.chkModThreshold->isChecked()) + { + argList.append("-t"); + argList.append(ui.txtModThreshold->text()); + } + if(ui.chkVisualDiffResult->isChecked()){ + argList.append("-visualdiff"); + } + // Modification 2017.02 + if(ui.chkFuncDifferencing->isChecked()) + { + argList.append("-funcDiff"); + } + } + + if (ui.chkDupThreshold->isChecked()) + { + argList.append("-tdup"); + argList.append(ui.txtDupThreshold->text()); + } + + if (ui.chkTruncThreshold->isChecked()) + { + argList.append("-trunc"); + argList.append(ui.txtTruncThreshold->text()); + } + + if (ui.rdoAsciiOutput->isChecked()) + argList.append("-ascii"); + else if (ui.rdoLegacyOutput->isChecked()) + argList.append("-legacy"); + + if (ui.chkUnifiedOut->isChecked()) + argList.append("-unified"); + + if (ui.chkClearCaseFiles->isChecked()) + argList.append("-cf"); + + if (!ui.chkProcDuplicates->isChecked()) + argList.append("-nodup"); + + if (!ui.chkProcComplexity->isChecked()) + argList.append("-nocomplex"); + + if(ui.chkWarnings->isChecked()){ + argList.append("-nowarnings"); + } + + if(ui.chkUncounted->isChecked()){ + argList.append("-nouncounted"); + } + if(ui.chkCc4Enable->isChecked()){ + argList.append("-cc4enable"); + } + if(ui.chkRamLimit->isChecked()){ + argList.append("-ramlimit"); + argList.append(ui.txtRamLimit->text()); + } +#ifdef ENABLE_THREADS + if( ui.chkThreads->isChecked() ) + { + argList.append("-threads"); + argList.append( ui.txtThreads->text().simplified() ); + } +#endif + +#if !defined(Q_OS_WIN) + if (!ui.chkProcLinks->isChecked()) + argList.append("-nolinks"); +#endif + + // convert arguments from QStringList to char ** + char **argv = new char *[MAX_ARGUMENTS]; + for (int i = 0; i < MAX_ARGUMENTS; ++i) + { + argv[i] = new char[ARGUMENT_LENGTH]; + strcpy(argv[i], ""); + } + for (i = 0; i < argList.count(); i++) + { + QByteArray ba = argList.at(i).toLocal8Bit(); + strcpy(argv[i], ba.data()); + } + + ui.statusBar->showMessage(tr("Clearing old results...")); + + // delete all previous output files + QDir outDir(ui.txtOutputDir->text()); + outDir.setNameFilters(QStringList() << "*outfile*.csv" << "MatchedPairs.csv" << "DuplicatePairs.csv" << "*outfile*.txt" << "MatchedPairs.txt" << "DuplicatePairs.txt" << "error_log_*.txt"); + QStringList outList = outDir.entryList(); + QFile outFile; + for (i = 0; i < outList.count(); i++) + { + outFile.setFileName(outDir.absolutePath() + "/" + outList.value(i)); + outFile.remove(); + } + + this->writePreferencesFile(); + QApplication::processEvents(); + if (execCanceled) + { + ui.statusBar->clearMessage(); + QApplication::restoreOverrideCursor(); + this->executionRunning(false); + for (i = 0; i < MAX_ARGUMENTS; ++i) + delete[] argv[i]; + delete[] argv; + return; + } + + ui.statusBar->showMessage(tr("Starting count process...")); + progressBar->setValue(0); + progressBar->show(); + + bool doDiff = false; + bool doDups = false; + double duplicate_threshold_used = 0.0; + unsigned long files_A_count = 0; + unsigned long files_B_count = 0; + if (ui.chkDifferencing->isChecked()) + { + doDiff = true; + // Modification: 2017.02 + if (ui.chkFuncDifferencing->isChecked()) + { + isFuncDiff = true; + } + else + { + isFuncDiff = false; + } + + DiffTool diffTool; + diffTool.ConnectUserIF(this); + diffTool.diffToolProcess(argList.count(), argv); + duplicate_threshold_used = diffTool.GetDuplicateThreshold(); // Modification: 2015.12 + + // Make sure worker Threads are done. Could be half done due to LOW Memory. + FinishWorkerThreads(); // Modification: 2015.12 + + files_A_count = SourceFileA.size(); // Modification: 2015.12 + CountPhysicalFiles( SourceFileA, files_A_count ); // Modification: 2015.12 + + files_B_count = SourceFileB.size(); // Modification: 2015.12 + CountPhysicalFiles( SourceFileB, files_B_count ); // Modification: 2015.12 + // Modification: 2017.02 + if(isFuncDiff) + { + SourceFileA.resize(0); + SourceFileB.resize(0); + DiffTool funcDiffTool; + funcDiffTool.funcDiffProcess(argList.count(), argv); + } + } + else + { + MainObject mainObject; + mainObject.ConnectUserIF(this); + mainObject.MainProcess(argList.count(), argv); + duplicate_threshold_used = mainObject.GetDuplicateThreshold(); // Modification: 2015.12 + + files_A_count = SourceFileA.size(); // Modification: 2015.12 + CountPhysicalFiles( SourceFileA, files_A_count ); // Modification: 2015.12 + } + + for (i = 0; i < MAX_ARGUMENTS; ++i) + delete[] argv[i]; + delete[] argv; + + QApplication::restoreOverrideCursor(); + ui.statusBar->clearMessage(); + progressBar->setValue(0); + progressBar->hide(); + this->executionRunning(false); + + if (!execCanceled) + { + // add all the output files in the combo box + if (ui.rdoCSVOutput->isChecked()) + { + + mTableDialog = new GTableDialog(ui.txtOutputDir->text(), this); + mTableDialog->show(); + } + else + { + + mAsciiDialog = new GAsciiDialog(ui.txtOutputDir->text(), this); + mAsciiDialog->show(); + } + +#ifndef NO_WEB_SUPPORT + /* Modification: 2016.01; USC + Can only show if web support is available (this is currently true on + all platforms that support Qt except for Qt 5.6 and above with mingw + */ + // Qwebviewer: Show side-by-side dailog + if(ui.chkDifferencing->isChecked() && ui.chkVisualDiffResult->isChecked()) + { + mSideBySideDialog = new GSideBySideDialog( + ui.txtOutputDir->text() + "\\" + FILE_LIST_A, + ui.txtOutputDir->text() + "\\" + FILE_LIST_B, + ui.txtOutputDir->text(), this); + mSideBySideDialog->show(); + } + else{} +#endif + + // Get the Time to show + time( &timeEnd ); + double total_seconds = difftime( timeEnd, timeStart ) + 0.5; + + string time_str; + char buf[MSG_STR_BUF_SIZE]; + + // Use more Secure C library APIs if available + #ifdef _MSC_VER + strcpy_s( buf, MSG_STR_BUF_SIZE * sizeof(char), "" ); + #else + // Use less Secure original C library APIs + strcpy( buf, "" ); + #endif + + if ( doDiff ) + { + TimeMsg( buf, time_str, " A Files: ", (long)files_A_count, " ", false ); + TimeMsg( buf, time_str, " B Files: ", (long)files_B_count, "", false ); + } + else + TimeMsg( buf, time_str, " ", (long)files_A_count, " files", false ); + + if ( 1.0 > total_seconds ) + total_seconds = 1.0; // AVOID divide by Zero + + // Use more Secure C library APIs if available + #ifdef _MSC_VER + strcpy_s( buf, MSG_STR_BUF_SIZE * sizeof(char), "" ); + sprintf_s( buf, MSG_STR_BUF_SIZE * sizeof(char), " for %.1f files processed per second\n", + ( (double)( files_A_count + files_B_count )/total_seconds ) + .05 ); + #else + // Use less Secure original C library APIs + strcpy( buf, "" ); + sprintf( buf, " for %.1f files processed per second\n", + ( (double)( files_A_count + files_B_count )/total_seconds ) + .05 ); + #endif + time_str += buf; + + if ( duplicate_threshold_used >= 0.0 ) + doDups = true; + + // show the Times of various steps and total elapsed Time if wanted + if ( true == show_any_times ) + ShowTiming( time_str, timeStart, timeEnd, show_total_only, doDiff, doDups ); + + bool extra_counts = false; + if ( stack_dump_count ) + { + string count; + IntToStr( (int)stack_dump_count, count ); + + if ( false == extra_counts ) + { + time_str += "\n"; + extra_counts = true; + } + + time_str += " Stack Dumps: "; + time_str += count; + time_str += "\n"; + } + + if ( errors_count ) + { + string count; + IntToStr( (int)errors_count, count ); + + if ( false == extra_counts ) + { + time_str += "\n"; + extra_counts = true; + } + + time_str += " Errors: "; + time_str += count; + time_str += "\n"; + } + + if ( warnings_count ) + { + string count; + IntToStr( (int)warnings_count, count ); + + if ( false == extra_counts ) + { + time_str += "\n"; + extra_counts = true; + } + + time_str += " Warnings: "; + time_str += count; + time_str += "\n"; + } + + if ( information_count ) + { + string count; + IntToStr( (int)information_count, count ); + + if ( false == extra_counts ) + { + time_str += "\n"; + extra_counts = true; + } + + time_str += " Information: "; + time_str += count; + time_str += "\n"; + } + + if ( uncounted_files_count ) + { + string count; + LongToStr( (long)uncounted_files_count, count ); + + if ( false == extra_counts ) + { + time_str += "\n"; + extra_counts = true; + } + + time_str += "Uncounted Files: "; + time_str += count; + time_str += "\n"; + } + + if ( time_str.size() ) + { + cout << time_str; + + // Write the Times to a file + SavePerformanceStats( time_str ); + + // and show in text message area + QString message = time_str.c_str(); + updateLog( message ); + } + + } + +} + +/*! +* Stop the running the count/comparison operation. +*/ +void GMainWindow::on_btnStop_clicked() +{ + execCanceled = true; + emit this->canceledExecution(); +} + +/*! +* Updates the interface for running execution. +* +* \param enabled execution enabled +*/ +void GMainWindow::executionRunning(bool enabled) +{ + ui.splitter->setEnabled(!enabled); + ui.bgrpOptions->setEnabled(!enabled); + ui.btnStart->setEnabled(!enabled); + ui.btnStop->setEnabled(enabled); +} + +/*! +* Update the displayed log messages. +* +* \param err error string +*/ +void GMainWindow::updateLog(const QString &err) +{ + QString msgLog = ui.txtMessages->toPlainText(); + msgLog += err; + ui.txtMessages->setPlainText(msgLog); + ui.txtMessages->ensureCursorVisible(); + + // Changed font in UI editor to Consolas (Monospaced font) + ui.txtMessages->setEnabled( true ); +} + +/*! +* Update the progress bar and/or status message. +* +* \param msg message string +* \param pct percent completion (-1 if none) +*/ +void GMainWindow::updateProgress(const QString &msg, int pct) +{ + if (!msg.isEmpty()) + ui.statusBar->showMessage(msg); + if (pct >= 0) + progressBar->setValue(pct); +} + +/*! +* Open a file. +*/ +void GMainWindow::on_actionOpen_File_triggered() +{ + QString filePath, defaultDir = ""; + if (!ui.txtOutputDir->text().isEmpty()) + defaultDir = ui.txtOutputDir->text(); + if (ui.rdoCSVOutput->isChecked()) + filePath = QFileDialog::getOpenFileName(this, tr("Open a File"), defaultDir, tr("CSV Files (*.csv);;Text Files (*.txt)")); + else + filePath = QFileDialog::getOpenFileName(this, tr("Open a File"), defaultDir, tr("Text Files (*.txt);;CSV Files (*.csv)")); + if (!filePath.isEmpty()) + { + if (filePath.toLower().endsWith(".csv")) + { + mTableDialog = new GTableDialog(filePath, this); + mTableDialog->show(); + } + else + { + mAsciiDialog = new GAsciiDialog(filePath, this); + mAsciiDialog->show(); + } + } + defaultDirSet = true; +} + +/*! +* Edit extensions. +*/ +void GMainWindow::on_actionEdit_Extensions_triggered() +{ + GExtensionDialog eExtension(&extensionMapDefault, &extensionMapCustom, this); + if (eExtension.exec() == QDialog::Accepted) + customChanged = true; +} + +/*! +* Exit the program. +*/ +void GMainWindow::on_actionExit_triggered() +{ + this->close(); +} + +/*! +* Display the user manual. +*/ +void GMainWindow::on_actionUser_Manual_triggered() +{ + QString fileName = "UCC_user_manual_v." + QString(PRODUCT_REVISION) + ".pdf"; + QString filePath = "../" + fileName; + QFileInfo fi(filePath); + if (!fi.exists()) + { + filePath = fileName; + fi.setFile(filePath); + if (!fi.exists()) + { + QMessageBox::warning(this, tr("File Error"), tr("Unable to open file: ") + fileName); + return; + } + } + GUtil::openFile(fi.absoluteFilePath()); +} + +/*! +* Display the release notes. +*/ +void GMainWindow::on_actionRelease_Notes_triggered() +{ + QString fileName = "UCC_release_notes_v." + QString(PRODUCT_REVISION) + ".pdf"; + QString filePath = "../" + fileName; + QFileInfo fi(filePath); + if (!fi.exists()) + { + filePath = fileName; + fi.setFile(filePath); + if (!fi.exists()) + { + QMessageBox::warning(this, tr("File Error"), tr("Unable to open file: ") + fileName); + return; + } + } + GUtil::openFile(fi.absoluteFilePath()); +} + +/*! +* Display the release notes. +*/ +void GMainWindow::on_actionLicense_triggered() +{ + QString fileName = "license.txt"; + QString filePath = "../" + fileName; + QFileInfo fi(filePath); + if (!fi.exists()) + { + filePath = fileName; + fi.setFile(filePath); + if (!fi.exists()) + { + QMessageBox::warning(this, tr("File Error"), tr("Unable to open file: ") + fileName); + return; + } + } + GUtil::openFile(fi.absoluteFilePath()); +} + +/*! +* Display the about information. +*/ +void GMainWindow::on_actionAbout_triggered() +{ + QMessageBox aboutBox(QMessageBox::NoIcon, "About UCC", + "Unified Code Counter\n\n" + "University of Southern California\n" + "Center for Systems and Software Engineering\n\n" + "Version: " + QString(PRODUCT_REVISION), + QMessageBox::Ok, this); + aboutBox.setIconPixmap(QPixmap::fromImage(QImage(":/images/gucc.png").scaled(100, 100, Qt::KeepAspectRatio))); + QPushButton *aboutQtButton = aboutBox.addButton("About Qt", QMessageBox::RejectRole); + aboutBox.exec(); + if (aboutBox.clickedButton() == (QAbstractButton *)aboutQtButton) + QMessageBox::aboutQt(this, "About Qt"); + else + aboutBox.accept(); +} + +/*! +* Add a file to list A. +*/ +void GMainWindow::on_btnAddFileA_clicked() +{ + ui.statusBar->showMessage(tr("Loading file browser...")); + + QStringList filters, fileList; + QString filePath, filterStr, defaultDir = ""; + int i; + + if (!defaultDirSet && !ui.txtOutputDir->text().isEmpty()) + defaultDir = ui.txtOutputDir->text(); + filters = this->getFilters(); + if (filters.count() > 0) + { + filterStr = "Selected Files (" + filters.first(); + for (i = 1; i < filters.count(); i++) + filterStr += " " + filters.at(i); + filterStr += ")"; + } + else + filterStr = "All Files (*.*)"; + fileList = QFileDialog::getOpenFileNames(this, tr("Add a File"), defaultDir, tr("%1").arg(filterStr)); + + foreach(filePath, fileList) + { + filePath = QDir::toNativeSeparators(filePath); + if (ui.lwFileListA->findItems(filePath, Qt::MatchFlags()).isEmpty()) + ui.lwFileListA->addItem(filePath); + + + } + defaultDirSet = true; + ui.statusBar->clearMessage(); +} + +/*! +* Add a file to list B. +*/ +void GMainWindow::on_btnAddFileB_clicked() +{ + QStringList filters, fileList; + QString filePath, filterStr, defaultDir = ""; + int i; + + if (!defaultDirSet && !ui.txtOutputDir->text().isEmpty()) + defaultDir = ui.txtOutputDir->text(); + filters = this->getFilters(); + if (filters.count() > 0) + { + filterStr = "Selected Files (" + filters.first(); + for (i = 1; i < filters.count(); i++) + filterStr += " " + filters.at(i); + filterStr += ")"; + } + else + filterStr = "All Files (*.*)"; + fileList = QFileDialog::getOpenFileNames(this, tr("Add a File"), defaultDir, tr("%1").arg(filterStr)); + + foreach(filePath, fileList) + { + filePath = QDir::toNativeSeparators(filePath); + if (ui.lwFileListB->findItems(filePath, Qt::MatchFlags()).isEmpty()) + ui.lwFileListB->addItem(filePath); + } + defaultDirSet = true; + ui.statusBar->clearMessage(); +} + +/*! +* Remove selected file(s) from list A. +*/ +void GMainWindow::on_btnRemoveFileA_clicked() +{ + QList lwItems = ui.lwFileListA->selectedItems(); + for (int i = lwItems.count() - 1; i >= 0; i--) + delete(lwItems.takeAt(i)); +} + +/*! +* Remove selected file(s) from list B. +*/ +void GMainWindow::on_btnRemoveFileB_clicked() +{ + QList lwItems = ui.lwFileListB->selectedItems(); + for (int i = lwItems.count() - 1; i >= 0; i--) + delete(lwItems.takeAt(i)); +} + +/*! +* Add a folder to list A. +*/ +void GMainWindow::on_btnAddFolderA_clicked() +{ + ui.statusBar->showMessage(tr("Loading file browser...")); + + QString dirPath, defaultDir = ""; + if (!defaultDirSet && !ui.txtOutputDir->text().isEmpty()) + defaultDir = ui.txtOutputDir->text(); + if (!ui.chkProcLinks->isChecked()) + dirPath = QFileDialog::getExistingDirectory(this, tr("Add a Folder"), defaultDir, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + else + dirPath = QFileDialog::getExistingDirectory(this, tr("Add a Folder"), defaultDir, QFileDialog::ShowDirsOnly); + if (!dirPath.isEmpty()) + { + dirPath = QDir::toNativeSeparators(dirPath); + if (ui.lwFileListA->findItems(dirPath, Qt::MatchFlags()).isEmpty()) + ui.lwFileListA->addItem(dirPath); + } + defaultDirSet = true; + ui.statusBar->clearMessage(); +} + +/*! +* Add a folder to list B. +*/ +void GMainWindow::on_btnAddFolderB_clicked() +{ + ui.statusBar->showMessage(tr("Loading file browser...")); + + QString dirPath, defaultDir = ""; + if (!defaultDirSet && !ui.txtOutputDir->text().isEmpty()) + defaultDir = ui.txtOutputDir->text(); + dirPath = QFileDialog::getExistingDirectory(this, + tr("Add a Folder"), defaultDir, QFileDialog::ShowDirsOnly); + if (!dirPath.isEmpty()) + { + if (!dirPath.isEmpty()) + { + dirPath = QDir::toNativeSeparators(dirPath); + if (ui.lwFileListB->findItems(dirPath, Qt::MatchFlags()).isEmpty()) + ui.lwFileListB->addItem(dirPath); + } + } + defaultDirSet = true; + ui.statusBar->clearMessage(); +} + +/*! +* Loads a custom context menu for list A. +* +* \param pos location of mouse selection +*/ +void GMainWindow::listACustomContextMenuRequested(const QPoint &pos) +{ + QMenu menu; + QAction *aItem = 0; + aItem = menu.addAction("Remove All Files/Folders", this, SLOT(removeAllFilesA())); + menu.exec(this->mapToGlobal(pos)); + //Warning fix 11.25.16 + (void)aItem; +} + +/*! +* Loads a custom context menu for list B. +* +* \param pos location of mouse selection +*/ +void GMainWindow::listBCustomContextMenuRequested(const QPoint &pos) +{ + QMenu menu; + QAction *aItem = 0; + aItem = menu.addAction("Remove All Files/Folders", this, SLOT(removeAllFilesB())); + menu.exec(this->mapToGlobal(pos)); + + //Warning fix + (void)aItem; +} + +/*! +* Remove all file/folders from list A. +*/ +void GMainWindow::removeAllFilesA() +{ + for (int i = ui.lwFileListA->count() - 1; i >= 0; i--) + delete(ui.lwFileListA->takeItem(i)); +} + +/*! +* Remove all file/folders from list B. +*/ +void GMainWindow::removeAllFilesB() +{ + for (int i = ui.lwFileListB->count() - 1; i >= 0; i--) + delete(ui.lwFileListB->takeItem(i)); +} + +/*! +* Clear selected extensions. +*/ +void GMainWindow::on_btnClearSelExt_clicked() +{ + ui.lwExtensionList->clearSelection(); +} + +/*! +* Process check boxes. +*/ +void GMainWindow::on_chkDifferencing_clicked() +{ + if (ui.chkDifferencing->isChecked()) + { + ui.lblFilesA->setText("File Set A to Differentiate:"); + ui.lblFilesB->show(); + ui.lwFileListB->show(); + ui.splitter->setSizes(QList() << 200 << 200 << 100); + + ui.lblAddFileB->show(); + ui.lblAddFolderB->show(); + ui.lblRemoveFileB->show(); + ui.btnAddFileB->show(); + ui.btnRemoveFileB->show(); + ui.btnAddFolderB->show(); + + // enable the modified threshold check box + ui.chkModThreshold->setEnabled(true); + // Modification: 2017.02 + //enable the function differencing checkbox + ui.chkFuncDifferencing->setEnabled(true); + } + else + { + ui.lblFilesA->setText("Files/Folders To Count:"); + ui.lblFilesB->hide(); + ui.lwFileListB->hide(); + ui.splitter->setSizes(QList() << 400 << 0 << 100); + + ui.lblAddFileB->hide(); + ui.lblRemoveFileB->hide(); + ui.lblAddFolderB->hide(); + ui.btnAddFileB->hide(); + ui.btnRemoveFileB->hide(); + ui.btnAddFolderB->hide(); + + // disable the modified threshold check box + ui.chkModThreshold->setEnabled(false); + // Modification: 2017.02 + //disable the function differencing checkbox + ui.chkFuncDifferencing->setEnabled(false); + } +} + +/*! +* Process modified threshold check box. +*/ +void GMainWindow::on_chkModThreshold_clicked() +{ + ui.txtModThreshold->setEnabled(ui.chkModThreshold->isChecked()); +} + +/*! +* Process duplicate threshold check box. +*/ +void GMainWindow::on_chkDupThreshold_clicked() +{ + ui.txtDupThreshold->setEnabled(ui.chkDupThreshold->isChecked()); +} + +/*! +* Process line truncation check box. +*/ +void GMainWindow::on_chkTruncThreshold_clicked() +{ + ui.txtTruncThreshold->setEnabled(ui.chkTruncThreshold->isChecked()); +} + +void GMainWindow::on_chkRamLimit_clicked() +{ + ui.txtRamLimit->setEnabled(ui.chkRamLimit->isChecked()); +} + +void GMainWindow::on_chkThreads_clicked() +{ + ui.txtThreads->setEnabled(ui.chkThreads->isChecked()); +} + +/*! +* Prompt user for an output directory. +*/ +void GMainWindow::on_btnBrowseOutputDir_clicked() +{ + ui.statusBar->showMessage(tr("Loading file browser...")); + + QString outputDir = ui.txtOutputDir->text(); + outputDir = QFileDialog::getExistingDirectory(this, tr("Select Output Directory"), outputDir, QFileDialog::ShowDirsOnly); + if (!outputDir.isEmpty()) + ui.txtOutputDir->setText(QDir::toNativeSeparators(outputDir)); + defaultDirSet = true; + ui.statusBar->clearMessage(); +} + +/*! +* Process extension file check box. +*/ +void GMainWindow::on_chkExtensionFile_clicked() +{ + if (ui.chkExtensionFile->isChecked()) + { + ui.txtExtensionFile->setEnabled(true); + ui.btnBrowseExtensionFile->setEnabled(true); + + if (ui.txtExtensionFile->text().isEmpty()) + this->on_btnBrowseExtensionFile_clicked(); + } + else + { + ui.txtExtensionFile->setText(""); + ui.txtExtensionFile->setEnabled(false); + ui.btnBrowseExtensionFile->setEnabled(false); + } +} + +//Modification: 2018.04 USC starts +void GMainWindow::on_createNewFile_clicked() +{ + QString extensionFile="extensions.txt"; + ui.statusBar->showMessage("Creating a new extensions File.."); + extensionFile = QFileDialog::getSaveFileName(this, + tr("Extension File"), extensionFile, tr("Extension File (*.txt)"), 0, QFileDialog::DontConfirmOverwrite); + if (extensionFile=="") + { + ui.chkExtensionFile->setChecked(false); + return; + } + QFileInfo fi(extensionFile); + + if(fi.exists()) + { + QMessageBox::warning(this, tr("File Error"), tr("Extension File Already exists") + extensionFile); + } + else + {QFile file(extensionFile); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + { + QMessageBox::warning(this, tr("File Error"), tr("Unable to open extension file: ") + extensionFile + "\n" + file.errorString() ); + ui.statusBar->clearMessage(); + return; + } + file.close(); +// QFileInfo fi(extensionFile); + if (!fi.isWritable()) + QMessageBox::warning(this, tr("File Error"), tr("Unable to write to extension file: ") + extensionFile); + else + { + ui.txtExtensionFile->setText(QDir::toNativeSeparators(extensionFile)); + this->checkButtonClicked = true; + this->parseExtensionsFile(); + this->on_actionEdit_Extensions_triggered(); + } + } + +} + +void GMainWindow::on_chooseExisting() +{ QString extensionFile="extensions.txt"; + ui.statusBar->showMessage("Opening an existing extension file..."); + extensionFile = QFileDialog::getOpenFileName(this, + tr("Extension File"), extensionFile, tr("Extension File (*.txt)"), 0, QFileDialog::DontConfirmOverwrite); + if (extensionFile==""){ + ui.chkExtensionFile->setChecked(false); + return; + } + QFileInfo fi(extensionFile); + if (fi.exists()) + {QFileInfo fi(extensionFile); + if (!fi.isWritable()) + QMessageBox::warning(this, tr("File Error"), tr("Unable to write to extension file: ") + extensionFile); + else + { + ui.txtExtensionFile->setText(QDir::toNativeSeparators(extensionFile)); + this->parseExtensionsFile(); + this->checkButtonClicked = false; + this->on_actionEdit_Extensions_triggered(); + }} + + //if(!fi.exists()){ + // QMessageBox::warning(this, tr("File Error"), tr("Extension File Not Found") + extensionFile); + //} + +} + +//Modification 2018.04 ends + +/*! +* Prompt user for an extension file. +*/ +void GMainWindow::on_btnBrowseExtensionFile_clicked() +{ + ui.statusBar->showMessage("Loading file browser..."); + +//2018.04 Modification starts + QMessageBox msgBox; + msgBox.setText(tr("Choose New to create a new extension File and Existing to choose an Extension file already present.")); + msgBox.setWindowFlags(windowFlags() & ~Qt::WindowCloseButtonHint); + msgBox.setWindowFlags(windowFlags() | Qt::WindowCloseButtonHint); + QPushButton* pButtonNew = msgBox.addButton(tr("New"), QMessageBox::ActionRole); + QPushButton* pButtonExist = msgBox.addButton(tr("Existing"), QMessageBox::ActionRole); + + QPushButton* pButtonAbort = msgBox.addButton( QMessageBox::Abort); + //QAbstractButton* pButtonNew = msgBox.addButton(tr("New"), QMessageBox::AcceptRole); + //QAbstractButton* pButtonExist = msgBox.addButton(tr("Existing"), QMessageBox::AcceptRole); + //QAbstractButton* pButtonAbort = msgBox.addButton(tr("Cancel"), QMessageBox::RejectRole); + + + msgBox.exec(); + //int i = QMessageBox::question(this, tr("Create File?"), tr("If you want to create a new file click YES else click NO for existing file")); + if (msgBox.clickedButton()==pButtonNew) + { + this->on_createNewFile_clicked(); + } + if(msgBox.clickedButton()==pButtonExist){ + this->on_chooseExisting(); + } + if(msgBox.clickedButton()==pButtonAbort){ + ui.chkExtensionFile->setChecked(false); + } + + // QString extensionFile = ui.txtExtensionFile->text(); +// if (extensionFile.isEmpty()) +// extensionFile = "extensions.txt"; +// extensionFile = QFileDialog::getSaveFileName(this, +// tr("Extension File"), extensionFile, tr("Extension File (*.txt)"), 0, QFileDialog::DontConfirmOverwrite); +// defaultDirSet = true; +// if (!extensionFile.isEmpty()) +// { +// QFileInfo fi(extensionFile); +// if (!fi.exists()) +// { +// int i = QMessageBox::question(this, tr("Create File?"), tr("Specified extension file does not exist.\n Do you want to create it?")); +// if (i != QMessageBox::Yes) +// { +// ui.statusBar->clearMessage(); +// return; +// } +// QFile file(extensionFile); +// if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) +// { +// QMessageBox::warning(this, tr("File Error"), tr("Unable to open extension file: ") + extensionFile + "\n" + file.errorString() ); +// ui.statusBar->clearMessage(); +// return; +// } +// file.close(); +// } +// if (!fi.isWritable()) +// QMessageBox::warning(this, tr("File Error"), tr("Unable to write to extension file: ") + extensionFile); +// else +// { +// ui.txtExtensionFile->setText(QDir::toNativeSeparators(extensionFile)); +// this->parseExtensionsFile(); +// this->on_actionEdit_Extensions_triggered(); +// } +// } +// ui.statusBar->clearMessage(); + +//2018.04 Modification ends +} + +/*! +* Get the file extension filters. +*/ +QStringList GMainWindow::getFilters() +{ + QListWidgetItem *lwItem; + QStringList filters, *extensions; + int i; + + QList lwItems = ui.lwExtensionList->selectedItems(); + foreach (lwItem, lwItems) + { + if (extensionMapCustom.contains(lwItem->text())) + { + extensions = extensionMapCustom.value(lwItem->text()); + for (i = 0; i < extensions->count(); i++) + filters << "*" + extensions->at(i); + } + else if (extensionMapDefault.contains(lwItem->text())) + { + extensions = extensionMapDefault.value(lwItem->text()); + for (i = 0; i < extensions->count(); i++) + filters << "*" + extensions->at(i); + } + } + return(filters); +} + +/*! +* Retrieve the default language counter extensions from UCC. +*/ +void GMainWindow::getDefaultExtensions() +{ + QStringList *extList; + QString langName, ext; + multimap lang_ext_map; + + MainObject *mainObject = new MainObject(); + mainObject->GetLanguageExtensionMap(&lang_ext_map); + + for (multimap::const_iterator iter = lang_ext_map.begin(); iter != lang_ext_map.end(); ++iter) + { + langName = QString::fromStdString(iter->first); + ext = QString::fromStdString(iter->second); + if (extensionMapDefault.contains(langName)) + { + extList = extensionMapDefault.value(langName); + if (extList) + extList->append(ext); + } + else if (!ext.startsWith(".*")) + { + extList = new QStringList(); + if (!ext.isEmpty()) + extList->append(ext); + extensionMapDefault.insert(langName, extList); + } + } + delete(mainObject); +} + +/*! +* Refresh the extension list based on the default list. +*/ +void GMainWindow::extensionsUpdated() +{ + QString langName; + + ui.lwExtensionList->clear(); + foreach (langName, extensionMapDefault.keys()) + ui.lwExtensionList->addItem(langName); +} + +/*! +* Refresh the custom extensions list from the file. +* +* \return status +*/ +bool GMainWindow::parseExtensionsFile() +{ + if (ui.txtExtensionFile->text().isEmpty()) + return(false); + + QFile file(ui.txtExtensionFile->text()); + if (!file.open(QIODevice::ReadOnly)) + { + QMessageBox::warning(this, tr("File Error"), tr("Unable to open the extension file: ") + ui.txtExtensionFile->text() + "\n" + file.errorString() ); + return(false); + } + + QTextStream ts(&file); + QStringList langAndExtensions, extensions, *extList; + QString line, langName, ext; + int i, n, n1, n2; + bool inComment = false; + + QList extLists = extensionMapCustom.values(); + for (i = extLists.count() - 1; i >= 0; i--) + delete(extLists.takeAt(i)); + extensionMapCustom.clear(); + + while (ts.status() == QTextStream::Ok && !ts.atEnd()) + { + line = ts.readLine().simplified(); + if (!line.isEmpty()) + { + // strip comments + if (inComment) + { + n = line.indexOf(']'); + if (n >= 0) + { + line = line.right(line.length() - (n + 1)).simplified(); + inComment = false; + } + } + n1 = line.indexOf('['); + while (n1 >= 0) + { + n2 = line.indexOf(']'); + if (n2 >= 0) + { + line = line.right(line.length() - (n2 + 1)).simplified(); + inComment = false; + } + else + { + line = line.left(n1).simplified(); + inComment = true; + } + n1 = line.indexOf('['); + } + if (line.count('=') == 1) + { + // get language and extensions + langAndExtensions = line.split('='); + langName = langAndExtensions.first(); + extensions = langAndExtensions.at(1).split(',', QString::SkipEmptyParts); + if (extensions.count() > 0) + { + if (!extensionMapCustom.contains(langName)) + { + extList = new QStringList(); + for (i = 0; i < extensions.count(); i++) + { + ext = extensions.at(i).simplified(); + if (ext.startsWith('.')) + extList->append(ext); + } + extensionMapCustom.insert(langName, extList); + } + } + langAndExtensions.clear(); + extensions.clear(); + } + } + } + file.close(); + + return(true); +} + +/*! +* Writes custom extensions list to the file. +* +* \return status +*/ +bool GMainWindow::writeExtensionsFile() +{ + if (ui.txtExtensionFile->text().isEmpty()) + return(false); + + QFile file(ui.txtExtensionFile->text()); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + { + QMessageBox::warning(this, tr("File Error"), tr("Unable to open the extension file: ") + ui.txtExtensionFile->text() + "\n" + file.errorString() ); + return(false); + } + + QTextStream ts(&file); + QStringList *extList; + int i; + QMapIterator it(extensionMapCustom); + while (it.hasNext()) + { + it.next(); + extList = it.value(); + if (extList && extList->count() > 0) + { + ts << it.key() << "=" << extList->first(); + for (i = 1; i < extList->count(); i++) + ts << "," << extList->at(i); + ts << endl; + } + } + file.close(); + + return(true); +} + +/*! +* Parses the user preferences file. +* +* \return status +*/ +bool GMainWindow::parsePreferencesFile() +{ + QFile file(QDir::homePath() + "/.gucc_prefs.txt"); + if (!file.open(QIODevice::ReadOnly)) + return(false); + else + { + QTextStream ts(&file); + QString line, key, val; + QFileInfo fi; + bool b, ok; + int i; + + while (!ts.atEnd()) + { + line = ts.readLine(); + if (!line.isEmpty()) + { + key = line.section("=", 0, 0).simplified(); + val = line.section("=", 1).simplified(); + if (!key.isEmpty() && !val.isEmpty()) + { + if (key == "Threads") + { + i = val.toInt(&ok); + if (ok) + { + ui.chkThreads->setChecked(true); + ui.txtThreads->setText(QString::number(i)); + } + } + else if (key == "RAM Limit") + { + i = val.toInt(&ok); + if (ok) + { + ui.chkRamLimit->setChecked(true); + ui.txtRamLimit->setText(QString::number(i)); + } + } + else if (key == "Output Directory") + { + fi.setFile(val); + if (fi.isDir()) + ui.txtOutputDir->setText(val); + } + else if (key == "Extension File") + { + fi.setFile(val); + if (fi.exists()) + { + ui.chkExtensionFile->setChecked(true); + ui.txtExtensionFile->setText(val); + ui.txtExtensionFile->setEnabled(true); + ui.btnBrowseExtensionFile->setEnabled(true); + this->parseExtensionsFile(); + } + } + else if (key == "Differencing") + { + b = val.toInt(&ok); + if (ok) + { + ui.chkDifferencing->setChecked(b); + ui.chkModThreshold->setEnabled(b); + if (b) + this->on_chkDifferencing_clicked(); + } + } + else if (key == "Modified Threshold") + { + i = val.toInt(&ok); + if (ok) + { + ui.chkModThreshold->setChecked(true); + ui.txtModThreshold->setText(QString::number(i)); + } + } + else if (key == "Duplicate Threshold") + { + i = val.toInt(&ok); + if (ok) + { + ui.chkDupThreshold->setChecked(true); + ui.txtDupThreshold->setEnabled(true); + ui.txtDupThreshold->setText(QString::number(i)); + } + } + else if (key == "Line Truncate") + { + i = val.toInt(&ok); + if (ok) + { + ui.chkTruncThreshold->setChecked(true); + ui.txtTruncThreshold->setEnabled(true); + ui.txtTruncThreshold->setText(QString::number(i)); + } + } + else if (key == "Output Format") + { + if (val == "ASCII") + ui.rdoAsciiOutput->setChecked(true); + else if (val == "Legacy") + ui.rdoLegacyOutput->setChecked(true); + else + ui.rdoCSVOutput->setChecked(true); + } + else if (key == "Unified Output") + { + b = val.toInt(&ok); + if (ok) + ui.chkUnifiedOut->setChecked(b); + } + else if (key == "Clear Case Files") + { + b = val.toInt(&ok); + if (ok) + ui.chkClearCaseFiles->setChecked(b); + } + else if (key == "Process Duplicates") + { + b = val.toInt(&ok); + if (ok) + ui.chkProcDuplicates->setChecked(b); + } + else if (key == "Process Complexity") + { + b = val.toInt(&ok); + if (ok) + ui.chkProcComplexity->setChecked(b); + } + else if (key == "CC4 Enable") + { + b = val.toInt(&ok); + if (ok) + ui.chkCc4Enable->setChecked(b); + } + #if !defined(Q_OS_WIN) + else if (key == "Follow Links") + { + b = val.toInt(&ok); + if (ok) + ui.chkProcLinks->setChecked(b); + } + #endif + } + } + } + file.close(); + } + return(true); +} + +/*! +* Writes the user preferences file. +* +* \return status +*/ +bool GMainWindow::writePreferencesFile() +{ + QFile file(QDir::homePath() + "/.gucc_prefs.txt"); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + return(false); + else + { + QTextStream ts(&file); + if (!ui.chkThreads->isChecked()) + ts << "Threads = " << ui.txtThreads->text().simplified() << endl; + if (!ui.chkRamLimit->isChecked()) + ts << "RAM Limit = " << ui.txtRamLimit->text().simplified() << endl; + if (!ui.txtOutputDir->text().isEmpty()) + { + QFileInfo fi(ui.txtOutputDir->text()); + if (fi.isDir()) + ts << "Output Directory = " << ui.txtOutputDir->text().trimmed() << endl; + } + if (ui.chkExtensionFile->isChecked() && !ui.txtExtensionFile->text().trimmed().isEmpty()) + { + QFileInfo fi(ui.txtExtensionFile->text()); + if (fi.isReadable()) + ts << "Extension File = " << ui.txtExtensionFile->text().trimmed() << endl; + } + if (ui.chkDifferencing->isChecked()) + { + ts << "Differencing = 1" << endl; + if (ui.chkModThreshold->isChecked()) + ts << "Modified Threshold = " << ui.txtModThreshold->text().simplified() << endl; + } + if (ui.chkDupThreshold->isChecked()) + ts << "Duplicate Threshold = " << ui.txtDupThreshold->text().simplified() << endl; + if (ui.chkTruncThreshold->isChecked()) + ts << "Line Truncate = " << ui.txtTruncThreshold->text().simplified() << endl; + if (bgrpOutputFormat.checkedId() == 1) + ts << "Output Format = ASCII" << endl; + else if (bgrpOutputFormat.checkedId() == 2) + ts << "Output Format = Legacy" << endl; + else + ts << "Output Format = CSV" << endl; + if (ui.chkUnifiedOut->isChecked()) + ts << "Unified Output = 1" << endl; + if (ui.chkClearCaseFiles->isChecked()) + ts << "Clear Case Files = 1" << endl; + if (!ui.chkProcDuplicates->isChecked()) + ts << "Process Duplicates = 0" << endl; + if (!ui.chkProcComplexity->isChecked()) + { + ts << "Process Complexity = 0" << endl; + ts << "CC4 Enable = 0" << endl; + } + else + { + // Only if Complexity checking is done + ts << "Process Complexity = 1" << endl; + if (ui.chkCc4Enable->isChecked()) + ts << "CC4 Enable = 1" << endl; + } +#if !defined(Q_OS_WIN) + if (!ui.chkProcLinks->isChecked()) + ts << "Follow Links = 1" << endl; +#endif + + file.close(); + } + return(true); +} + +void GMainWindow::dropEvent(QDropEvent *ev) +{ + QList urls = ev->mimeData()->urls(); + + // Reading the first widget's span. Currently, we have only + // three widgets: 1. for list A 2. for list B 3. for filter extensions. + // So, if the drop location is not in widget related to A, then it belongs + // to list B. + QWidget* currDropWidget = ui.splitter->widget(0); + QRect currRect; + int topLeftX = 0; + int topLeftY = 0; + int width = 0; + int height = 0; + QPoint point = ev->pos(); + + // Get rectangle properties for first widget. + currRect = currDropWidget->geometry(); + currRect.getRect(&topLeftX, &topLeftY, &width, &height); + + foreach(QUrl url, urls) + { + // Logic is to get the widget information using + // QWidget * QSplitter::widget(int index) const. + // Each widget has a geometry() API which returns QRect using which + // we can get starting and ending positions of that widget. + // Using this information we compare it with ev->pos->x to check whether + // a drop location is inside widget or not. + // Note: all this should be done if differencing is checked. Else, we can just add + // the files/directories to list A. + if (ui.chkDifferencing->isChecked()) + { + if (point.x() > topLeftX && point.x() < topLeftX + width) + { + ui.lwFileListA->addItem(url.toLocalFile()); + } + else + { + ui.lwFileListB->addItem(url.toLocalFile()); + } + } + else // case for counting + { + ui.lwFileListA->addItem(url.toLocalFile()); + } + } +} + +void GMainWindow::dragEnterEvent(QDragEnterEvent *ev) +{ + if (ev->mimeData()->hasUrls()) + { + ev->acceptProposedAction(); + } +} + +/* + Oct 22 2016 + button used for choosing custom header text file +*/ +void GMainWindow::on_btnCustomHeader_clicked() +{ + ui.statusBar->showMessage(tr("Loading file browser...")); + QString fileName = QFileDialog::getOpenFileName(this, + tr("Open File"), "", tr("Text File (*.txt)")); + userHeaderFile = fileName.toUtf8().constData(); + ui.txtHeader->setText(fileName); +} + +/* + Oct 22 2016 + check box for no header +*/ + +void GMainWindow::on_cbxNoHeader_clicked() +{ + if (ui.cbxNoHeader->checkState() == Qt::Checked){ + ui.btnCustomHeader->setEnabled(false); + ui.txtHeader->setEnabled(false); + } else { + ui.btnCustomHeader->setEnabled(true); + ui.txtHeader->setEnabled(true); + } + ui.txtHeader->setText(QString("")); +} + +void GMainWindow::on_txtHeader_textChanged(const QString &arg1) +{ + QString fileName = ui.txtHeader->text(); + userHeaderFile = fileName.toUtf8().constData(); + (void)arg1; //Modification: 2018.04: Unused Variable + +} + +void GMainWindow::on_txtHeader_editingFinished() +{ + QString fileName = ui.txtHeader->text(); + QFileInfo check_file(fileName); + if (!check_file.exists() || !check_file.isFile() || !fileName.endsWith(".txt")){ + QMessageBox msgBox; + msgBox.setText("Invalid header text file path! No header is shown by default."); + msgBox.exec(); + } +} + diff --git a/gui/GMainWindow.h b/gui/GMainWindow.h new file mode 100644 index 0000000..c1c1995 --- /dev/null +++ b/gui/GMainWindow.h @@ -0,0 +1,130 @@ +//! GMainWindow class definitions. +/*! +* \file GMainWindow.h +* +* This file contains the GMainWindow class definition. +*/ + +#ifndef GMAINWINDOW_H +#define GMAINWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "GAsciiDialog.h" +#include "GExtensionDialog.h" +#include "GTableDialog.h" +#include "ui_GMainWindow.h" + + +#define FILE_LIST_A "fileListA.txt" +#define FILE_LIST_B "fileListB.txt" + +class GSideBySideDialog; + +//! Main window. +/*! +* \class GMainWindow +* +* Defines a main window. +*/ +class GMainWindow : public QMainWindow +{ + Q_OBJECT + +public: + GMainWindow(QWidget *parent = 0, Qt::WindowFlags f = 0); + ~GMainWindow(); + //Modification: 2018.04 starts + Ui::GMainWindowClass ui; + bool checkButtonClicked; + bool writeExtensionsFile(); + //Modification: 2018.04 ends + +signals: + void canceledExecution(); + +public slots: + void updateLog(const QString &err); + void updateProgress(const QString &msg, int pct); + +private slots: + void on_btnStart_clicked(); + void on_btnStop_clicked(); + void on_actionOpen_File_triggered(); + void on_actionEdit_Extensions_triggered(); + void on_actionExit_triggered(); + void on_actionUser_Manual_triggered(); + void on_actionRelease_Notes_triggered(); + void on_actionLicense_triggered(); + void on_actionAbout_triggered(); + void on_btnAddFileA_clicked(); + void on_btnAddFileB_clicked(); + void on_btnRemoveFileA_clicked(); + void on_btnRemoveFileB_clicked(); + void on_btnAddFolderA_clicked(); + void on_btnAddFolderB_clicked(); + void listACustomContextMenuRequested(const QPoint &pos); + void listBCustomContextMenuRequested(const QPoint &pos); + void removeAllFilesA(); + void removeAllFilesB(); + void on_btnClearSelExt_clicked(); + void on_chkDifferencing_clicked(); + void on_chkModThreshold_clicked(); + void on_chkDupThreshold_clicked(); + void on_chkTruncThreshold_clicked(); + void on_btnBrowseOutputDir_clicked(); + void on_chkExtensionFile_clicked(); + void on_chooseExisting(); //Modification: 2018.04 + void on_createNewFile_clicked(); //Modification : 2018.04 + void on_btnBrowseExtensionFile_clicked(); + void on_chkRamLimit_clicked(); + void on_chkThreads_clicked(); + QStringList getFilters(); + + void on_btnCustomHeader_clicked(); + + void on_cbxNoHeader_clicked(); + + void on_txtHeader_textChanged(const QString &arg1); + + void on_txtHeader_editingFinished(); + + +private: + //Ui::GMainWindowClass ui; //Modification: 2018.04 + + GExtensionDialog *extensionDialog; + GAsciiDialog *mAsciiDialog; + GTableDialog *mTableDialog; + QButtonGroup bgrpOutputFormat; + QProgressBar *progressBar; + //Qwebviewer + GSideBySideDialog *mSideBySideDialog; + + QMap extensionMapDefault; //!< Map of default language extensions + QMap extensionMapCustom; //!< Map of custom language extensions + bool customChanged; //!< Flag indicating that custom extensions have changed + bool defaultDirSet; //!< Flag indicating that a file browser has been opened + bool execCanceled; //!< Flag indicating canceled execution + + void executionRunning(bool enabled); + void getDefaultExtensions(); + void extensionsUpdated(); + bool parseExtensionsFile(); + //bool writeExtensionsFile(); //Modification: 2018.04 + bool parsePreferencesFile(); + bool writePreferencesFile(); + +protected: + void dropEvent(QDropEvent *ev); + void dragEnterEvent(QDragEnterEvent *ev); +}; + +#endif // GMAINWINDOW_H diff --git a/gui/GMainWindow.ui b/gui/GMainWindow.ui new file mode 100644 index 0000000..887465c --- /dev/null +++ b/gui/GMainWindow.ui @@ -0,0 +1,1140 @@ + + + GMainWindowClass + + + + 0 + 0 + 1148 + 683 + + + + Unified Code Counter (UCC) + + + + :/images/gucc.png:/images/gucc.png + + + + + + + Qt::Horizontal + + + + + + + + + Files/Folders To Count: + + + + + + + Qt::CustomContextMenu + + + QAbstractItemView::ExtendedSelection + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Add File + + + + + + + :/images/plus_sign.png:/images/plus_sign.png + + + + 22 + 22 + + + + + + + + Add File + + + + + + + true + + + Remove File + + + + + + + :/images/minus_sign.png:/images/minus_sign.png + + + + 22 + 22 + + + + + + + + Remove File + + + + + + + Add Folder + + + + + + + :/images/add_folder.png:/images/add_folder.png + + + + 22 + 22 + + + + + + + + Add Folder + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + File Set B to Differentiate: + + + + + + + Qt::CustomContextMenu + + + QAbstractItemView::ExtendedSelection + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Add File + + + + + + + :/images/plus_sign.png:/images/plus_sign.png + + + + 22 + 22 + + + + + + + + Add File + + + + + + + true + + + Remove File + + + + + + + :/images/minus_sign.png:/images/minus_sign.png + + + + 22 + 22 + + + + + + + + Remove File + + + + + + + Add Folder + + + + + + + :/images/add_folder.png:/images/add_folder.png + + + + 22 + 22 + + + + + + + + Add Folder + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + Filter Extensions: + + + + + + + QAbstractItemView::ExtendedSelection + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Clear Extension Selection + + + + + + + :/images/minus_sign.png:/images/minus_sign.png + + + + 22 + 22 + + + + + + + + Clear Selection + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + 500 + 200 + + + + Program Options + + + + + 10 + 230 + 151 + 21 + + + + <html><head/><body><p><span style=" font-weight:600;">Duplicate Threshold (</span><span style=" font-weight:600; font-style:italic;">-tdup &lt;#&gt;</span><span style=" font-weight:600;">)</span></p><p>Specifies the percentage of logical source lines of code (LSLOC) that have changed between two files of the same name in order to determine whether the files are duplicates. If the percentage of common LSLOC between two files is less than or equal to the specified threshold, the files are considered duplicates. This method compares LSLOC similar to the differencing function and ignores formatting including blank lines and comments. Note that files of different names may be checked for an exact physical match. The valid range is 0 to 100 and defaults to 0.</p></body></html> + + + Duplicate Threshold + + + + + + 10 + 97 + 111 + 17 + + + + <html><head/><body><p><span style=" font-weight:600;">Differencing (</span><span style=" font-weight:600; font-style:italic;">-d</span><span style=" font-weight:600;">)</span></p><p>Enables the differencing function. If not specified, only the counting functions will execute.</p></body></html> + + + Differencing + + + + + false + + + + 10 + 121 + 151 + 17 + + + + <html><head/><body><p><span style=" font-weight:600;">Modified Threshold (</span><span style=" font-weight:600; font-style:italic;">-t &lt;#&gt;</span><span style=" font-weight:600;">)</span></p><p>Specifies the percentage of common characters between two lines of code that are being compared in order to determine whether the line is modified or replaced. If the percentage of common characters between the compared lines is greater than the specified threshold, the line is considered replaced and will be counted as one line deleted and one line added. Otherwise, it will be counted as one modified line. The valid range is 0 to 100 and defaults to 60.</p></body></html> + + + Modified Threshold + + + + + false + + + + 180 + 120 + 81 + 20 + + + + + 0 + 0 + + + + 60 + + + + + + 10 + 254 + 111 + 20 + + + + <html><head/><body><p><span style=" font-weight:600;">Line Truncate (</span><span style=" font-weight:600; font-style:italic;">-trunc &lt;#&gt;</span><span style=" font-weight:600;">)</span></p><p>Specifies the maximum number of characters allowed in a logical source line of code (LSLOC). Any characters beyond the specified threshold will be truncated and ignored when compared. If the truncation is disabled by setting the threshold to 0 or the threshold is set too high, very long LSLOC may significantly degrade performance.</p></body></html> + + + Line Truncate + + + + + + 10 + 147 + 191 + 17 + + + + Visual Differencing Result + + + + + false + + + + 175 + 253 + 91 + 20 + + + + + 0 + 0 + + + + 10000 + + + + + false + + + + 175 + 228 + 91 + 21 + + + + + 0 + 0 + + + + 0 + + + + + + 460 + 100 + 161 + 91 + + + + Processing + + + + + + <html><head/><body><p><span style=" font-weight:600;">Process Duplicates (</span><span style=" font-weight:600; font-style:italic;">inverse -nodup</span><span style=" font-weight:600;">)</span></p><p>Enables separate processing of duplicate files. Disabling this avoids extra processing time to determine the presence of duplicate files within each baseline. When disabled, all files will be counted and reported together, regardless of whether they are duplicates. Otherwise, file within a baseline will be checked for duplicates and results will be reported separately. Please see the user manual for details.</p></body></html> + + + Process Duplicates? + + + true + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Process Complexity (</span><span style=" font-weight:600; font-style:italic;">inverse -nocomplex</span><span style=" font-weight:600;">)</span></p><p>Enables printing of keyword counts and processing of complexity metrics. Disabling this can reduce processing time and limit reports.</p></body></html> + + + Process Complexity? + + + true + + + + + + + <html><head/><body><p><span style=" font-family:'Calibri';">If this is set then no warning messages will show on the user interface.  Warning messages will still be in the log files.</span></p></body></html> + + + No Warnings + + + + + + + + + 300 + 100 + 141 + 142 + + + + Output Format + + + + + + <html><head/><body><p><span style=" font-weight:600;">Comma Separated (</span><span style=" font-weight:600; font-style:italic;">default</span><span style=" font-weight:600;">)</span></p><p>Prints CSV (*.csv) report files instead of ASCII text (*.txt) files. The content of the CSV format is identical to the ASCII format.</p></body></html> + + + Comma Separated + + + true + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Plain Text (</span><span style=" font-weight:600; font-style:italic;">-ascii</span><span style=" font-weight:600;">)</span></p><p>Prints ASCII text (*.txt) report files instead of CSV (*.csv) files. The content of the ASCII format is identical to the CSV format.</p></body></html> + + + Plain Text + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Legacy Text (</span><span style=" font-weight:600; font-style:italic;">-legacy</span><span style=" font-weight:600;">)</span></p><p>Prints legacy formatted ASCII text report files instead of the current format of the CSV or ASCII text files. The purpose of this option is to maintain backward compatibility with some older UCC results post-processing software systems. This options is rarely required.</p></body></html> + + + Legacy Text + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Unified Output (</span><span style=" font-weight:600; font-style:italic;">-unified</span><span style=" font-weight:600;">)</span></p><p>Prints language report files to a single unified report file. The results are written to 'TOTAL_outfile.csv' or 'TOTAL_outfile.txt'. In the absence of this option, results for each language are written to separate files.</p></body></html> + + + Unified Output + + + + + + + CC4 Enable + + + + + + + + + 10 + 200 + 161 + 21 + + + + <html><head/><body><p><span style=" font-family:'Calibri';">Tell UCC what is a reasonable RAM limit to use to decide if a warning is needed about estimated minimum RAM use.  # may be 1 (default is 5 if not specified) up to 5120.  Each number above zero represents an increment of 100 Mbytes.  So 10 is approximately 1 Gbyte and 5120 is exactly 500 Gbytes.</span></p></body></html> + + + Ram Limit: 1 to 5120 + + + + + false + + + + 175 + 200 + 91 + 21 + + + + + 0 + 0 + + + + + + + 20 + + + + + + 10 + 170 + 131 + 20 + + + + <html><head/><body><p><span style=" font-family:'Calibri';">Tell UCC to use extra worker Threads for faster processing. You should probably not use more than 3 times the number of CPU cores available.</span></p></body></html> + + + Threads: 2 to 80 + + + + + + 180 + 170 + 81 + 20 + + + + <html><head/><body><p>Tell UCC that you want to use extra worker threads for faster processing. No extra worker threads will be used if this is not checked. You probably should not use more than 3 times the actual number of CPU cores.</p></body></html> + + + 2 + + + + + + 10 + 23 + 751 + 78 + + + + + + + false + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Working Directory</span></p><p>Specifies the directory where any intermediate files will be written. The output files will be written to the working directory by default unless an Output Directory is specified.</p></body></html> + + + Output Directory + + + + + + + + + + <html><head/><body><p>Browse the file system for the Working Directory.</p></body></html> + + + + + + + :/images/open.png:/images/open.png + + + + 22 + 22 + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Extension File (</span><span style=" font-weight:600; font-style:italic;">-extfile &lt;filePath&gt;</span><span style=" font-weight:600;">)</span></p><p>Specifies a file containing user specified file extensions for any of the available language counters. Any language counter specified within this file will have its associated extensions replaced. If a language is specified with no extensions, the language counter will be disabled. The file format contains a single line entry for each language. Single or multi-line comments may be included with square brackets []. For example:</p><p>C_CPP = *.cpp, *.h [C/C++ extensions]</p></body></html> + + + Extension File + + + + + + + false + + + <html><head/><body><p>Browse the file system for the Extension File.</p></body></html> + + + + + + + :/images/open.png:/images/open.png + + + + 22 + 22 + + + + + + + + + + 640 + 160 + 161 + 17 + + + + <html><head/><body><p><span style=" font-weight:600;">Follow Links (</span><span style=" font-weight:600; font-style:italic;">inverse -nolinks</span><span style=" font-weight:600;">)</span></p><p>Enables following symbolic links to directories and counting of links to files on Unix systems. Disabling this can prevent duplicate file counts.</p></body></html> + + + Follow Links? + + + + + + 640 + 116 + 161 + 21 + + + + <html><head/><body><p><span style=" font-weight:600;">Clear Case Files (</span><span style=" font-weight:600; font-style:italic;">-cf</span><span style=" font-weight:600;">)</span></p><p>Indicates that the target files were retrieved from IBM Rational ClearCase. ClearCase appends information at the end of file names beginning with '@@'. Use of this option strips all characters after the last '@@' sequence from the file name.</p></body></html> + + + Clear Case Files + + + + + + 640 + 140 + 161 + 17 + + + + <html><head/><body><p><span style=" font-family:'Calibri';">If this is set then there will be no Uncounted file message for either the user interface or the log files.  Not recommended except for experienced users.</span></p></body></html> + + + No Uncounted File Message + + + + + + 460 + 190 + 671 + 81 + + + + Custom Header + + + + + 10 + 20 + 161 + 21 + + + + No Header + + + + + + 540 + 32 + 121 + 31 + + + + Add Header File + + + + + + 10 + 40 + 521 + 20 + + + + + + + + 110 + 90 + 201 + 31 + + + + <html><head/><body><p><span style=" font-weight:600;">Differencing (</span><span style=" font-weight:600; font-style:italic;">-d</span><span style=" font-weight:600;">)</span></p><p>Enables the differencing function. If not specified, only the counting functions will execute.</p></body></html> + + + Function Level Differencing + + + gbxHeader + chkDupThreshold + chkDifferencing + chkModThreshold + txtModThreshold + chkTruncThreshold + chkVisualDiffResult + txtTruncThreshold + txtDupThreshold + grpbProcessing + grpbOutputFormat + chkRamLimit + txtRamLimit + chkThreads + txtThreads + layoutWidget + chkProcLinks + chkClearCaseFiles + chkUncounted + + chkFuncDifferencing + + + + + + + + + + <html><head/><body><p>Starts the execution of counting and/or comparison based on the current selections.</p></body></html> + + + Start + + + + :/images/start.png:/images/start.png + + + + + + + false + + + <html><head/><body><p>Stops the currently running counting operation.</p></body></html> + + + Stop + + + + :/images/stop.png:/images/stop.png + + + + + + + + + false + + + + 0 + 0 + + + + + 16777215 + 80 + + + + + Consolas + + + + true + + + + + + + + + + + 0 + 0 + 1148 + 22 + + + + + :/images/gucc.png:/images/gucc.png + + + + Help + + + + + + + + + + + + + :/images/open.png:/images/open.png + + + Open File + + + Open File + + + + + + :/images/close.png:/images/close.png + + + Exit + + + + + + :/images/help.png:/images/help.png + + + User Manual + + + + + About + + + + + + :/images/edit.png:/images/edit.png + + + View/Edit Extensions + + + View/Edit Extensions + + + + + Release Notes + + + Release Notes + + + + + License + + + + + lwFileListA + btnAddFileA + btnRemoveFileA + btnAddFolderA + lwFileListB + btnAddFileB + btnRemoveFileB + btnAddFolderB + lwExtensionList + btnClearSelExt + txtOutputDir + btnBrowseOutputDir + chkExtensionFile + txtExtensionFile + btnBrowseExtensionFile + chkDifferencing + chkModThreshold + txtModThreshold + chkTruncThreshold + txtTruncThreshold + rdoCSVOutput + rdoAsciiOutput + rdoLegacyOutput + chkUnifiedOut + btnStart + btnStop + txtMessages + + + + + + diff --git a/gui/GSideBySideDialog.cpp b/gui/GSideBySideDialog.cpp new file mode 100644 index 0000000..e69fefd --- /dev/null +++ b/gui/GSideBySideDialog.cpp @@ -0,0 +1,99 @@ +//! GSideBySideDialog class methods. +/*! +* \file GSideBySideDialog.cpp +* +* This file contains the GSideBySideDialog class methods. +*/ + +/* Modification: 2016.01; USC + Updated to no longer require UI file, needed for dynamic + webkit vs webengine code */ +#ifndef NO_WEB_SUPPORT +#include +#include +#include +#include +#include +#include "GSideBySideDialog.h" + +//Different version of Qt need different web classes +#ifdef NEED_WEBKIT_LEGACY +#if (QT_VERSION < 0x050000) +#include +#else +#include +#endif +#else +#include +#endif + +/*! +* Constructs a GSideBySideDialog object. +* +* \param filePathA souce A file or directory +* \param filePathB souce B file or directory +* \param parent parent widget +*/ +GSideBySideDialog::GSideBySideDialog(const QString &filePathA, const QString &filePathB, + const QString &htmlPath, QWidget *parent) + :QDialog(parent) +{ + //Create the child widgets + QLineEdit *lineEdit = new QLineEdit(this); + QLineEdit *lineEdit_2 = new QLineEdit(this); + QPushButton *btnClose = new QPushButton(this); + btnClose->setText("Close"); + connect(btnClose, SIGNAL(clicked()), this, SLOT(accept())); +#ifdef NEED_WEBKIT_LEGACY + QWebView *webView = new QWebView(this); +#else + QWebEngineView *webView = new QWebEngineView(this); +#endif + + //layout the child widgets in this window + QVBoxLayout *masterVLayout = new QVBoxLayout(); + + QHBoxLayout *editLayout = new QHBoxLayout(); + editLayout->addWidget(lineEdit); + editLayout->addWidget(lineEdit_2); + editLayout->setContentsMargins(0,0,0,0); + + QHBoxLayout *closeLayout = new QHBoxLayout(); + closeLayout->addStretch(); + closeLayout->addWidget(btnClose); + closeLayout->addStretch(); + closeLayout->setContentsMargins(0,0,0,0); + + masterVLayout->addLayout(editLayout); + masterVLayout->addWidget(webView); + masterVLayout->addLayout(closeLayout); + masterVLayout->setContentsMargins(0,0,0,0); + + this->setLayout(masterVLayout); + this->setWindowTitle("UCC Differencing Side-by-side Results"); + this->resize(811, 497); + + //Initialize data from the passed in arguments + if (!filePathA.isEmpty()) + { + lineEdit->setText(filePathA); + lineEdit->setEnabled(true); + } + + if (!filePathB.isEmpty()) + { + lineEdit_2->setText(filePathB); + lineEdit_2->setEnabled(true); + } + + QString filename = htmlPath + "/" + HTML_FILE; + if (QFile(filename).exists()) + { + webView->load(QUrl::fromLocalFile(filename)); + } + else + { + qDebug() << "WARNING: " << filename << " does not exist!"; + } +} +#endif diff --git a/gui/GSideBySideDialog.h b/gui/GSideBySideDialog.h new file mode 100644 index 0000000..d2934ab --- /dev/null +++ b/gui/GSideBySideDialog.h @@ -0,0 +1,42 @@ +//! GSideBySideDialog class definitions. +/*! +* \file GSideBySideDialog.h +* +* This file contains the GSideBySideDialog class definition. +*/ +#ifndef GSIDEBYSIDEDIALOG_H +#define GSIDEBYSIDEDIALOG_H + +#ifndef NO_WEB_SUPPORT + +#include +/* Modification: 2016.01; USC + Updated to no longer require UI file, needed for dynamic + webkit vs webengine code */ + +#define HTML_FILE "highlighted_diff.html" + + +//! Sidebyside dialog. +/*! +* \class GSideBySideDialog +* +* Defines an Sidebyside dialog. +*/ +namespace Ui { +class GSideBySideDialog; +} + +class GSideBySideDialog : public QDialog +{ + Q_OBJECT + +public: +/* Modification: 2016.01; USC + Minor cosmetic updates */ + GSideBySideDialog(const QString &filePathA, const QString &filePathB, + const QString &htmlPath, QWidget *parent = 0); +}; + +#endif +#endif // GSIDEBYSIDEDIALOG_H diff --git a/gui/GTableDialog.cpp b/gui/GTableDialog.cpp new file mode 100644 index 0000000..c3e26cc --- /dev/null +++ b/gui/GTableDialog.cpp @@ -0,0 +1,138 @@ +//! GAsciiDialog class methods. +/*! +* \file GAsciiDialog.cpp +* +* This file contains the GAsciiDialog class methods. +*/ + +#include +#include +#include "GTableDialog.h" + +/*! +* Constructs a GTableDialog object. +* +* \param filePath results file or directory +* \param parent parent widget +* \param f window flags +*/ +GTableDialog::GTableDialog(const QString &filePath, QWidget *parent, Qt::WindowFlags f) + : QDialog(parent, f) +{ + ui.setupUi(this); + + outputDir = ""; + ui.cboResultsSelect->clear(); + ui.tblResults->clear(); + + if (!filePath.isEmpty()) + { + QFileInfo fi(filePath); + if (fi.isDir()) + { + outputDir = filePath; + QDir dir(filePath); + dir.setNameFilters(QStringList() << tr("*outfile*.csv") << tr("MatchedPairs.csv") << tr("*DuplicatePairs.csv")); + QStringList fileList = dir.entryList(); + for (int i = 0; i < fileList.count(); i++) + ui.cboResultsSelect->addItem(fileList.at(i)); + } + else if (fi.isFile()) + { + outputDir = fi.absolutePath(); + ui.cboResultsSelect->addItem(fi.fileName()); + } + } +} + +/*! +* Selects a file to display. +* +* \param fileName file name +*/ +void GTableDialog::on_cboResultsSelect_currentIndexChanged(const QString &fileName) +{ + ui.tblResults->clear(); + ui.tblResults->setColumnCount(0); + ui.tblResults->setRowCount(0); + QFile file(outputDir + "/" + fileName); + if (file.open(QIODevice::ReadOnly)) + { + QTextStream ts(&file); + QTableWidgetItem *twItem; + QString line; + int i = -1, j, k, n; + while (ts.status() == QTextStream::Ok && !ts.atEnd()) + { + line = ts.readLine(); + if (!line.isEmpty()) + { + i++; + ui.tblResults->insertRow(i); + QStringList s; + + /* + * In some cases, we want to display quotes or commas in CSV file. + * To do that, we need to quote the whole line. + * In this if condition, we parse this kind of text and display it in one column + */ + if (line.at(0) == QChar('"') && line.endsWith(QChar('"'))) + { + line = line.mid(1, line.size() - 2); + QString temp = ""; + for (int it1 = 0; it1 < line.size(); it1++) + { + temp.append(line.at(it1)); + /* + * The quote character we want to display in CSV has been replace with two quote characters ("") + * So here we need to skip one quote character + */ + if (line.at(it1) == QChar('"') && it1 + 1 < line.size() && line.at(it1 + 1) == QChar('"')) + it1++; + } + s.append(temp); + } + else s = line.split(','); + if (s.count() > ui.tblResults->columnCount()) + ui.tblResults->setColumnCount(s.count()); + for (j = 0; j < s.count(); j++) + { + twItem = new QTableWidgetItem(s.value(j)); + ui.tblResults->setItem(i, j, twItem); + if (s.value(j).length() > 15 && j == s.count() - 1) + ui.tblResults->setSpan(i, j, 1, 2); + else if (j < s.count() - 1 && s.value(j + 1).isEmpty()) + { + n = 2; + for (k = j + 1; k < s.count() - 1; k++) + { + if (!s.value(k + 1).isEmpty()) + break; + n++; + } + ui.tblResults->setSpan(i, j, 1, n); + } + } + } + } + + // update span for title rows + for (i = 0; i < ui.tblResults->rowCount() - 1; i++) + { + QString text = ui.tblResults->item(i, 0)->text(); + if (ui.tblResults->item(i, 0)->text().startsWith(" ")) + { + for (j = 0; j < ui.tblResults->columnCount(); j++) + { + if (!ui.tblResults->item(i + 1, j) || ui.tblResults->item(i + 1, j)->text().isEmpty()) + break; + n = ui.tblResults->columnSpan(i + 1, j); + if (n > 1) + j += n - 1; + } + if (j < ui.tblResults->columnCount()) + ui.tblResults->setSpan(i, 0, 1, j); + } + } + } +} diff --git a/gui/GTableDialog.h b/gui/GTableDialog.h new file mode 100644 index 0000000..3875162 --- /dev/null +++ b/gui/GTableDialog.h @@ -0,0 +1,37 @@ +//! GTableDialog class definitions. +/*! +* \file GTableDialog.h +* +* This file contains the GTableDialog class definition. +*/ + +#ifndef GTABLEDIALOG_H +#define GTABLEDIALOG_H + +#include +#include "ui_GTableDialog.h" + +//! Table dialog. +/*! +* \class GTableDialog +* +* Defines a table dialog. +*/ +class GTableDialog : public QDialog +{ + Q_OBJECT + +public: + GTableDialog(const QString &filePath, QWidget *parent = 0, Qt::WindowFlags f = 0); + ~GTableDialog(){}; + +private slots: + void on_cboResultsSelect_currentIndexChanged(const QString &fileName); + +private: + Ui::GTableDialogClass ui; + + QString outputDir; //!< Output directory defined by filePath +}; + +#endif // GTABLEDIALOG_H diff --git a/gui/GTableDialog.ui b/gui/GTableDialog.ui new file mode 100644 index 0000000..ca8c4e7 --- /dev/null +++ b/gui/GTableDialog.ui @@ -0,0 +1,97 @@ + + + GTableDialogClass + + + + 0 + 0 + 800 + 500 + + + + UCC Results + + + + :/images/gucc.png:/images/gucc.png + + + true + + + + + + + + + QAbstractItemView::SingleSelection + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Close + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + btnClose + clicked() + GTableDialogClass + accept() + + + 430 + 476 + + + 506 + 469 + + + + + + closeWin() + + diff --git a/gui/GUCC.ico b/gui/GUCC.ico new file mode 100644 index 0000000..323a6f4 Binary files /dev/null and b/gui/GUCC.ico differ diff --git a/gui/GUCC.rc b/gui/GUCC.rc new file mode 100644 index 0000000..1d11fa7 --- /dev/null +++ b/gui/GUCC.rc @@ -0,0 +1,2 @@ +IDI_ICON1 ICON DISCARDABLE "gucc.ico" + diff --git a/gui/GUtil.cpp b/gui/GUtil.cpp new file mode 100644 index 0000000..defa4cc --- /dev/null +++ b/gui/GUtil.cpp @@ -0,0 +1,42 @@ +//! GUtil class static methods. +/*! +* \file GUtil.h +* +* This file contains the GUtil class static methods. +*/ + +#include +#include "GUtil.h" + +/*! +* Opens the specified file using the default application. +* +* \param filePath file path to open +* +* \return status +*/ +bool GUtil::openFile(const QString &filePath) +{ + QString appl; + QStringList argList; + +#if defined(Q_OS_MACX) + appl = "open"; +#elif defined(Q_OS_WIN) + appl = "cmd"; + argList << "/C"; +#else + return(false); +#endif + argList << filePath; + + // create external application process + QProcess *proc = new QProcess(0); + + // launch process + proc->start(appl, argList); + if (! proc->waitForStarted()) + return(false); + else + return(true); +} diff --git a/gui/GUtil.h b/gui/GUtil.h new file mode 100644 index 0000000..096fe3c --- /dev/null +++ b/gui/GUtil.h @@ -0,0 +1,25 @@ +//! GUtil class definitions. +/*! +* \file GUtil.h +* +* This file contains the GUtil class definition. +*/ + +#ifndef GUTIL_H +#define GUTIL_H + +#include + +//! Utility class. +/*! +* \class GUtil +* +* Defines a utility class. +*/ +class GUtil +{ +public: + static bool openFile(const QString &filePath); +}; + +#endif // GUTIL_H diff --git a/gui/Qt_main.cpp b/gui/Qt_main.cpp new file mode 100644 index 0000000..4698f47 --- /dev/null +++ b/gui/Qt_main.cpp @@ -0,0 +1,25 @@ +//! The main GUCC function. +/*! +* \file Qt_main.cpp +* +* This file contains the main GUCC function. +*/ + +#include +#include "GMainWindow.h" + +/*! +* Main function. +* +* \param argc number of arguments +* \param argv argument list +* +* \return function status +*/ +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + GMainWindow w; + w.show(); + return a.exec(); +} diff --git a/gui/UCCWorker.cpp b/gui/UCCWorker.cpp new file mode 100644 index 0000000..1213f1d --- /dev/null +++ b/gui/UCCWorker.cpp @@ -0,0 +1,60 @@ + +//! Qt Object interface to rest of UCC work to allow Qt Thread use +/*! +* \file UCCWorker.cpp +* +* This file encapsulates Qt implementation details and dependencies +* so that the rest of UCC is relatively unchanged. +* +* ADDED to UCC 2015_12 release by Randy Maxwell +* Changes started on 2015_12_03 +* Changes ended on 2015_12_05 +* Addition of Multithreading performance enhancement feature +* Example: UCC -threads 4 (to have 4 worker threads) +*/ + +#include "../src/UCCThread.h" + +#ifdef ENABLE_THREADS + +#include +#include "UCCWorker.h" + +// See UCCThread.cpp +extern void prv_WorkThread_Function( const unsigned int thr_array_idx ); + +void WorkerThread::run() +{ + // Implement run to only call UCC + // to prevent any Event loops from running + prv_WorkThread_Function( thread_array_idx ); +} + + +// Constructor to call to base class constructor +UCCWorker::UCCWorker() +{} + +// Destructor to call to base class destructor +UCCWorker::~UCCWorker() +{} + +// Provide a Qt Object wrapper +WorkerThread * UCCWorker::startWorkInAThread( const unsigned int thread_idx ) +{ + // Call into the thread procedure. + // This will run as long as needed. + WorkerThread *pWorkerThread = new WorkerThread; + + pWorkerThread->thread_array_idx = thread_idx; + + connect( pWorkerThread, SIGNAL( finished() ), + pWorkerThread, SLOT( deleteLater() ) ); + + // Start Qt Thread which will Call UCC worker thread proc + pWorkerThread->start(); + + return pWorkerThread; +} + +#endif diff --git a/gui/UCCWorker.h b/gui/UCCWorker.h new file mode 100644 index 0000000..ef784cc --- /dev/null +++ b/gui/UCCWorker.h @@ -0,0 +1,50 @@ +//! Qt Object interface to rest of UCC work to allow Qt Thread use +/*! +* \file UCCWorker.h +* +* This file encapsulates Qt Thread implementation details and dependencies +* so that the rest of UCC is relatively unchanged. +* +* ADDED to UCC 2015_12 release by Randy Maxwell +* Changes started on 2015_12_03 +* Changes ended on 2015_12_03 +* Addition of Multithreading performance enhancement feature +* Example: UCC -threads 4 (to have 4 worker threads) +*/ + +#ifndef UCCWORKER_H +#define UCCWORKER_H + +#include "../src/UCCThread.h" + +#ifdef ENABLE_THREADS + +#include +#include + +// Declare a class derived from the QThread +class WorkerThread : public QThread { + + // Declare here + void run(); + +public: + unsigned int thread_array_idx; +}; + +// QT Object +class UCCWorker : public QObject +{ + +public: + UCCWorker(); + ~UCCWorker(); + + // Interface to do UCC processing + WorkerThread * startWorkInAThread( const unsigned int thread_idx ); +}; + +#endif // ENABLE_THREADS + +#endif // UCCWORKER_H + diff --git a/gui/gucc.pro b/gui/gucc.pro new file mode 100644 index 0000000..0e277ba --- /dev/null +++ b/gui/gucc.pro @@ -0,0 +1,189 @@ +# Modification: 2016.01; USC +# Updated included modules to support Qt 5.7 +QT += core gui +greaterThan(QT_MAJOR_VERSION, 4) { + QT += widgets + qtHaveModule(webengine) { + QT += webenginewidgets + } else { + qtHaveModule(webkit) { + DEFINES += NEED_WEBKIT_LEGACY + QT += webkitwidgets + } else { + DEFINES += NO_WEB_SUPPORT + warning(Compiling with no web support. Visual differencing integration will be disabled!) + } + } +} else { + DEFINES += NEED_WEBKIT_LEGACY + QT += webkit +} + +CONFIG += qt warn_on debug_and_release no_batch +# Modification: 2016.01; USC +# Fixed mingw 4.9.1 compilation errors +CONFIG += c++11 + +TEMPLATE = app +LANGUAGE = C++ + +DEFINES += QTGUI + +HEADERS += \ + GAsciiDialog.h \ + GExtensionDialog.h \ + GMainWindow.h \ + GSideBySideDialog.h \ + GTableDialog.h \ + GUtil.h\ + ../src/CAdaCounter.h \ + ../src/CBashCounter.h \ + ../src/CBatchCounter.h \ + ../src/cc_main.h \ + ../src/CCCounter.h \ + ../src/CCFScriptCounter.h \ + ../src/CCJavaCsScalaCounter.h \ + ../src/CCodeCounter.h \ + ../src/CColdFusionCounter.h \ + ../src/CCsharpCounter.h \ + ../src/CCshCounter.h \ + ../src/CCssCounter.h \ + ../src/CDataCounter.h \ + ../src/CFortranCounter.h \ + ../src/CHtmlCounter.h \ + ../src/CJavaCounter.h \ + ../src/CJavascriptCounter.h \ + ../src/CMakefileCounter.h \ + ../src/CMatlabCounter.h \ + ../src/CMidasCounter.h \ + ../src/CmpMngr.h \ + ../src/CNeXtMidasCounter.h \ + ../src/CObjCCounter.h \ + ../src/CPascalCounter.h \ + ../src/CPerlCounter.h \ + ../src/CPhpCounter.h \ + ../src/CPythonCounter.h \ + ../src/CRubyCounter.h \ + ../src/CSqlCounter.h \ + ../src/CTagCounter.h \ + ../src/CUtil.h \ + ../src/CVbCounter.h \ + ../src/CVbscriptCounter.h \ + ../src/CVerilogCounter.h \ + ../src/CVHDLCounter.h \ + ../src/CWebCounter.h \ + ../src/CXMidasCounter.h \ + ../src/CXmlCounter.h \ + ../src/DiffTool.h \ + ../src/MainObject.h \ + ../src/UserIF.h \ + ../src/CmpMngrHtml.h \ + ../src/CAssemblyCounter.h \ + ../src/CIdlCounter.h \ + ../src/UCCThread.h \ + ../src/UCCGlobals.h \ + ../src/UCCFilesOut.h \ + ../src/UCCExceptDump.h \ + ../src/sema.h \ + ../src/LangUtils.h \ + ../src/CScalaCounter.h \ + ../src/CCobolCounter.h \ + ../src/UCCAfterLibraryIncludes.h \ + ../src/UCCBeforeLibraryIncludes.h \ + # Modification: 2017.02 + ../src/FunctionParser.h \ + UCCWorker.h + +SOURCES += \ + GAsciiDialog.cpp \ + GExtensionDialog.cpp \ + GMainWindow.cpp \ + GSideBySideDialog.cpp \ + GTableDialog.cpp \ + GUtil.cpp\ + ../src/CAdaCounter.cpp \ + ../src/CBashCounter.cpp \ + ../src/CBatchCounter.cpp \ + ../src/cc_main.cpp \ + ../src/CCCounter.cpp \ + ../src/CCFScriptCounter.cpp \ + ../src/CCJavaCsScalaCounter.cpp \ + ../src/CCodeCounter.cpp \ + ../src/CColdFusionCounter.cpp \ + ../src/CCsharpCounter.cpp \ + ../src/CCshCounter.cpp \ + ../src/CCssCounter.cpp \ + ../src/CDataCounter.cpp \ + ../src/CFortranCounter.cpp \ + ../src/CHtmlCounter.cpp \ + ../src/CJavaCounter.cpp \ + ../src/CJavascriptCounter.cpp \ + ../src/CMakefileCounter.cpp \ + ../src/CMatlabCounter.cpp \ + ../src/CMidasCounter.cpp \ + ../src/CmpMngr.cpp \ + ../src/CNeXtMidasCounter.cpp \ + ../src/CObjCCounter.cpp \ + ../src/CPascalCounter.cpp \ + ../src/CPerlCounter.cpp \ + ../src/CPhpCounter.cpp \ + ../src/CPythonCounter.cpp \ + ../src/CRubyCounter.cpp \ + ../src/CSqlCounter.cpp \ + ../src/CTagCounter.cpp \ + ../src/CUtil.cpp \ + ../src/CVbCounter.cpp \ + ../src/CVbscriptCounter.cpp \ + ../src/CVerilogCounter.cpp \ + ../src/CVHDLCounter.cpp \ + ../src/CWebCounter.cpp \ + ../src/CXMidasCounter.cpp \ + ../src/CXmlCounter.cpp \ + ../src/DiffTool.cpp \ + ../src/MainObject.cpp \ + ../src/UserIF.cpp \ + ../src/CmpMngrHtml.cpp \ + ../src/CAssemblyCounter.cpp \ + ../src/CIdlCounter.cpp \ + ../src/UCCThread.cpp \ + ../src/UCCGlobals.cpp \ + ../src/UCCFilesOut.cpp \ + ../src/UCCExceptDump.cpp \ + ../src/LangUtils.cpp \ + ../src/CScalaCounter.cpp \ + ../src/CCobolCounter.cpp \ + ../src/main.cpp \ + # Modification: 2017.02 + ../src/FunctionParser.cpp \ + Qt_main.cpp \ + UCCWorker.cpp + +FORMS += \ + GAsciiDialog.ui \ + GExtensionDialog.ui \ + GMainWindow.ui \ + GTableDialog.ui + +RESOURCES += \ + gucc.qrc + +win32 { + TARGET = GUCC + UI_DIR = GeneratedFiles + RCC_DIR = GeneratedFiles + RC_FILE = GUCC.rc + #Modification: 2016.01; USC + #Fixed mingw 4.9.1 compilation errors + DEFINES += WIN32 +} +unix { + UI_DIR = .ui + MOC_DIR = .moc + OBJECTS_DIR = .obj + DEFINES += UNIX +} +macx { + TARGET = GUCC + RC_FILE = images/gucc.icns + DEFINES += UNIX +} diff --git a/gui/gucc.qrc b/gui/gucc.qrc new file mode 100644 index 0000000..e9cc882 --- /dev/null +++ b/gui/gucc.qrc @@ -0,0 +1,15 @@ + + + images/add_folder.png + images/arrow.png + images/close.png + images/edit.png + images/gucc.png + images/help.png + images/minus_sign.png + images/open.png + images/plus_sign.png + images/start.png + images/stop.png + + diff --git a/gui/images/add_folder.png b/gui/images/add_folder.png new file mode 100644 index 0000000..f5232f9 Binary files /dev/null and b/gui/images/add_folder.png differ diff --git a/gui/images/arrow.png b/gui/images/arrow.png new file mode 100644 index 0000000..01f1d67 Binary files /dev/null and b/gui/images/arrow.png differ diff --git a/gui/images/close.png b/gui/images/close.png new file mode 100644 index 0000000..c5483d1 Binary files /dev/null and b/gui/images/close.png differ diff --git a/gui/images/edit.png b/gui/images/edit.png new file mode 100644 index 0000000..dccd9f2 Binary files /dev/null and b/gui/images/edit.png differ diff --git a/gui/images/gucc.icns b/gui/images/gucc.icns new file mode 100644 index 0000000..2b0af4b Binary files /dev/null and b/gui/images/gucc.icns differ diff --git a/gui/images/gucc.png b/gui/images/gucc.png new file mode 100644 index 0000000..37ec9d4 Binary files /dev/null and b/gui/images/gucc.png differ diff --git a/gui/images/help.png b/gui/images/help.png new file mode 100644 index 0000000..310056a Binary files /dev/null and b/gui/images/help.png differ diff --git a/gui/images/minus_sign.png b/gui/images/minus_sign.png new file mode 100644 index 0000000..26d2147 Binary files /dev/null and b/gui/images/minus_sign.png differ diff --git a/gui/images/open.png b/gui/images/open.png new file mode 100644 index 0000000..33e0d63 Binary files /dev/null and b/gui/images/open.png differ diff --git a/gui/images/plus_sign.png b/gui/images/plus_sign.png new file mode 100644 index 0000000..d572b8f Binary files /dev/null and b/gui/images/plus_sign.png differ diff --git a/gui/images/start.png b/gui/images/start.png new file mode 100644 index 0000000..e355ea9 Binary files /dev/null and b/gui/images/start.png differ diff --git a/gui/images/stop.png b/gui/images/stop.png new file mode 100644 index 0000000..52e593a Binary files /dev/null and b/gui/images/stop.png differ diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..c0195c7 --- /dev/null +++ b/license.txt @@ -0,0 +1,208 @@ + UCC LICENSE + + COPYRIGHT (C) 2006 - 2018 + Center for Systems and Software Engineering + University of Southern California + Salvatori 330, 941 West 37th Place + Los Angeles, California 90089-0781, USA + +This CodeCount program is free software with limitations; you can redistribute it and/or +modify it under the terms of the USC-CSSE Limited Public License as published by the +University of Southern California Center for Systems and Software Engineering; either version 1 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +------------------------------------------------------------------------------------------- +USC-CSSE LIMITED PUBLIC LICENSE TERMS AND CONDITIONS +------------------------------------------------------------------------------------------- +The precise terms and conditions for copying, distribution and modification are as follows. + +0. This License applies to any program or other work which contains a notice placed by the +copyright holder saying it may be distributed under the terms of this Limited Public +License. The "Program", below, refers to any such program or work, and a "work based on the +Program" means either the Program or any derivative work under copyright law: that is to +say, a work containing the Program or a portion of it, either verbatim or with +modifications and/or translated into another language. (Hereinafter, translation is +included without limitation in the term "modification".) Each licensee is addressed as +"you". + +Activities other than copying, distribution and modification are not covered by this +License; they are outside its scope. The act of running the Program is not restricted nor +is the output from the Program is covered. + +1. You may copy and distribute verbatim copies of the Program's source code as you receive +it, in any medium, provided that you conspicuously and appropriately publish on each copy +an appropriate copyright notice and disclaimer of warranty; keep intact all the notices +that refer to this License and to the absence of any warranty; and give any other +recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may at your +option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus forming a +work based on the Program, and copy and distribute such modifications or work under the +terms of Section 1 above, provided that you also meet all of these conditions: + +2.a) You must cause the modified files to carry prominent notices stating that you changed +the files and the date of any change. + +2.b) You must cause any work that you distribute or publish, that in whole or in part +contains or is derived from the Program or any part thereof, to be licensed as a whole at +no charge to all third parties under the terms of this License. + +2.c) If the modified program normally reads commands interactively when run, you must cause +it, when started running for such interactive use in the most ordinary way, to print or +display an announcement including an appropriate copyright notice and a notice that there +is no warranty (or else, saying that you provide a warranty) and that users may +redistribute the program under these conditions, and telling the user how to view a copy of +this License. (Exception: if the Program itself is interactive but does not normally print +such an announcement, your work based on the Program is not required to print an +announcement.) + +2.d) You must provide a copy of the modified machine-readable source code files to the +University of Southern California Center for Systems and Software Engineering Salvatori 330, +941 West 37th Place, Los Angeles, California 90089-0781, USA, on an appropriate medium or +via our contact information at http://csse.usc.edu. + +These requirements apply to the modified work as a whole. If identifiable sections of that +work are not derived from the Program, and can be reasonably considered independent and +separate works in themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you distribute the same +sections as part of a whole which is a work based on the Program, the distribution of the +whole must be on the terms of this License, whose permissions for other licensees extend to +the entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to work +written entirely by you; rather, the intent is to exercise the right to control the +distribution of derivative or collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program with the Program (or +with a work based on the Program) on a volume of a storage or distribution medium does not +bring the other work under the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under Section 2) in +object code or executable form under the terms of Sections 1 and 2 above provided that you +also do one of the following: + +3.a) Accompany it with the complete corresponding machine-readable source code, which must +be distributed under the terms of Sections 1 and 2 above on a medium customarily used for +software interchange; or, + +3.b) Accompany it with a written offer, valid for at least three years, to give any third +party, for a charge no more than your cost of physically performing source distribution, a +complete machine-readable copy of the corresponding source code, to be distributed under +the terms of Sections 1 and 2 above on a medium customarily used for software interchange; +or, + +3.c) Accompany it with the information you received as to the offer to distribute +corresponding source code. (This alternative is allowed only for noncommercial distribution +and only if you received the program in object code or executable form with such an offer, +in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for making modifications to +it. For an executable work, complete source code means all the source code for all modules +it contains, plus any associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a special exception, +the source code distributed need not include anything that is normally distributed (in +either source or binary form) with the major components (compiler, kernel, and so on) of +the operating system on which the executable runs, unless that component itself accompanies +the executable. + +If distribution of executable or object code is made by offering access to copy from a +designated place, then offering equivalent access to copy the source code from the same +place counts as distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as expressly +provided under this License. Any attempt otherwise to copy, modify, sublicense or +distribute the Program is void, and will automatically terminate your rights under this +License. However, parties who have received copies, or rights, from you under this License +will not have their licenses terminated so long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. However, +nothing else grants you permission to modify or distribute the Program or its derivative +works. These actions are prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the Program), you indicate your +acceptance of this License to do so, and all its terms and conditions for copying, +distributing or modifying the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), the recipient +automatically receives a license from the original licensor to copy, distribute or modify +the Program subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. You are not +responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent infringement or for any +other reason (not limited to patent issues), conditions are imposed on you (whether by court +order, agreement or otherwise) that contradict the conditions of this License, they +do not excuse you from the conditions of this License. If you cannot distribute so as to +satisfy simultaneously your obligations under this License and any other pertinent +obligations, then as a consequence you may not distribute the Program at all. For example, +if a patent license would not permit royalty-free redistribution of the Program by all +those who receive copies directly or indirectly through you, then the only way you could +satisfy both it and this License would be to refrain entirely from distribution of the +Program. + +If any portion of this section is held invalid or unenforceable under any particular +circumstance, the balance of the section is intended to apply and the section as a whole is +intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other +property right claims or to contest validity of any such claims; this section has the sole +purpose of protecting the integrity of this free software with limitations distribution +system, which is implemented by limited public license practices. Many people have made +generous contributions to the wide range of software distributed through that system in +reliance on consistent application of that system; it is up to the author/donor to decide +if he or she is willing to distribute software through any other system and a licensee +cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of +the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain countries either +by patents or by copyrighted interfaces, the original copyright holder who places the +Program under this License may add an explicit geographical distribution limitation +excluding those countries, so that distribution is permitted only in or among countries not +thus excluded. In such case, this License incorporates the limitation as if written in the +body of this License. + +9. The University of Southern California Center for Systems and Software Engineering may publish +revised and/or new versions of this Limited Public License from time to time. Such new +versions will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies a version +number of this License which applies to it and "any later version", you have the option of +following the terms and conditions either of that version or of any later version published +by the University of Southern California Center for Systems and Software Engineering. If the Program +does not specify a version number of this License, you may choose any version ever +published by the University of Southern California Center for Systems and Software Engineering. + +10. If you wish to incorporate parts of the Program into other free programs whose +distribution conditions are different, write to the author to ask for permission. For +software which is copyrighted by the University of Southern California Center for Software +Engineering, write to the University of Southern California Center for Systems and Software Engineering; +we sometimes make exceptions for this. Our decision will be guided by the goal of preserving +the free status of all derivatives of our free software with limitations. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO +THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE +COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE +COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT +HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, +BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL +DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO +LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES +OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR +OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS diff --git a/src/BOOST_LICENSE_1_0.txt b/src/BOOST_LICENSE_1_0.txt new file mode 100644 index 0000000..36b7cd9 --- /dev/null +++ b/src/BOOST_LICENSE_1_0.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/src/CAdaCounter.cpp b/src/CAdaCounter.cpp new file mode 100644 index 0000000..2a14866 --- /dev/null +++ b/src/CAdaCounter.cpp @@ -0,0 +1,783 @@ +//! Code counter class methods for the Ada language. +/*! +* \file CAdaCounter.cpp +* +* This file contains the code counter class methods for the Ada language. +*/ + +#include "CAdaCounter.h" + +/*! +* Constructs a CAdaCounter object. +*/ +CAdaCounter::CAdaCounter() +{ + classtype = ADA; + language_name = "Ada"; + casesensitive = false; + + //Modification: 11.2016 Ext-4 starts + file_extension = CUtil::getExtensionsToLanguage("Ada", file_extension); + /* + file_extension.push_back(".ada"); + file_extension.push_back(".a"); + file_extension.push_back(".adb"); + file_extension.push_back(".ads");*/ + //Modification: 11.2016 Ext-4 + + LineCommentStart.push_back("--"); + + QuoteStart = "\""; + QuoteEnd = "\""; + QuoteEscapeRear = '\"'; + + directive.push_back("controlled"); + directive.push_back("elaborate"); + directive.push_back("inline"); + directive.push_back("interface"); + directive.push_back("list"); + directive.push_back("memory_size"); + directive.push_back("optimize"); + directive.push_back("pack"); + directive.push_back("page"); + directive.push_back("pragma"); + directive.push_back("priority"); + directive.push_back("shared"); + directive.push_back("storage_unit"); + directive.push_back("suppress"); + directive.push_back("system_name"); + + data_name_list.push_back("access"); + data_name_list.push_back("array"); + data_name_list.push_back("body"); + data_name_list.push_back("constant"); + data_name_list.push_back("declare"); + data_name_list.push_back("function"); + data_name_list.push_back("generic"); + data_name_list.push_back("limited"); + data_name_list.push_back("new"); + data_name_list.push_back("package"); + data_name_list.push_back("private"); + data_name_list.push_back("procedure"); + data_name_list.push_back("record"); + data_name_list.push_back("renames"); + data_name_list.push_back("separate"); + data_name_list.push_back("subtype"); + data_name_list.push_back("task"); + data_name_list.push_back("type"); + data_name_list.push_back("use"); + data_name_list.push_back("with"); + + exec_name_list.push_back("abort"); + exec_name_list.push_back("accept"); + exec_name_list.push_back("begin"); + exec_name_list.push_back("case"); + exec_name_list.push_back("delay"); + exec_name_list.push_back("else"); + exec_name_list.push_back("elsif"); + exec_name_list.push_back("end"); + exec_name_list.push_back("entry"); + exec_name_list.push_back("exception"); + exec_name_list.push_back("exit"); + exec_name_list.push_back("goto"); + exec_name_list.push_back("if"); + exec_name_list.push_back("loop"); + exec_name_list.push_back("others"); + exec_name_list.push_back("raise"); + exec_name_list.push_back("return"); + exec_name_list.push_back("select"); + exec_name_list.push_back("terminate"); + exec_name_list.push_back("when"); + + math_func_list.push_back("exp"); + math_func_list.push_back("random"); + math_func_list.push_back("sqrt"); + + trig_func_list.push_back("arccos"); + trig_func_list.push_back("arccosh"); + trig_func_list.push_back("arccot"); + trig_func_list.push_back("arccoth"); + trig_func_list.push_back("arcsin"); + trig_func_list.push_back("arcsinh"); + trig_func_list.push_back("arctan"); + trig_func_list.push_back("arctanh"); + trig_func_list.push_back("cos"); + trig_func_list.push_back("cosh"); + trig_func_list.push_back("cot"); + trig_func_list.push_back("coth"); + trig_func_list.push_back("sin"); + trig_func_list.push_back("sinh"); + trig_func_list.push_back("tan"); + trig_func_list.push_back("tanh"); + + log_func_list.push_back("log"); + + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("/"); + cmplx_calc_list.push_back("mod"); + cmplx_calc_list.push_back("rem"); + + cmplx_cond_list.push_back("case"); + cmplx_cond_list.push_back("else"); + cmplx_cond_list.push_back("elsif"); + cmplx_cond_list.push_back("if"); + cmplx_cond_list.push_back("loop"); + cmplx_cond_list.push_back("when"); + + cmplx_logic_list.push_back("="); + cmplx_logic_list.push_back("<"); + cmplx_logic_list.push_back(">"); + cmplx_logic_list.push_back("/="); + cmplx_logic_list.push_back(">="); + cmplx_logic_list.push_back("<="); + cmplx_logic_list.push_back("&"); + cmplx_logic_list.push_back("and"); + cmplx_logic_list.push_back("or"); + cmplx_logic_list.push_back("xor"); + cmplx_logic_list.push_back("in"); + cmplx_logic_list.push_back("not in"); + + cmplx_preproc_list.push_back("controlled"); + cmplx_preproc_list.push_back("elaborate"); + cmplx_preproc_list.push_back("inline"); + cmplx_preproc_list.push_back("interface"); + cmplx_preproc_list.push_back("list"); + cmplx_preproc_list.push_back("memory_size"); + cmplx_preproc_list.push_back("optimize"); + cmplx_preproc_list.push_back("pack"); + cmplx_preproc_list.push_back("page"); + cmplx_preproc_list.push_back("pragma"); + cmplx_preproc_list.push_back("priority"); + cmplx_preproc_list.push_back("shared"); + cmplx_preproc_list.push_back("storage_unit"); + cmplx_preproc_list.push_back("suppress"); + cmplx_preproc_list.push_back("system_name"); + + cmplx_assign_list.push_back(":="); + + //cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("elsif"); + //cmplx_cyclomatic_list.push_back("IIf"); + cmplx_cyclomatic_list.push_back("for"); + cmplx_cyclomatic_list.push_back("while"); + //cmplx_cyclomatic_list.push_back("Until"); + //cmplx_cyclomatic_list.push_back("Catch"); + cmplx_cyclomatic_list.push_back("when"); + //cmplx_cyclomatic_list.push_back("case"); + + ignore_cmplx_cyclomatic_list.push_back("end if"); + + //cmplx_cyclomatic_logic_list.push_back("&"); + cmplx_cyclomatic_logic_list.push_back("and"); + cmplx_cyclomatic_logic_list.push_back("or"); + //cmplx_cyclomatic_logic_list.push_back("xor"); + + cmplx_cyclomatic_case_list.push_back("when"); + cmplx_cyclomatic_switch_list.push_back("end case"); + cmplx_cyclomatic_default_list.push_back("others"); +} + +/*! +* Replaces quoted strings inside a string starting at idx_start with '$'. +* Handles special cases for Ada literal strings. +* +* \param strline string to be processed +* \param idx_start index of line character to start search +* \param contd specifies the quote string is continued from the previous line +* \param CurrentQuoteEnd end quote character of the current status +* +* \return method status +*/ +int CAdaCounter::ReplaceQuote(string &strline, size_t &idx_start, bool &contd, char &CurrentQuoteEnd) +{ + size_t idx = 0; + bool done = false; + while( ! done ) + { + idx = strline.find("'\"'", idx); // replace all '"' by '$' + if (idx != string::npos) + strline.replace(idx, 3, 3, '$'); + else + break; + } + return CCodeCounter::ReplaceQuote(strline, idx_start, contd, CurrentQuoteEnd); +} + +/*! +* Counts directive lines of code. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CAdaCounter::CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak) +{ + bool contd = false, trunc_flag = false; + size_t idx, strSize; + unsigned int cnt = 0; + string strDirLine = ""; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$:"; + + filemap::iterator itfmBak = fmapBak->begin(); + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++, itfmBak++) + { + if (CUtil::CheckBlank(iter->line)) + continue; + size_t lineNumber = iter->lineNumber; + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(" " + iter->line, directive, cnt, 1, exclude, "", "", &result->directive_count, false); + } + + if (!contd) + { + // if not a continuation of a previous directive + for (vector::iterator viter = directive.begin(); viter != directive.end(); viter++) + { + // merged bug fix for considering only stand-alone keywords + // e.g. package should not be considered a directive (only 'pack' is) + if (((idx = CUtil::FindKeyword(iter->line, *viter, 0, TO_END_OF_STRING, false)) != string::npos) && idx == 0) + { + contd = true; + break; + } + } + if (contd) + { + strSize = CUtil::TruncateLine(itfmBak->line.length(), 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine = itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + } + else + { + // continuation of a previous directive + strSize = CUtil::TruncateLine(itfmBak->line.length(), strDirLine.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine += "\n" + itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + + if (contd) + { + // drop continuation symbol + if (strDirLine[strDirLine.length()-1] == '\\') + strDirLine = strDirLine.substr(0, strDirLine.length()-1); + + // if a directive or continuation of a directive (no continuation symbol found) + if (iter->line[iter->line.length()-1] != ',' && iter->line[iter->line.length()-1] != '\\') + { + contd = false; + if (result->addSLOC(strDirLine, lineNumber, trunc_flag)) + result->directive_lines[LOG]++; + } + iter->line = ""; + } + } + return 1; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CAdaCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + bool found_accept = false; + string strLSLOC = ""; + string strLSLOCBak = ""; + unsigned int cnt = 0; + unsigned int loopLevel = 0; + + filemap::iterator fit, fitbak; + string line, lineBak, tmp; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$:"; + + unsigned int l_paren_cnt = 0; + bool l_foundblock, found_forifwhile, found_end, found_type, found_is; + l_foundblock = found_forifwhile = found_end = found_is = false; + + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + // insert blank at the beginning (for searching keywords) + line = ' ' + fit->line; + lineBak = ' ' + fitbak->line; + + if (!CUtil::CheckBlank(line)) + { + // blank line means blank_line/comment_line/directive + // call SLOC function to detect logical SLOC and add to result + LSLOC(result, line, fit->lineNumber, lineBak, strLSLOC, strLSLOCBak, l_paren_cnt, l_foundblock, + found_forifwhile, found_end, found_type, found_is, found_accept, loopLevel); + + cnt = 0; + CUtil::CountTally(line, data_name_list, cnt, 1, exclude, "", "", NULL, false); + + // need to check also if the data line continues + if (cnt > 0) + result->data_lines[PHY]++; + else + result->exec_lines[PHY]++; + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count, false); + } + } + } + return 1; +} + +/*! +* Processes a logical line of code. +* This method is called after a logical SLOC is determined. +* The method adds LSLOC to the result, increases counts, and resets variables. +* +* \param result counter results +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param found_block found block flag +* \param found_forifwhile found for, if, or while flag +* \param found_end found end flag +* \param found_type found type flag +* \param found_is found is flag +* \param found_accept found accept flag +* \param trunc_flag truncate lines? +*/ +void CAdaCounter::FoundSLOC(results* result, size_t lineNumber, string &strLSLOC, string &strLSLOCBak, bool &found_block, bool &found_forifwhile, + bool &found_end, bool &found_type, bool &found_is, bool &found_accept, bool &trunc_flag) +{ + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$:"; + + // add to the list for comparison purpose + if (result->addSLOC(CUtil::TrimString(strLSLOCBak), lineNumber, trunc_flag)) + { + // determine logical type, data declaration or executable + unsigned int cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", &result->data_name_count, false); + if (cnt > 0) + result->data_lines[LOG]++; + else + result->exec_lines[LOG]++; + } + + // reset all variables whenever a new statement/logical SLOC is found + strLSLOC = ""; + strLSLOCBak = ""; + found_block = false; + found_forifwhile = false; + found_end = false; + found_type = false; + found_is = false; + found_accept = false; +} + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results +* \param line processed physical line of code +* \param lineBak original physical line of code +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param paren_cnt count of parenthesis +* \param found_block found block flag +* \param found_forifwhile found for, if, or while flag +* \param found_end found end flag +* \param found_type found type flag +* \param found_is found is flag +* \param found_accept found accept flag +* \param loopLevel nested loop level +*/ +void CAdaCounter::LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + unsigned int &paren_cnt, bool &found_block, bool &found_forifwhile, bool &found_end, + bool &found_type, bool &found_is, bool &found_accept, unsigned int &loopLevel) +{ + size_t start = 0; //starting index of the working string + size_t i = 0, tempi, strSize; + string templine = CUtil::TrimString(line); + string tmp; + bool trunc_flag = false; + string keywordchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + // there may be more than 1 logical SLOC in a line + for (i = 0; i < line.length(); i++) + { + switch (line[i]) + { + case ';': + /*if (paren_cnt > 0) + break;*/ //this is not necessary + if (!found_end) + { + // check for empty statement (=1 LSLOC) + if (CUtil::TrimString(line.substr(start, i + 1 - start)) == ";" && strLSLOC.length() < 1) + { + strLSLOC = ";"; + strLSLOCBak = ";"; + } + else + { + strSize = CUtil::TruncateLine(i - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_accept, trunc_flag); + } + else + { + found_end = false; // end xxx + found_block = false; + found_is = false; + found_forifwhile = false; + found_type = false; + found_accept = false; + strLSLOC = ""; + strLSLOCBak = ""; + } + start = i + 1; + break; + case '(': + if (found_type) + found_type = false; + paren_cnt++; + break; + case ')': + if (paren_cnt > 0) + paren_cnt--; + break; + } + + // continue the following processing only if line[i] is not in a middle of a word + if (keywordchars.find(line[i]) != string::npos && i < line.length() - 1) + continue; + + // if it ends in xxx, then it has already been counted, so ignore it + tmp = "xxx " + CUtil::TrimString(line.substr(start, i + 1 - start)); + tempi = CUtil::FindKeyword(tmp, "end", 0, TO_END_OF_STRING, false); + if (tempi != string::npos) + { + found_end = true; + + // record end loop for nested loop processing + if (print_cmplx) + { + tmp = CUtil::TrimString(line.substr(start, i + 5 - start)); + if (CUtil::FindKeyword(tmp, "end loop", 0, TO_END_OF_STRING, false) != string::npos) + if (loopLevel > 0) + loopLevel--; + } + start = i + 1; + } + + if (!found_end) + { + if (!found_forifwhile) + { + if (CUtil::FindKeyword(tmp, "for", 0 , TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "while", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "if", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "elsif", 0, TO_END_OF_STRING, false) != string::npos) + { + found_forifwhile = true; + } + + // 'exception' is removed because it is not counted + if (CUtil::FindKeyword(tmp, "loop", 0, TO_END_OF_STRING, false) != string::npos) + { + // found a SLOC + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_accept, trunc_flag); + + start = i + 1; + + // record nested loop level + if (print_cmplx) + { + loopLevel++; + if ((unsigned int)result->cmplx_nestloop_count.size() < loopLevel) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopLevel-1]++; + } + continue; + } + } + else if (CUtil::FindKeyword(tmp, "loop", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "then", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "record", 0, TO_END_OF_STRING, false) != string::npos) // for..use..record + { + // found a SLOC + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_accept, trunc_flag); + start = i + 1; + + // record nested loop level + if (print_cmplx) + { + if (CUtil::FindKeyword(tmp, "loop", 0, TO_END_OF_STRING, false) != string::npos) + { + loopLevel++; + if ((unsigned int)result->cmplx_nestloop_count.size() < loopLevel) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopLevel-1]++; + } + } + continue; + } + + // similarly, check for procedure, task, function - it ends with 'is' keyword + // procedure ... is... + // package ... is ... + if (!found_block) + { + if (CUtil::FindKeyword(tmp, "procedure", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "function", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "package", 0, TO_END_OF_STRING, false) !=string::npos || + CUtil::FindKeyword(tmp, "task", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "case",0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "protected", 0, TO_END_OF_STRING, false) != string::npos) + { + found_block = true; + } + } + else // procedure...is... + { + // the 'if' statement below attempts to 'alleviate' the issue with + // procedure Swap is new Exchange(Elem => Integer); + // procedure Test1 is begin end Test1; + // only add new SLOC if 'is' is at the end of line and follows 'procedure', etc. + // NOTE: this implementation may not be complete + tempi = CUtil::FindKeyword(templine, "is", 0, TO_END_OF_STRING, false); + if (tempi == templine.length() - 2) + { + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_accept, trunc_flag); + start = i + 1; + found_is = true; + continue; + } + } + if (!found_type) + { + if (CUtil::FindKeyword(tmp, "type", 0, TO_END_OF_STRING, false) != string::npos) + found_type = true; + } + else + { + if (CUtil::FindKeyword(tmp, "record", 0, TO_END_OF_STRING, false) != string::npos) + { + // the 'if' statement below attempts to resolves the issue with + // type Expression is tagged null record; + // so, ignore this case. + // NOTE: this implementation may not be complete + if (templine.at(templine.length() - 1) != ';') + { + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_accept, trunc_flag); + start = i + 1; + continue; + } + } + } + + // process 'select...end select;', 'accept ... end accept;' + // 'record ... end record;' is handled via 'type' + // select ... end select; --> only one word statement 'select' + // accept id... do ... end [id]; --> SLOC starting from 'accept' to 'do' + // find 'do' only already found 'accept' + if (CUtil::FindKeyword(tmp, "select", 0, TO_END_OF_STRING, false) != string::npos) + { + // found 'select' statement, one SLOC + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_accept, trunc_flag); + start = i + 1; + continue; + } + + if (!found_accept) + { + if (CUtil::FindKeyword(tmp, "accept", 0, TO_END_OF_STRING, false) != string::npos) + found_accept = true; + } + else + { + if (CUtil::FindKeyword(tmp, "do", 0, TO_END_OF_STRING, false) != string::npos) + { + // found a SLOC + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_accept, trunc_flag); + start = i + 1; + } + } + } + } + + tmp = CUtil::TrimString(line.substr(start, i - start)); + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + tmp = CUtil::TrimString(lineBak.substr(start, i - start)); + strLSLOCBak += tmp.substr(0, strSize); + + // drop continuation symbol + if (strLSLOC[strLSLOC.length()-1] == '\\') + { + strLSLOC = strLSLOC.substr(0, strLSLOC.length()-1); + strLSLOCBak = strLSLOCBak.substr(0, strLSLOCBak.length()-1); + } + } + if (tmp == "") + found_forifwhile = false; +} + +/*! +* Parses lines for function/method names. +* +* \param line line to be processed +* \param lastline last line processed +* \param functionStack stack of functions +* \param functionName function name found +* \param functionCount function count found +* +* \return 1 if function name is found +*/ +int CAdaCounter::ParseFunctionName(const string &line, string &/*lastline*/, + filemap &functionStack, string &functionName, unsigned int &functionCount) +{ + string str; + size_t idx; + //unsigned int fcnt; waring fix + + idx = CUtil::FindKeyword(line, "procedure"); + if (idx != string::npos) + { + if (idx + 10 < line.length()) + { + str = line.substr(idx + 10); + idx = str.find("is"); + if (idx != string::npos) + { + str = CUtil::ClearRedundantSpaces(str.substr(0, idx)); + } + lineElement element(++functionCount, str); + functionStack.push_back(element); + } + } + else + { + idx = CUtil::FindKeyword(line, "function"); + if (idx != string::npos) + { + if (idx + 9 < line.length()) + { + str = line.substr(idx + 9); + idx = str.find("("); + if (idx != string::npos) + { + str = CUtil::ClearRedundantSpaces(str.substr(0, idx)); + } + lineElement element(++functionCount, str); + functionStack.push_back(element); + } + } + } + + idx = CUtil::FindKeyword(line, "with"); + if (idx == string::npos) + { + idx = CUtil::FindKeyword(line, "use"); + if (idx == string::npos) + { + if (functionStack.empty()) + { + // dealing with some code out of any subroutines, it a "main" code + return 2; + } + } + } + + idx = CUtil::FindKeyword(line, "end"); + if (idx != string::npos) + { + // Modification: 2018.01 + if (!functionStack.empty()) + { + idx = line.find(functionStack.back().line); + if (idx != string::npos) + { + functionName = functionStack.back().line; + functionCount = functionStack.back().lineNumber; + functionStack.pop_back(); + return 1; + } + } + } + + return 0; +} diff --git a/src/CAdaCounter.h b/src/CAdaCounter.h new file mode 100644 index 0000000..88d0abb --- /dev/null +++ b/src/CAdaCounter.h @@ -0,0 +1,48 @@ +//! Code counter class definition for the Ada language. +/*! +* \file CAdaCounter.h +* +* This file contains the code counter class definition for the Ada language. +*/ + +#ifndef AdaCounter_h +#define AdaCounter_h + +#include "CCodeCounter.h" + +//! Ada code counter class. +/*! +* \class CAdaCounter +* +* Defines the Ada code counter class. +*/ +class CAdaCounter : public CCodeCounter +{ +public: + CAdaCounter(); + +protected: + virtual int CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak = NULL); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak = NULL); + virtual int ReplaceQuote(string &strline, size_t &idx_start, bool &contd, char &CurrentQuoteEnd); + void LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + unsigned int &paren_cnt, bool &forflag, bool &found_forifwhile, bool &found_while, bool &found_type, bool &found_is, bool &found_accept, unsigned int &loopLevel); + void FoundSLOC(results* result, size_t lineNumber, string &strLSLOC, string &strLSLOCBak, bool &found_block, bool &found_forifwhile, + bool &found_end, bool &found_type, bool &found_is, bool &found_accept, bool &trunc_flag); + int ParseFunctionName(const string &line, string &lastline, + filemap &functionStack, string &functionName, unsigned int &functionCount); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CAdaCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CAdaCounter(const CAdaCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CAdaCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CAdaCounter operator=(const CAdaCounter); // Declare without implementation +}; + +#endif diff --git a/src/CAssemblyCounter.cpp b/src/CAssemblyCounter.cpp new file mode 100644 index 0000000..37e17f9 --- /dev/null +++ b/src/CAssemblyCounter.cpp @@ -0,0 +1,299 @@ +//! Code counter class methods for assembly languages. +/*! +* \file CAssemblyCounter.cpp +* +* This file contains the code counter class methods for the assembly languages. +* To-do: Globalize changeMode using struct +*/ + +#include "CAssemblyCounter.h" + +/*! +* Constructs a CAssemblyCounter object. +*/ +CAssemblyCounter::CAssemblyCounter() +{ + classtype = ASSEMBLY; + language_name = "Assembly"; + + //Modification: 11.2016 Ext-4 starts + file_extension = CUtil::getExtensionsToLanguage("Assembly", file_extension); + + /*file_extension.push_back(".asm"); + file_extension.push_back(".s"); + file_extension.push_back(".asm.ppc");*/ + //Modification: 11.2016 Ext-4 ends + + statementSeparator = ";"; // assume it is ';' unless it conflicts with comment marker + + BlockCommentStart.push_back("/*"); + BlockCommentEnd.push_back("*/"); + ContinueLine = "\\"; + + // .data & .bss section declaration storage mapping classes, which define data declaration + // see documentation: http://publib.boulder.ibm.com/infocenter/pseries/v5r3/topic/com.ibm.aix.aixassem/doc/alangref/csect.htm#wyl350ken + data_declaration_code.push_back("RW"); + data_declaration_code.push_back("TC0"); + data_declaration_code.push_back("TC"); + data_declaration_code.push_back("TD"); + data_declaration_code.push_back("UA"); + data_declaration_code.push_back("DS"); + data_declaration_code.push_back("BS"); + data_declaration_code.push_back("UC"); + data_declaration_code.push_back("ER"); + data_declaration_code.push_back("SD"); + data_declaration_code.push_back("LD"); + data_declaration_code.push_back("CM"); +} + +/*! +* Perform preprocessing of file lines before counting. +* +* \param fmap list of file lines +* +* \return method status +*/ +int CAssemblyCounter::PreCountProcess(filemap* fmap) +{ + FindLineCommentMarker(fmap); + return 0; +} + +/*! +* Detect the character(s) that is used for marking a line as a comment line. +* If such a character is found, it is added to LineCommentStart vector. +* Since assembly code counter covers multiple assembly languages, there can be +* mulitple markers in the same program. +* GNU Assembler, for instance, supports # and ; for comment lines +* +* \param fmap list of file lines +* +*/ +void CAssemblyCounter::FindLineCommentMarker(filemap* fmap) +{ + StringVector markerChecklist; // contains line comment markers that need to be checked/tested + markerChecklist.push_back("#"); + markerChecklist.push_back(";"); + markerChecklist.push_back("|"); + + LineCommentStart.clear(); // remove all existing values just in case it was set unintentionally + + /* Algorithm: + 1. Read each line + a. For each comment marker, find position/index of the marker + i. If position is 0, a marker is found + 1. Save/add the marker: LineCommentStart.push_back() + 2. Remove the marker from the check list + ii. If position is > 0 && not last index && (position-1) is a space, a marker is found. + 1. Save/add the marker: LineCommentStart.push_back() + 2. Remove the marker from the check list + */ + StringVector::iterator nextMarker; + string line; + size_t position; + for (filemap::iterator fit = fmap->begin(); fit != fmap->end(); fit++) + { + line = fit->line; + nextMarker = markerChecklist.begin(); + while (nextMarker != markerChecklist.end()) + { + position = line.find(*nextMarker); + if (position == string::npos) + { + nextMarker++; + continue; + } + else if (position == 0) + { + LineCommentStart.push_back(*nextMarker); + nextMarker = markerChecklist.erase(nextMarker); + } + else if (position > 0 && position != (line.length() - 1) && line.at(position - 1) == ' ') + { + LineCommentStart.push_back(*nextMarker); + nextMarker = markerChecklist.erase(nextMarker); + } + else + nextMarker++; + } + } +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CAssemblyCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* /*fmapBak*/) +{ + string logicalLine, nextPhysicalLine, physicalLine; + unsigned int i; + int changeSectionMode; + bool isDataSection = false; + bool isDataDeclarationLine = false; // specifies whether a given single line is the declaration of a data section (i.e. '.data') + bool hasLineContinuation = false; + int countPhysicalLine = 0; + + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++) + { + // A physical line does not contain trailing or leading spaces. + // nextPhysicalLine may contains multiple logical statements separated by a separator. + // nextPhysicalLine may end with a line continuation character. + // In this case, nextPhysicalLine will be constructed as multiple physical lines. + nextPhysicalLine = iter->line; + + if (!hasLineContinuation) + physicalLine = ""; + + // do not process blank lines - blank line means blank_line/comment_line + if (!CUtil::CheckBlank(nextPhysicalLine)) + { + countPhysicalLine++; // keep track of the number of physical lines (blank is not a physical line) + if (CUtil::EndsWith(nextPhysicalLine, "\\")) + { + physicalLine += nextPhysicalLine.substr(0, nextPhysicalLine.length() - 1) + " "; + hasLineContinuation = true; + continue; // go to next physical line + } + else + { + physicalLine += nextPhysicalLine; + hasLineContinuation = false; + } + isDataDeclarationLine = false; + + // since a physical line may contain multiple logical lines, check for statement separator + StringVector tokens = CUtil::Split(physicalLine, statementSeparator); + for (i = 0; i < tokens.size(); i++) + { + logicalLine = CUtil::TrimString(tokens[i], 0); // one logical line + changeSectionMode = SwitchSectionMode(logicalLine); + if (changeSectionMode == 1) + isDataSection = false; // false means it is in executable section, not data section + else if (changeSectionMode == 2) + { + isDataSection = true; // true means it has entered a data section of the program + isDataDeclarationLine = true; // declaration of a data section (i.e. '.data') itself will not be counted as data line, but counted as executable + } + + /* + Some Logical SLOC Counting Rules: + 1) MyLabel: instr 1 LSLOC since instruction is present on the same line as the label + 2) MyLabel: 0 LSLOC. Label without instruction is not an executable statement + 3) .instruction param 1 LSLOC + 4) .instruction 1 LSLOC. Some instructions do not require parameters + 5) # 0 LSLOC since comment line. Comment lines have been taken care of in previous processing. No processing needed here + 6) ; 0 LSLOC. Logical statement separator. Program will parse into multiple lines for processing + 7) instr 1 LSLOC. If a line passes all of the above filter/exception, it is an executable line + + Reference: http://www.ibm.com/developerworks/library/l-ppc/ + */ + + // The strategy is to figure out what should be excluded from counting (these are known as counting exceptions). + // Those lines that are not excluded will be assumed to be instructions; thus, they will be counted. + + // 1. First exception to look for is mylabel: + // Rationale: label without instruction is not counted as a logical SLOC + if (CUtil::EndsWith(logicalLine, ":", true)) + continue; + + // 2. Do not count the following NASM statements: %endmacro, %endif, %endrep + if (CUtil::StartsWith(logicalLine, "%end", true)) + continue; + + // 3. Do not count the following MASM statements: endm, endp, ends, endw, end, .end, .endw + // and do not count the following GAS statements: .end, .endm, .endef, .endfunc, .endif, .endloop + if (CUtil::StartsWith(logicalLine, ".end", true) || CUtil::StartsWith(logicalLine, "end", true)) + continue; + + // anything that has passed the above exceptions is a logical SLOC that will be counted as 1 instruction + if (isDataSection && !isDataDeclarationLine) + result->data_lines[LOG]++; + else + result->exec_lines[LOG]++; + + bool trunc_flag = false; + CUtil::TruncateLine(physicalLine.length(), logicalLine.length(), this->lsloc_truncate, trunc_flag); + result->addSLOC(logicalLine, trunc_flag); // add SLOC so that diff can be performed + } + + if (isDataSection && !isDataDeclarationLine) + result->data_lines[PHY] += countPhysicalLine; + else + result->exec_lines[PHY] += countPhysicalLine; + + countPhysicalLine = 0; + } + } + return 1; +} + +/*! +* Determine whether or not to switch to text mode, data mode or no change from previous setting. +* +* \param statement statement text +* +* \return 0 for no change, 1 for text section, and 2 for data section. +*/ +int CAssemblyCounter::SwitchSectionMode(const string &statement) +{ + const int noChange = 0; + const int textSection = 1; + const int dataSection = 2; + + string lStatement = CUtil::ToLower(statement); + + // 1. Data section usually starts with .data (or its variances) or .bss, while code section starts with .text + + if (lStatement.substr(0, 13) == "section .data" || lStatement.substr(0, 12) == "section .bss" || // NASM syntax for data section declaration + lStatement.substr(0, 5) == ".data" || lStatement.substr(0, 6) == ".const" || // MASM & GAS syntax for data section + lStatement.substr(0, 4) == ".bss" || // GAS syntax here & below + (lStatement.substr(0, 9) == ".section " && // find: .section name["flag"]. This is GAS syntax + (lStatement.find("\"d\"", 10) != string::npos || lStatement.find("\'d\'", 10) != string::npos || // find: "d" or 'd' + lStatement.find("\"b\"", 10) != string::npos || lStatement.find("\'b\'", 10) != string::npos)) || // or "b" or 'b' + lStatement.substr(0, 6) == ".rdata" || lStatement.substr(0, 6) == ".sdata" || lStatement.substr(0, 6) == ".kdata" || // MIPS syntax + lStatement.substr(0, 5) == ".sbss" || lStatement.substr(0, 4) == ".lit") //.lit8 and .lit4 are data sections for MIPS syntax + { + return dataSection; + } + else if (lStatement.substr(0, 13) == "section .text" || // NASM syntax for executable section declaration + lStatement.substr(0, 12) == "section .txt" || // NASM syntax only + lStatement.substr(0, 5) == ".code" || // MASM syntax only + lStatement.substr(0, 14) == ".section .text" || // GAS syntax + lStatement.substr(0, 5) == ".text" || // GAS, PowerPC, MIPS + lStatement.substr(0, 5) == ".init" || lStatement.substr(0, 5) == ".fini" || // MIPS syntax + lStatement.substr(0, 6) == ".ktext") // SPIM MIPS syntax + { + return textSection; + } + else if (lStatement.substr(0, 6) == ".csect") // only apply to PowerPC + { + // 2. A text section and a data section can also be defined by .csect instruction. + // Whether .csect is data or text depends on the given storage mapping classes. + // Data section can also be defined by .csect [name][storageMappingClass], where + // storageMappingClass is one of classes for .data or .bss. + // See .csect documentation: http://publib.boulder.ibm.com/infocenter/pseries/v5r3/topic/com.ibm.aix.aixassem/doc/alangref/csect.htm#wyl350ken + + // since both [] and {} are supported, easier to convert {} to [] so that there is only one thing to deal with + + lStatement = CUtil::ReplaceWith(CUtil::ReplaceWith(lStatement, "{", "["), "}", "]"); + + StringVector::iterator vit; + for (vit = data_declaration_code.begin(); vit != data_declaration_code.end(); vit++) + { + string strToFind = "[" + CUtil::ToLower(*vit) + "]"; + if (lStatement.find(strToFind, 6) != string::npos) + return dataSection; + } + return textSection; // it was a .csect, but not a data .csect. Therefore, it is assumed to be a text .csect + } + return noChange; // if it is neither data nor text, mode change is NOT needed +} diff --git a/src/CAssemblyCounter.h b/src/CAssemblyCounter.h new file mode 100644 index 0000000..cc69c95 --- /dev/null +++ b/src/CAssemblyCounter.h @@ -0,0 +1,45 @@ +//! Code counter class definition for assembly languages +/*! +* \file CAssemblyCounter.h +* +* This file contains the code counter class definition for assembly languages. +*/ + +#ifndef CAssemblyCounter_h +#define CAssemblyCounter_h + +#include "CCodeCounter.h" + +//! Assembly code counter class. +/*! +* \class CAssemblyCounter +* +* Defines the assembly code counter class. +*/ +class CAssemblyCounter : public CCodeCounter +{ +public: + CAssemblyCounter(); + +protected: + virtual int PreCountProcess(filemap* /*fmap*/); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapmBak = NULL); + int SwitchSectionMode(const string &statement); // data mode or text mode + void FindLineCommentMarker(filemap* fmap); + StringVector data_declaration_code; //!< storage mapping codes to define data declaration statements + string statementSeparator; + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CAssemblyCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CAssemblyCounter(const CAssemblyCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CAssemblyCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CAssemblyCounter operator=(const CAssemblyCounter); // Declare without implementation +}; + +#endif diff --git a/src/CBashCounter.cpp b/src/CBashCounter.cpp new file mode 100644 index 0000000..338d2dc --- /dev/null +++ b/src/CBashCounter.cpp @@ -0,0 +1,947 @@ +//! Code counter class methods for the Bash shell script language. +/*! +* \file CBashCounter.cpp +* +* This file contains the code counter class methods for the Bash shell script language. +* This also includes the Korn shell language. +*/ + +#include "CBashCounter.h" + +/*! +* Constructs a CBashCounter object. +*/ +CBashCounter::CBashCounter() +{ + classtype = BASH; + language_name = "Bash"; + + //Modification: 11.2016 Ext-4 starts + file_extension = CUtil::getExtensionsToLanguage("Bash", file_extension); + /* + file_extension.push_back(".sh"); + file_extension.push_back(".ksh"); + //Modification: 11.2016 Ext-4 ends*/ + + QuoteStart = "\"'"; + QuoteEnd = "\"'"; + QuoteEscapeFront = '\\'; + ContinueLine = "\\"; + LineCommentStart.push_back("#"); + + directive.push_back("#!"); + + exclude_keywords.push_back("done"); + exclude_keywords.push_back("esac"); + exclude_keywords.push_back("fi"); + + continue_keywords.push_back("do"); + continue_keywords.push_back("else"); + continue_keywords.push_back("then"); + + data_name_list.push_back("declare"); + data_name_list.push_back("local"); + data_name_list.push_back("type"); + data_name_list.push_back("typeset"); + + exec_name_list.push_back("alias"); + exec_name_list.push_back("awk"); + exec_name_list.push_back("bind"); + exec_name_list.push_back("break"); + exec_name_list.push_back("builtin"); + exec_name_list.push_back("caller"); + exec_name_list.push_back("case"); + exec_name_list.push_back("cd"); + exec_name_list.push_back("command"); + exec_name_list.push_back("continue"); + exec_name_list.push_back("coproc"); + exec_name_list.push_back("dirs"); + exec_name_list.push_back("echo"); + exec_name_list.push_back("elif"); + exec_name_list.push_back("enable"); + exec_name_list.push_back("eval"); + exec_name_list.push_back("exec"); + exec_name_list.push_back("exit"); + exec_name_list.push_back("export"); + exec_name_list.push_back("for"); + exec_name_list.push_back("function"); + exec_name_list.push_back("getopts"); + exec_name_list.push_back("hash"); + exec_name_list.push_back("if"); + exec_name_list.push_back("let"); + exec_name_list.push_back("mapfile"); + exec_name_list.push_back("popd"); + exec_name_list.push_back("printf"); + exec_name_list.push_back("pushd"); + exec_name_list.push_back("pwd"); + exec_name_list.push_back("read"); + exec_name_list.push_back("readarray"); + exec_name_list.push_back("readonly"); + exec_name_list.push_back("return"); + exec_name_list.push_back("select"); + exec_name_list.push_back("set"); + exec_name_list.push_back("shift"); + exec_name_list.push_back("source"); + exec_name_list.push_back("test"); + exec_name_list.push_back("time"); + exec_name_list.push_back("times"); + exec_name_list.push_back("trap"); + exec_name_list.push_back("ulimit"); + exec_name_list.push_back("umask"); + exec_name_list.push_back("unalias"); + exec_name_list.push_back("unset"); + exec_name_list.push_back("until"); + exec_name_list.push_back("while"); + + cmplx_calc_list.push_back("%"); + cmplx_calc_list.push_back("^"); + cmplx_calc_list.push_back("++"); + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("--"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("/"); + + cmplx_cond_list.push_back("case"); + cmplx_cond_list.push_back("elif"); + cmplx_cond_list.push_back("if"); + cmplx_cond_list.push_back("for"); + cmplx_cond_list.push_back("select"); + cmplx_cond_list.push_back("until"); + cmplx_cond_list.push_back("while"); + + cmplx_logic_list.push_back("&&"); + cmplx_logic_list.push_back("||"); + cmplx_logic_list.push_back("=="); + cmplx_logic_list.push_back("!"); + cmplx_logic_list.push_back("~"); + cmplx_logic_list.push_back(">"); + cmplx_logic_list.push_back("<"); + cmplx_logic_list.push_back(">="); + cmplx_logic_list.push_back("=<"); + cmplx_logic_list.push_back("-lt"); + cmplx_logic_list.push_back("-gt"); + cmplx_logic_list.push_back("-ge"); + cmplx_logic_list.push_back("-le"); + cmplx_logic_list.push_back("-eq"); + cmplx_logic_list.push_back("-ne"); + + cmplx_assign_list.push_back("="); + + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("for"); + //cmplx_cyclomatic_list.push_back("case"); + cmplx_cyclomatic_list.push_back("elif"); + cmplx_cyclomatic_list.push_back("while"); + //cmplx_cyclomatic_list.push_back("else"); + cmplx_cyclomatic_list.push_back("until"); + cmplx_cyclomatic_list.push_back(";;"); + + cmplx_cyclomatic_logic_list.push_back("&&"); + cmplx_cyclomatic_logic_list.push_back("||"); + + cmplx_cyclomatic_case_list.push_back(";;"); + cmplx_cyclomatic_switch_list.push_back("case"); +} + +/*! +* Perform preprocessing of file lines before counting. +* +* \param fmap list of file lines +* +* \return method status +*/ +int CBashCounter::PreCountProcess(filemap* fmap) +{ + filemap::iterator fit; + for (fit = fmap->begin(); fit != fmap->end(); fit++) + { + if (fit->line.empty()) + continue; + for (size_t i = fit->line.length() - 1; i > 0; i--) + { + // replace $# and ${# with $ to avoid determination of a comment + if (fit->line[i] == '#' && (fit->line[i-1] == '$' || fit->line[i-1] == '{')) + fit->line[i] = '$'; + } + } + return 0; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CBashCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + filemap::iterator fit, fitbak; + string line, lineBak; + + bool data_continue = false; + string strLSLOC = ""; + string strLSLOCBak = ""; + string str; + unsigned int phys_exec_lines = 0; + unsigned int phys_data_lines = 0; + unsigned int temp_lines = 0; + unsigned int cnt = 0; + StringVector loopLevel; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + line = fit->line; + lineBak = fitbak->line; + + // do not process blank lines (blank_line/comment_line/directive) + if (!CUtil::CheckBlank(line)) + { + // process logical SLOC + LSLOC(result, line, fit->lineNumber, lineBak, strLSLOC, strLSLOCBak, data_continue, + temp_lines, phys_exec_lines, phys_data_lines, loopLevel); + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count); + } + + // update physical SLOC lines + result->exec_lines[PHY] += phys_exec_lines; + phys_exec_lines = 0; + + result->data_lines[PHY] += phys_data_lines; + phys_data_lines = 0; + } + } + return 1; +} + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results +* \param line processed physical line of code +* \param lineBak original physical line of code +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param data_continue continuation of a data declaration line +* \param temp_lines tracks physical line count +* \param phys_exec_lines number of physical executable lines +* \param phys_data_lines number of physical data lines +* \param loopLevel nested loop level +*/ +void CBashCounter::LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + bool &data_continue, unsigned int &temp_lines, unsigned int &phys_exec_lines, + unsigned int &phys_data_lines, StringVector &loopLevel) +{ + size_t start, end; + size_t i = 0, m, strSize; + bool trunc_flag = false, found; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string str, spc; + unsigned int cnt = 0; + + string tmp = CUtil::TrimString(line); + string tmpBak = CUtil::TrimString(lineBak); + start = 0; + + // skip whole line '{' or '}' + if (tmp == "{" || tmp == "}") + { + strLSLOC = strLSLOCBak = ""; + phys_exec_lines++; + temp_lines = 0; + return; + } + + // trim trailing '{' + if (tmp[tmp.length() - 1] == '{') + { + tmp = CUtil::TrimString(tmp.substr(0, tmp.length() - 1)); + tmpBak = CUtil::TrimString(tmpBak.substr(0, tmpBak.length() - 1)); + } + + // there may be more than 1 logical SLOC in this line + while (start < tmp.length()) + { + // check for semicolon to denote end of SLOC + end = tmp.find(";", start); + if (end != string::npos) + { + // handle empty statement + if (CUtil::TrimString(tmp.substr(start, end - start + 1)) == ";") + { + start = end + 1; + strLSLOC = strLSLOCBak = ""; + temp_lines = 0; + if (tmp == ";") + phys_exec_lines++; + continue; + } + + // handle for (( ; ; )) + i = CUtil::FindKeyword(tmp, "for", start, end); + if (i != string::npos) + { + i += 3; + i = tmp.find("((", i); + if (i != string::npos && i < end) + { + i += 2; + i = tmp.find("))", i); + if (i != string::npos) + { + i += 2; + end = tmp.find(";", i); + if (end == string::npos) + end = tmp.length() - 1; + } + else + end = tmp.length() - 1; + } + } + + // handle case ';;' or ';&' or ';;&' + if (end < tmp.length() - 1) + { + if (tmp[end + 1] == ';' || tmp[end + 1] == '&') + end++; + if (end < tmp.length() - 2 && tmp[end + 2] == '&') + end++; + } + } + else + end = tmp.length() - 1; + + // process nested loops + if (print_cmplx) + { + str = CUtil::TrimString(tmp.substr(start, end - start + 1)); + if (CUtil::FindKeyword(str, "for") != string::npos + || CUtil::FindKeyword(str, "while") != string::npos + || CUtil::FindKeyword(str, "until")!= string::npos + || CUtil::FindKeyword(str, "select")!= string::npos) + { + if (CUtil::FindKeyword(str, "for") != string::npos) + loopLevel.push_back("for"); + else if (CUtil::FindKeyword(str, "while")!= string::npos) + loopLevel.push_back("while"); + else if (CUtil::FindKeyword(str, "until") != string::npos) + loopLevel.push_back("until"); + else if (CUtil::FindKeyword(str, "select") != string::npos) + loopLevel.push_back(""); + + // record nested loop level + if (CUtil::FindKeyword(str, "select") == string::npos) + { + unsigned int loopCnt = 0; + for (StringVector::iterator lit = loopLevel.begin(); lit < loopLevel.end(); lit++) + { + if ((*lit) != "") + loopCnt++; + } + if ((unsigned int)result->cmplx_nestloop_count.size() < loopCnt) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopCnt-1]++; + } + } + if (CUtil::FindKeyword(str, "done") != string::npos && loopLevel.size() > 0) + loopLevel.pop_back(); + } + + // check for line containing excluded keywords + for (StringVector::iterator it = exclude_keywords.begin(); it != exclude_keywords.end(); it++) + { + i = CUtil::FindKeyword(tmp, (*it), start, end); + if (i != string::npos) + { + // strip specified keyword and skip if empty + start = i + (*it).length(); + if (CUtil::CheckBlank(CUtil::TrimString(tmp.substr(start, end - start)))) + start = end + 1; + break; + } + } + if (start > end) + { + if (temp_lines == 0 && phys_data_lines == 0 && phys_exec_lines == 0) + phys_exec_lines = 1; + continue; + } + + // check for continuation words + found = false; + if (tmp[end] == ';') + str = CUtil::TrimString(tmp.substr(start, end - start)); + else + str = CUtil::TrimString(tmp.substr(start, end - start + 1)); + for (StringVector::iterator it = continue_keywords.begin(); it != continue_keywords.end(); it++) + { + if (str == (*it)) + { + found = true; + strLSLOC += str + " "; + strLSLOCBak += str + " "; + start = end + 1; + if (temp_lines == 0 && phys_data_lines == 0 && phys_exec_lines == 0) + phys_exec_lines = 1; + temp_lines = 0; + } + } + if (found) + continue; + + // check for line continuation + if (tmp[end] == '\\') + { + // strip off trailing (\) + strSize = CUtil::TruncateLine(end - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + spc = ""; + str = tmp.substr(start, strSize); + for (m = str.length() - 1; m > 0; m--) + { + if (str[m] == ' ') + spc += " "; + else + break; + } + if (m == 0) + { + if (str[0] == ' ') + spc += " "; + } + strLSLOC += CUtil::TrimString(tmp.substr(start, strSize)) + spc; + strLSLOCBak += CUtil::TrimString(tmpBak.substr(start, strSize)) + spc; + } + start = end + 1; + + // make sure that we are not beginning to process a new data line + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", NULL); + + if (cnt > 0) + data_continue = true; + if (data_continue == true) + temp_lines++; + if (temp_lines == 0 && phys_data_lines == 0 && phys_exec_lines == 0) + phys_exec_lines = 1; + } + else + { + // save LSLOC + if (tmp[end] == ';') + { + // don't trim if ';;' + if (tmp.length() > 1 && tmp[end - 1] == ';') + strSize = CUtil::TruncateLine(end - start + 1, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + else + strSize = CUtil::TruncateLine(end - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + } + else + strSize = CUtil::TruncateLine(end - start + 1, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += CUtil::TrimString(tmp.substr(start, strSize)); + strLSLOCBak += CUtil::TrimString(tmpBak.substr(start, strSize)); + } + start = end + 1; + if (strLSLOCBak.length() > 0) + { + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + { + // add a logical SLOC + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", &result->data_name_count); + + temp_lines++; + if (data_continue == true || cnt > 0) + { + result->data_lines[LOG]++; + phys_data_lines = temp_lines; + } + else + { + result->exec_lines[LOG]++; + phys_exec_lines = temp_lines; + } + } + else if (data_continue == true) + phys_data_lines = temp_lines; + else + phys_exec_lines = temp_lines; + } + data_continue = false; + temp_lines = 0; + strLSLOC = strLSLOCBak = ""; + } + } +} + +/*! +* Parses lines for function/method names. +* +* \param line line to be processed +* \param lastline last line processed +* \param functionStack stack of functions +* \param functionName function name found +* \param functionCount function count found +* +* \return 1 if function name is found +*/ +int CBashCounter::ParseFunctionName(const string &line, string &lastline, + filemap &functionStack, string &functionName, unsigned int &functionCount) +{ + string tline, str; + size_t i, idx, tidx, cnt; + unsigned int fcnt; + + tline = CUtil::TrimString(line); + idx = tline.find('{'); + if (idx != string::npos) + { + // check whether it is at first index, if yes then function name is at above line + if (idx == 0) + { + lineElement element(++functionCount, lastline); + functionStack.push_back(element); + lastline.erase(); + } + else + { + str = tline.substr(0, idx); + if (str.find('(') != string::npos && str[0] != '(') + lastline = str; + else + lastline += " " + str; + + lineElement element(++functionCount, CUtil::TrimString(lastline)); + functionStack.push_back(element); + lastline.erase(); + } + } + else if (tline.length() > 0 && tline[tline.length() - 1] != ';' && + lastline.length() > 0 && lastline[lastline.length() - 1] != ';') + { + // append until all parenthesis are closed + tidx = lastline.find('('); + if (tidx != string::npos) + { + cnt = 1; + while (tidx != string::npos) + { + tidx = lastline.find('(', tidx + 1); + if (tidx != string::npos) + cnt++; + } + tidx = lastline.find(')'); + while (tidx != string::npos) + { + cnt++; + tidx = lastline.find(')', tidx + 1); + } + if (cnt % 2 != 0) + lastline += " " + tline; + else + lastline = tline; + } + else + lastline = tline; + } + else + lastline = tline; + + idx = line.find('}'); + if (idx != string::npos && !functionStack.empty()) + { + str = functionStack.back().line; + fcnt = functionStack.back().lineNumber; + functionStack.pop_back(); + idx = CUtil::FindKeyword(str, "sub"); + if (idx != string::npos && idx + 4 < str.length()) + { + functionName = CUtil::ClearRedundantSpaces(str.substr(idx + 4)); + functionCount = fcnt; + lastline.erase(); + return 1; + } + lastline.erase(); + } + + // check stack for any "sub" + idx = string::npos; + if (!functionStack.empty()) + { + for (i = 0; i < functionStack.size(); i++) + { + idx = CUtil::FindKeyword(functionStack[i].line, "sub"); + if (idx != string::npos) + break; + } + } + if (idx == string::npos) + { + // dealing with some code out of any subroutines, it a "main" code + return 2; + } + return 0; +} + + +//Writen by Rujun Ma 11.28.16 +/*! +* Counts the number of comment lines, removes comments, and +* replaces quoted strings by special chars, e.g., $ +* All arguments are modified by the method. +* Special processing for """ and ''' which can be multi-line literal +* or a multi-line comment if it stands alone. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CBashCounter::CountCommentsSLOC(filemap* fmap, results* result, filemap *fmapBak) +{ + if (BlockCommentStart.empty() && LineCommentStart.empty()) + return 0; + if (classtype == UNKNOWN || classtype == DATAFILE) + return 0; + bool contd = false; + bool contd_nextline; + int comment_type = 0; + /* + comment_type: + 0 : not a comment + 1 : line comment, whole line + 2 : line comment, embedded + 3 : block comment, undecided + 4 : block comment, embedded + */ + size_t idx_start, idx_end, comment_start; + size_t quote_idx_start; + string curBlckCmtStart, curBlckCmtEnd, tmp; + string CurrentQuoteEnd = ""; + bool quote_contd = false; + filemap::iterator itfmBak = fmapBak->begin(); + quote_idx_start = 0; + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++, itfmBak++) + { + contd_nextline = false; + quote_idx_start = 0; + idx_start = 0; + if (CUtil::CheckBlank(iter->line)) + continue; + //this should be counted as a compiler directive, not a comment + if (iter->line.size() > 2 && iter->line.substr(0, 2) == "#!") { + if (iter->line.find('/') != std::string::npos) continue; + } + if (quote_contd) + { + // Replace quote until next character + ReplaceQuote(iter->line, quote_idx_start, quote_contd, CurrentQuoteEnd); + if (quote_contd) + continue; + } + if (contd) + comment_type = 3; + while (!contd_nextline && idx_start < iter->line.length()) + { + // need to handle multiple quote chars in some languages, both " and ' may be accepted + quote_idx_start = FindQuote(iter->line, QuoteStart, quote_idx_start, QuoteEscapeFront); + comment_start = idx_start; + if (!contd) + { + FindCommentStart(iter->line, comment_start, comment_type, curBlckCmtStart, curBlckCmtEnd); + if (comment_start != string::npos && comment_type > 2) + { + // python: check whether this is a multi-line literal or a block comment + tmp = CUtil::TrimString(iter->line, -1); + if (iter->line.length() - tmp.length() != comment_start) + { + quote_idx_start = comment_start; + comment_start = string::npos; + } + } + } + if (comment_start == string::npos && quote_idx_start == string::npos) + break; + if (comment_start != string::npos) + idx_start = comment_start; + // if found quote before comment, e.g., "this is quote");//comment + if (quote_idx_start != string::npos && (comment_start == string::npos || quote_idx_start < comment_start)) + { + ReplaceQuote(iter->line, quote_idx_start, quote_contd, CurrentQuoteEnd); + if (quote_idx_start > idx_start && quote_idx_start != iter->line.length()) + { + // comment delimiter inside quote + idx_start = quote_idx_start; + continue; + } + } + else if (comment_start != string::npos) + { + // comment delimiter starts first + switch (comment_type) + { + case 1: // line comment, definitely whole line + iter->line = ""; + itfmBak->line = ""; + result->comment_lines++; + contd_nextline = true; + break; + case 2: // line comment, possibly embedded + iter->line = iter->line.substr(0, idx_start); + itfmBak->line = itfmBak->line.substr(0, idx_start); + // trim trailing space + iter->line = CUtil::TrimString(iter->line, 1); + itfmBak->line = CUtil::TrimString(itfmBak->line, 1); + if (iter->line.empty()) + result->comment_lines++; // whole line + else + result->e_comm_lines++; // embedded + contd_nextline = true; + break; + case 3: // block comment + case 4: + if (contd) + idx_end = iter->line.find(curBlckCmtEnd); + else + idx_end = iter->line.find(curBlckCmtEnd, idx_start + curBlckCmtStart.length()); + if (idx_end == string::npos) + { + if (comment_type == 3) + { + iter->line = ""; + itfmBak->line = ""; + result->comment_lines++; + } + else if (comment_type == 4) + { + iter->line = iter->line.substr(0, idx_start); + itfmBak->line = itfmBak->line.substr(0, idx_start); + // trim trailing space + iter->line = CUtil::TrimString(iter->line, 1); + itfmBak->line = CUtil::TrimString(itfmBak->line, 1); + if (iter->line.empty()) + result->comment_lines++; // whole line + else + result->e_comm_lines++; // embedded + } + contd = true; + contd_nextline = true; + break; + } + else + { + contd = false; + iter->line.erase(idx_start, idx_end - idx_start + curBlckCmtEnd.length()); + itfmBak->line.erase(idx_start, idx_end - idx_start + curBlckCmtEnd.length()); + if (iter->line.empty()) + result->comment_lines++; + else + { + // trim trailing space + iter->line = CUtil::TrimString(iter->line, 1); + itfmBak->line = CUtil::TrimString(itfmBak->line, 1); + if (iter->line.empty()) + result->comment_lines++; // whole line + else + result->e_comm_lines++; // embedded + } + // quote chars found may be erased as it is inside comment + quote_idx_start = idx_start; + } + break; + default: + cout << "Error in CountCommentsSLOC()" << endl; + break; + } + } + } + } + return 1; +} +/*! +* Replaces up to ONE quoted string inside a string starting at idx_start. +* Uses a string instead of a character to allow processing multi-line +* literals """ and '''. +* +* \param strline string to be processed +* \param idx_start index of line character to start search +* \param contd specifies the quote string is continued from the previous line +* \param CurrentQuoteEnd end quote string of the current status +* +* \return method status +*/ +int CBashCounter::ReplaceQuote(string &strline, size_t &idx_start, bool &contd, string &CurrentQuoteEnd) +{ + size_t idx_end, idx_quote; + if (contd) + { + // python: use string instead of character to check for """ and ''' + idx_start = 0; + if (strline.length() >= CurrentQuoteEnd.length() && + strline.substr(0, CurrentQuoteEnd.length()) == CurrentQuoteEnd) + { + idx_start = CurrentQuoteEnd.length(); + contd = false; + return 1; + } + strline[0] = '$'; + } + else + { + // handle two quote chars in some languages, both " and ' may be accepted + idx_start = FindQuote(strline, QuoteStart, idx_start, QuoteEscapeFront); + if (idx_start != string::npos) + { + idx_quote = QuoteStart.find_first_of(strline[idx_start]); + CurrentQuoteEnd = QuoteEnd[idx_quote]; + // python: check for """ or ''' + if (strline.length() >= idx_start + 3) + { + if (CurrentQuoteEnd == "\"") + { + if (strline.substr(idx_start, 3) == "\"\"\"") + CurrentQuoteEnd = "\"\"\""; + } + else if (CurrentQuoteEnd == "'") + { + if (strline.substr(idx_start, 3) == "'''") + CurrentQuoteEnd = "'''"; + } + } + } + else + { + idx_start = strline.length(); + return 0; + } + } + // python: handle """ and ''' + if (CurrentQuoteEnd.length() == 3) + { + if (idx_start + 3 >= strline.length()) + idx_end = string::npos; + else + { + idx_end = strline.find(CurrentQuoteEnd, idx_start + 3); + if (idx_end != string::npos) + idx_end += 2; // shift to last quote character + } + } + else + idx_end = CUtil::FindCharAvoidEscape(strline, CurrentQuoteEnd[0], idx_start + 1, QuoteEscapeFront); + if (idx_end == string::npos) + { + idx_end = strline.length() - 1; + strline.replace(idx_start + 1, idx_end - idx_start, idx_end - idx_start, '$'); + contd = true; + idx_start = idx_end + 1; + } + else + { + if (CurrentQuoteEnd.length() != 3 && (QuoteEscapeRear) && (strline.length() > idx_end + 1) && (strline[idx_end + 1] == QuoteEscapeRear)) + { + strline[idx_end] = '$'; + strline[idx_end + 1] = '$'; + } + else + { + contd = false; + strline.replace(idx_start + 1, idx_end - idx_start - 1, idx_end - idx_start - 1, '$'); + idx_start = idx_end + 1; + } + } + return 1; +} +/*! +* Counts directive lines of code. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CBashCounter::CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak) +{ + bool contd = false, trunc_flag = false; + size_t idx, strSize; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string strDirLine = ""; + filemap::iterator itfmBak = fmapBak->begin(); + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++, itfmBak++) + { + if (CUtil::CheckBlank(iter->line)) + continue; + size_t lineNumber = iter->lineNumber; + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(" " + iter->line, directive, cnt, 1, exclude, "", "", &result->directive_count); + } + if (!contd) + { + // if not a continuation of a previous directive + for (vector::iterator viter = directive.begin(); viter != directive.end(); viter++) + { + if ((idx = iter->line.find((*viter), 0)) != string::npos && idx == 0) + { + contd = true; + break; + } + } + if (contd) + { + strSize = CUtil::TruncateLine(itfmBak->line.length(), 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine = itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + } + else + { + // continuation of a previous directive + strSize = CUtil::TruncateLine(itfmBak->line.length(), strDirLine.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine += "\n" + itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + if (contd) + { + // drop continuation symbol + if (strDirLine[strDirLine.length() - 1] == '\\') + strDirLine = strDirLine.substr(0, strDirLine.length() - 1); + // if a directive or continuation of a directive (no continuation symbol found) + if (iter->line[iter->line.length() - 1] != '_') + { + contd = false; + if (result->addSLOC(strDirLine, lineNumber, trunc_flag)) + result->directive_lines[LOG]++; + } + iter->line = ""; + } + } + return 1; +} diff --git a/src/CBashCounter.h b/src/CBashCounter.h new file mode 100644 index 0000000..d0b9713 --- /dev/null +++ b/src/CBashCounter.h @@ -0,0 +1,55 @@ +//! Code counter class definition for the Bash shell script language. +/*! +* \file CBashCounter.h +* +* This file contains the code counter class definition for the Bash shell script language. +* This also includes the Korn shell language. +*/ + +#ifndef CBashCounter_h +#define CBashCounter_h + +#include "CCodeCounter.h" + +//! Bash shell script code counter class. +/*! +* \class CBashCounter +* +* Defines the Bash shell script code counter class. +*/ +class CBashCounter : public CCodeCounter +{ +public: + CBashCounter(); + +protected: + virtual int PreCountProcess(filemap* fmap); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak = NULL); + //Added by Rujun Ma for BashCounter fix. 11.27.16 + //changes maade by Nikhila Banukumar + using CCodeCounter::ReplaceQuote; + virtual int ReplaceQuote(string &strline, size_t &idx_start, bool &contd, string &CurrentQuoteEnd); + virtual int CountCommentsSLOC(filemap* fmap, results* result, filemap* fmapBak = NULL); + virtual int CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak = NULL); + + void LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, + string &strLSLOCBak, bool &data_continue, unsigned int &temp_lines, unsigned int &phys_exec_lines, + unsigned int &phys_data_lines, StringVector &loopLevel); + int ParseFunctionName(const string &line, string &lastline, filemap &functionStack, + string &functionName, unsigned int &functionCount); + StringVector continue_keywords; //!< List of keywords to continue to next line + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CBashCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CBashCounter(const CBashCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CBashCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CBashCounter operator=(const CBashCounter); // Declare without implementation +}; + +#endif diff --git a/src/CBatchCounter.cpp b/src/CBatchCounter.cpp new file mode 100644 index 0000000..f301a98 --- /dev/null +++ b/src/CBatchCounter.cpp @@ -0,0 +1,904 @@ +//! Code counter class methods for the DOS Batch language. +/*! +* \file CBatchCounter.cpp +* +* This file contains the code counter class methods for the DOS Batch language. +* This also includes the MS-DOS commands.*/ + +#include +#include "CBatchCounter.h" +//#include "MainObject.h" + +// changed calling tolower() to use lowerChars array +extern unsigned char lowerChars[256]; + +/*! +* Constructs a CBatchCounter object. +*/ +CBatchCounter::CBatchCounter () +{ + classtype = BATCH; + language_name = "DOS_Batch"; + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("DOS_Batch", file_extension); + //file_extension.push_back(".bat"); + + QuoteStart = "\"'"; + QuoteEnd = "\"'"; + QuoteEscapeFront = '\\'; + ContinueLine = "^"; + + //Comments in .bat + LineCommentStart.push_back("rem"); + LineCommentStart.push_back("::"); + + continue_keywords.push_back("else"); + + //commands in .bat + exec_name_list.push_back("ansi.sys"); + exec_name_list.push_back("append"); + exec_name_list.push_back("arp"); + exec_name_list.push_back("assign"); + exec_name_list.push_back("assoc"); + exec_name_list.push_back("at"); + exec_name_list.push_back("atmadm"); + exec_name_list.push_back("attrib"); + exec_name_list.push_back("backup"); + exec_name_list.push_back("batch"); + exec_name_list.push_back("bootcfg"); + exec_name_list.push_back("bootsect"); + exec_name_list.push_back("break"); + exec_name_list.push_back("cacls"); + exec_name_list.push_back("call"); + exec_name_list.push_back("cd"); + exec_name_list.push_back("chcp"); + exec_name_list.push_back("chdir"); + exec_name_list.push_back("chkdsk"); + exec_name_list.push_back("chkntfs"); + exec_name_list.push_back("choice"); + exec_name_list.push_back("cipher"); + exec_name_list.push_back("cls"); + exec_name_list.push_back("cmd"); + exec_name_list.push_back("color"); + exec_name_list.push_back("command"); + exec_name_list.push_back("comp"); + exec_name_list.push_back("compact"); + exec_name_list.push_back("control"); + exec_name_list.push_back("convert"); + exec_name_list.push_back("copy"); + exec_name_list.push_back("ctty"); + exec_name_list.push_back("date"); + exec_name_list.push_back("debug"); + exec_name_list.push_back("defrag"); + exec_name_list.push_back("del"); + exec_name_list.push_back("delete"); + exec_name_list.push_back("deltree"); + exec_name_list.push_back("dir"); + exec_name_list.push_back("disable"); + exec_name_list.push_back("diskcomp"); + exec_name_list.push_back("diskcopy"); + exec_name_list.push_back("diskpart"); + exec_name_list.push_back("doskey"); + exec_name_list.push_back("dosshell"); + exec_name_list.push_back("drivparm"); + exec_name_list.push_back("echo"); + exec_name_list.push_back("edit"); + exec_name_list.push_back("edlin"); + exec_name_list.push_back("emm386"); + exec_name_list.push_back("enable"); + exec_name_list.push_back("endlocal"); + exec_name_list.push_back("erase"); + exec_name_list.push_back("exit"); + exec_name_list.push_back("expand"); + exec_name_list.push_back("extract"); + exec_name_list.push_back("fasthelp"); + exec_name_list.push_back("fc"); + exec_name_list.push_back("fdisk"); + exec_name_list.push_back("find"); + exec_name_list.push_back("findstr"); + exec_name_list.push_back("fixboot"); + exec_name_list.push_back("fixmbr"); + exec_name_list.push_back("for"); + exec_name_list.push_back("forfiles"); + exec_name_list.push_back("format"); + exec_name_list.push_back("ftp"); + exec_name_list.push_back("fType"); + exec_name_list.push_back("goto"); + exec_name_list.push_back("gpupdate"); + exec_name_list.push_back("graftabl"); + exec_name_list.push_back("help"); + exec_name_list.push_back("himem"); + exec_name_list.push_back("sys"); + exec_name_list.push_back("hostname"); + exec_name_list.push_back("if"); + exec_name_list.push_back("ifshlp"); + exec_name_list.push_back("sys"); + exec_name_list.push_back("ipconfig"); + exec_name_list.push_back("keyb"); + exec_name_list.push_back("label"); + exec_name_list.push_back("lh"); + exec_name_list.push_back("listsvc"); + exec_name_list.push_back("loadfix"); + exec_name_list.push_back("loadhigh"); + exec_name_list.push_back("lock"); + exec_name_list.push_back("logoff"); + exec_name_list.push_back("logon"); + exec_name_list.push_back("map"); + exec_name_list.push_back("md"); + exec_name_list.push_back("mem"); + exec_name_list.push_back("mkdir"); + exec_name_list.push_back("mode"); + exec_name_list.push_back("more"); + exec_name_list.push_back("move"); + exec_name_list.push_back("msav"); + exec_name_list.push_back("msbackup"); + exec_name_list.push_back("msd"); + exec_name_list.push_back("mscdex"); + exec_name_list.push_back("mwbackup"); + exec_name_list.push_back("nbtstat"); + exec_name_list.push_back("net"); + exec_name_list.push_back("netsh"); + exec_name_list.push_back("netstat"); + exec_name_list.push_back("nlsfunc"); + exec_name_list.push_back("nslookup"); + exec_name_list.push_back("path"); + exec_name_list.push_back("pathping"); + exec_name_list.push_back("pause"); + exec_name_list.push_back("ping"); + exec_name_list.push_back("popd"); + exec_name_list.push_back("power"); + exec_name_list.push_back("print"); + exec_name_list.push_back("prompt"); + exec_name_list.push_back("pushd"); + exec_name_list.push_back("qbasic"); + exec_name_list.push_back("rd"); + exec_name_list.push_back("reg"); + exec_name_list.push_back("ren"); + exec_name_list.push_back("rename"); + exec_name_list.push_back("rmdir"); + exec_name_list.push_back("robocopy"); + exec_name_list.push_back("route"); + exec_name_list.push_back("runas"); + exec_name_list.push_back("scandisk"); + exec_name_list.push_back("scanreg"); + exec_name_list.push_back("set"); + exec_name_list.push_back("setlocal"); + exec_name_list.push_back("setver"); + exec_name_list.push_back("sfc"); + exec_name_list.push_back("share"); + exec_name_list.push_back("shift"); + exec_name_list.push_back("shutdown"); + exec_name_list.push_back("smartdrv"); + exec_name_list.push_back("sort"); + exec_name_list.push_back("start"); + exec_name_list.push_back("subst"); + exec_name_list.push_back("switches"); + exec_name_list.push_back("sys"); + exec_name_list.push_back("systeminfo"); + exec_name_list.push_back("systemroot"); + exec_name_list.push_back("taskkill"); + exec_name_list.push_back("tasklist"); + exec_name_list.push_back("telnet"); + exec_name_list.push_back("time"); + exec_name_list.push_back("title"); + exec_name_list.push_back("tracert"); + exec_name_list.push_back("tree"); + exec_name_list.push_back("tskill"); + exec_name_list.push_back("Type"); + exec_name_list.push_back("undelete"); + exec_name_list.push_back("unformat"); + exec_name_list.push_back("unlock"); + exec_name_list.push_back("ver"); + exec_name_list.push_back("verify"); + exec_name_list.push_back("vol"); + exec_name_list.push_back("wmic"); + exec_name_list.push_back("xcopy"); + + //arithmetic operations in .bat + cmplx_calc_list.push_back("%"); + cmplx_calc_list.push_back("^"); + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("/"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("<<"); + cmplx_calc_list.push_back(">>"); + cmplx_calc_list.push_back("&"); + cmplx_calc_list.push_back("|"); + + //complex commands in .bat + cmplx_cond_list.push_back("for"); + cmplx_cond_list.push_back("if"); + cmplx_cond_list.push_back("choice"); + + //logical operations in .bat + cmplx_logic_list.push_back("&&"); + cmplx_logic_list.push_back("||"); + cmplx_logic_list.push_back("=="); + cmplx_logic_list.push_back("equ"); + cmplx_logic_list.push_back("neq"); + cmplx_logic_list.push_back("lss"); + cmplx_logic_list.push_back("leq"); + cmplx_logic_list.push_back("gtr"); + cmplx_logic_list.push_back("geq"); + + cmplx_assign_list.push_back("="); + + // cyclometic complexity + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("while"); + cmplx_cyclomatic_list.push_back("for"); + cmplx_cyclomatic_list.push_back("unless"); + + cmplx_cyclomatic_logic_list.push_back("&&"); + cmplx_cyclomatic_logic_list.push_back("||"); + + //there is no keyword "case" in DOS_Batch, so no CC3. +} + +/*! +* Perform preprocessing of file lines before counting. +* +* \param fmap list of file lines +* +* \return method status +*/ +int CBatchCounter::PreCountProcess(filemap* fmap) +{ + filemap::iterator fit; + for (fit = fmap->begin(); fit != fmap->end(); fit++) + { + if (fit->line.empty()) + continue; + for (size_t i = fit->line.length() - 1; i > 0; i--) + { + // to lowercase + fit->line[i] = (char)lowerChars[ TL_ARR( fit->line[i] ) ]; + fit->line[i-1] = (char)lowerChars[ TL_ARR( fit->line[i-1] ) ]; + + // replace with space + if (fit->line[i] == '(' || fit->line[i] == ')' || fit->line[i] == '\'' || fit->line[i] == '.' || fit->line[i] == '@') { + fit->line[i] = ' '; + } + if (fit->line[i-1] ==')' || fit->line[i-1] =='(' || fit->line[i-1] == '\'' || fit->line[i-1] == '.' || fit->line[i-1] == '@') { + fit->line[i-1] = ' '; + } + } + if (CUtil::CheckBlank(fit->line)) + fit->line = "$"; + + //ignore words after keyword "echo" + /*int idx = CUtil::FindKeyword(fit->line, "echo");*/ + size_t idx = CUtil::FindKeyword(fit->line, "echo"); // warning fix + if(idx != string::npos) { + for (size_t i = idx + 4; i < fit->line.length(); i++) + { + fit->line[i] = '$'; + } + } + } + return 0; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CBatchCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + filemap::iterator fit, fitbak; + string line, lineBak; + + bool data_continue = false; + string strLSLOC = ""; + string strLSLOCBak = ""; + string str; + unsigned int phys_exec_lines = 0; + unsigned int phys_data_lines = 0; + unsigned int temp_lines = 0; + unsigned int cnt = 0; + //StringVector loopLevel; warning fix + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + line = fit->line; + lineBak = fitbak->line; + + // do not process blank lines (blank_line/comment_line/directive) + if (!CUtil::CheckBlank(line)) + { + // process logical SLOC + LSLOC(result, line, fit->lineNumber, lineBak, strLSLOC, strLSLOCBak, data_continue, + temp_lines, phys_exec_lines, phys_data_lines/*, loopLevel*/); //warning fix + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count); + } + + // update physical SLOC lines + result->exec_lines[PHY] += phys_exec_lines; + phys_exec_lines = 0; + + result->data_lines[PHY] += phys_data_lines; + phys_data_lines = 0; + } + } + return 1; +} + +void CBatchCounter::LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + bool &data_continue, unsigned int &temp_lines, unsigned int &phys_exec_lines, + unsigned int &phys_data_lines/*, StringVector &loopLevel*/)// warning fix +{ + size_t start, end; + size_t strSize; + bool trunc_flag = false; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string str, spc; + unsigned int cnt = 0; + + string tmp = CUtil::TrimString(line); + string tmpBak = CUtil::TrimString(lineBak); + start = 0; + end = tmp.length(); + + // skip whole line '(' or ')' + if (tmp == "(" || tmp == ")") + { + strLSLOC = strLSLOCBak = ""; + phys_exec_lines++; + temp_lines = 0; + return; + } + + // there may be more than 1 logical SLOC in this line +#ifdef _MSC_VER + char * context = NULL; // used for strtok_s( ) below +#else + // +#endif + + bool has_for = false; + bool has_for_in = false; + while (start < tmp.length()) + { + end = tmp.length(); + if (start == 0) { + has_for = false; + has_for_in = false; + } + //std::cout << start <<"start\n"; + + // check if is a label + bool isLabel = false; + for (unsigned int i = 0; tmp[i] != '\0'; i++) { + if (tmp[i] == ' ') { + continue; + } else if (tmp[i] == ':') { + isLabel = true; + break; + } else { + break; + } + } + if(isLabel) { + strLSLOC = strLSLOCBak = ""; + phys_exec_lines++; + temp_lines = 0; + return; + } + + + char * token = NULL; + + // Allocate a char array that must be deleted later. + char* line = new char[end - start + 1]; + + #ifdef _MSC_VER + // Use more Secure C library API + strcpy_s( line, (end - start + 1) * sizeof(char), tmp.substr(start).c_str() ); + #else + // Use older less Secure C library API + strcpy( line, tmp.substr(start).c_str() ); + #endif + + // check |, & + bool hasPipe = false; + size_t pipePos = 0; + bool hasAmp = false; + size_t ampPos = 0; + + for (int i = 0; line[i] != '\0'; i++) { + if (line[i] == '|') { + if (!hasPipe) { + pipePos = (size_t)i; + } + hasPipe = true; + line[i] = ' '; + } + if (line[i] == '&') { + if (!hasAmp) { + ampPos = (size_t)i; + } + hasAmp = true; + line[i] = ' '; + } + } + + #ifdef _MSC_VER + // Use more Secure C library API + token = strtok_s( line, " .@:", &context ); + #else + // Use older less Secure C library API + token = strtok( line, " .@:" ); + #endif + + for (int i = 0; token[i] != '\0'; i++) { + token[i] = (char)lowerChars[ TL_ARR( token[i] ) ]; + } + if (strcmp(token, "else") == 0) + { + #ifdef _MSC_VER + // Use more Secure C library API + token = strtok_s( NULL, " .@:", &context ); + #else + // Use older less Secure C library API + token = strtok( NULL, " .@:" ); + #endif + } + + if (token == NULL) { + strLSLOC = strLSLOCBak = ""; + phys_exec_lines++; + temp_lines = 0; + delete[] line; + return; + } + + //first keyword + //std::cout << token << "TOKEN\n"; + string firstWord(token); + firstWord = CUtil::ToLower(firstWord); + bool checkFirstWord = false; + for(std::vector::iterator it = exec_name_list.begin(); it != exec_name_list.end(); ++it) { + if (*it == firstWord) { + checkFirstWord = true; + break; + } + } + if(!checkFirstWord) { + strLSLOC = strLSLOCBak = ""; + phys_exec_lines++; + temp_lines = 0; + delete[] line; + return; + } + + + + //if, for, choice as first word + bool isComplexWord = false; + bool isCall = false; + size_t nextKeyWordStart = (size_t)(-1); + bool isKeyWord = false; + bool hasElse = false; + char * nextToken = NULL; + size_t tmpNextKeyWordStart = (size_t)(-1); + size_t tmpEnd = (size_t)(-1); + + for(std::vector::iterator it = cmplx_cond_list.begin(); it != cmplx_cond_list.end(); ++it) { + if (*it == firstWord) { + isComplexWord = true; + break; + } + } + + //for as first word + if (firstWord.compare("for") == 0) { + has_for = true; + } + + + //call as first word + if (firstWord.compare("call") == 0) { + isCall = true; + } + + + // go through the rest of words + #ifdef _MSC_VER + // Use more Secure C library API + nextToken = strtok_s( NULL, " .@:", &context ); + #else + // Use older less Secure C library API + nextToken = strtok( NULL, " .@:" ); + #endif + + while (nextToken != NULL) + { + string nextWord(nextToken); + nextWord = CUtil::ToLower(nextWord); + + if (has_for && nextWord.compare("in") == 0) + has_for_in = true; + + if (nextWord.compare("else") == 0) + hasElse = true; + + if (!isKeyWord) + { + for(std::vector::iterator it2 = exec_name_list.begin(); it2 != exec_name_list.end(); ++it2) + { + if (*it2 == nextWord) + { + isKeyWord = true; + tmpNextKeyWordStart = CUtil::FindKeyword(CUtil::ToLower(tmp), nextWord, start + firstWord.length(), end); + //std::cout << tmpNextKeyWordStart << "NEXT_START\n"; + tmpEnd = tmpNextKeyWordStart; // !!!!! + //std::cout << tmpEnd << "CURR_END\n"; + + break; + } + } + } + #ifdef _MSC_VER + // Use more Secure C library API + nextToken = strtok_s( NULL, " .@:", &context ); + #else + // Use older less Secure C library API + nextToken = strtok( NULL, " .@:" ); + #endif + } + + if (isKeyWord) + { + //std::cout << pipePos << "pipePos\n"; + if (hasPipe && pipePos >= tmpEnd) + hasPipe = false; + + //std::cout << ampPos << "ampPos\n"; + if (hasAmp && ampPos >= tmpEnd) + hasAmp = false; + } + else + { + hasPipe = false; + hasAmp = false; + } + + if ((isComplexWord || isCall || hasElse || has_for_in || hasPipe || hasAmp) && isKeyWord) { // REMEMBER TO CHANGE THE SAME CODE BELOW + nextKeyWordStart = tmpNextKeyWordStart; + end = tmpEnd; + } + + + if (tmp[0] != '^') { + //std::cout << "2\n"; + strSize = CUtil::TruncateLine(end - start + 1, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) { + strLSLOC += CUtil::TrimString(tmp.substr(start, strSize)); + strLSLOCBak += CUtil::TrimString(tmpBak.substr(start, strSize)); + } + start = end + 1; + if ((isComplexWord || isCall || hasElse || has_for_in || hasPipe || hasAmp) && isKeyWord) { // REMEMBER TO CHANGE THE SAME CODE ABOVE + start = nextKeyWordStart; + } + if (strLSLOCBak.length() > 0) { //add physical SLOC + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) { //add a logical SLOC + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", &result->data_name_count); + + temp_lines++; + if (data_continue == true || cnt > 0) { + result->data_lines[LOG]++; + phys_data_lines = temp_lines; + } + else { + result->exec_lines[LOG]++; + phys_exec_lines = temp_lines; + } + } + else if (data_continue == true) + phys_data_lines = temp_lines; + else + phys_exec_lines = temp_lines; + } + data_continue = false; + temp_lines = 0; + strLSLOC = strLSLOCBak = ""; + } + + delete[] line; + line = NULL; + } +} + +/*! + * Parses lines for function/method names. + * + * \param line line to be processed + * \param lastline last line processed + * \param functionStack stack of functions + * \param functionName function name found + * + * \return 1 if function name is found + */ +int CBatchCounter::ParseFunctionName(const string &line, string &lastline, + filemap &functionStack, string &functionName, unsigned int &functionCount) +{ + /* + * functionStack is used to record block names encountered, it will be poped when its closing "}" is encountered + * cyclomatic_stack is used to record the previous cyc_count when start of block is encountered, it will be poped + * when end of block is encountered and poped value will be added to the current cyc_cnt + */ + + string tline, str; + //size_t i, idx, tidx, cnt; + size_t idx; // warning fix + + tline = CUtil::TrimString(line, 0); + // Look for label + idx = tline.find(':'); + if(idx == 0) { + // check if there is any function in the stack that has noot been closed + if(!functionStack.empty()) { + // pop start of the block from functionStack + functionName = functionStack.back().line; + functionCount = functionStack.back().lineNumber; + functionStack.pop_back(); + // push current function into stack + lineElement element(++functionCount, CUtil::ClearRedundantSpaces(tline)); + functionStack.push_back(element); + lastline.erase(); + + return 1; + } + + str = CUtil::ClearRedundantSpaces(tline); + lineElement element(++functionCount, str); + functionStack.push_back(element); + lastline.erase(); + } + + // Look for end of function + idx = CUtil::FindKeyword(tline, "goto"); + if(idx != string::npos) { + str = CUtil::TrimString(tline.substr(idx+4)); + idx = CUtil::FindKeyword(str, ":eof"); + } + if(idx == 0 && !functionStack.empty()) { + functionName = functionStack.back().line; + functionCount = functionStack.back().lineNumber; + functionStack.pop_back(); + lastline.erase(); + return 1; + } + + // update lastline + lastline = tline; + + // code that belong to "main" block + if(functionStack.empty()) { + return 2; + } + + return 0; + +} + +/*! +* Counts file language complexity based on specified language keywords/characters. +* +* \param fmap list of processed file lines +* \param result counter results +* +* \return method status +*/ +int CBatchCounter::CountComplexity(filemap* fmap, results* result) +{ + if (classtype == UNKNOWN || classtype == DATAFILE) + return 0; + filemap::iterator fit; + size_t idx; + unsigned int cnt, ret, cyclomatic_cnt = 0, ignore_cyclomatic_cnt = 0, main_cyclomatic_cnt = 0, function_count = 0, cyclomatic_logic_cnt = 0, main_cyclomatic_logic_cnt = 1; + string line, lastline, file_ext, function_name = ""; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + filemap function_stack; + stack cyclomatic_stack; + stack cyclomatic_logic_stack; + map function_map; + map logical_map; + bool process_cyclomatic_complexity = false; + // check whether to process cyclomatic complexity + if (cmplx_cyclomatic_list.size() > 0) + { + process_cyclomatic_complexity = true; + if (skip_cmplx_cyclomatic_file_extension_list.size() > 0) + { + idx = result->file_name.find_last_of("."); + if (idx != string::npos) + { + file_ext = result->file_name.substr(idx); + file_ext = CUtil::ToLower(file_ext); + if (find(skip_cmplx_cyclomatic_file_extension_list.begin(), skip_cmplx_cyclomatic_file_extension_list.end(), file_ext) != skip_cmplx_cyclomatic_file_extension_list.end()) + process_cyclomatic_complexity = false; + } + } + } + + // process each line + for (fit = fmap->begin(); fit != fmap->end(); fit++) + { + line = fit->line; + + if (CUtil::CheckBlank(line)) + continue; + + line = " " + line; + + // mathematical functions + cnt = 0; + CUtil::CountTally(line, math_func_list, cnt, 1, exclude, "", "", &result->math_func_count, casesensitive); + result->cmplx_math_lines += cnt; + + // trigonometric functions + cnt = 0; + CUtil::CountTally(line, trig_func_list, cnt, 1, exclude, "", "", &result->trig_func_count, casesensitive); + result->cmplx_trig_lines += cnt; + + // logarithmic functions + cnt = 0; + CUtil::CountTally(line, log_func_list, cnt, 1, exclude, "", "", &result->log_func_count, casesensitive); + result->cmplx_logarithm_lines += cnt; + + // calculations + cnt = 0; + CUtil::CountTally(line, cmplx_calc_list, cnt, 1, exclude, "", "", &result->cmplx_calc_count, casesensitive); + result->cmplx_calc_lines += cnt; + + // conditionals + cnt = 0; + CUtil::CountTally(line, cmplx_cond_list, cnt, 1, exclude, "", "", &result->cmplx_cond_count, casesensitive); + result->cmplx_cond_lines += cnt; + + // logical operators + cnt = 0; + CUtil::CountTally(line, cmplx_logic_list, cnt, 1, exclude, "", "", &result->cmplx_logic_count, casesensitive); + result->cmplx_logic_lines += cnt; + + // preprocessor directives + cnt = 0; + CUtil::CountTally(line, cmplx_preproc_list, cnt, 1, exclude, "", "", &result->cmplx_preproc_count, casesensitive); + result->cmplx_preproc_lines += cnt; + + // assignments + cnt = 0; + CUtil::CountTally(line, cmplx_assign_list, cnt, 1, exclude, "", "", &result->cmplx_assign_count, casesensitive); + result->cmplx_assign_lines += cnt; + + /* No pointers for DOS batch commands + // pointers + cnt = 0; + // Pointers are embedded syntax so there is NO exclude string or include strings + CUtil::CountTally(line, cmplx_pointer_list, cnt, 1, "", "", "", &result->cmplx_pointer_count, casesensitive); + result->cmplx_pointer_lines += cnt; + */ + + // cyclomatic complexity + if (process_cyclomatic_complexity) + { + // search for cyclomatic complexity keywords + CUtil::CountTally(line, cmplx_cyclomatic_list, cyclomatic_cnt, 1, exclude, "", "", 0, casesensitive); + + // search for keywords to exclude + if (ignore_cmplx_cyclomatic_list.size() > 0) + CUtil::CountTally(line, ignore_cmplx_cyclomatic_list, ignore_cyclomatic_cnt, 1, exclude, "", "", 0, casesensitive); + + // search for cyclomatic complexity logical keywords + if (cmplx_cyclomatic_logic_list.size() > 0) + CUtil::CountTally(line, cmplx_cyclomatic_logic_list, cyclomatic_logic_cnt, 1, exclude, "", "", 0, casesensitive); + + // parse function name if found + ret = (unsigned int)ParseFunctionName( line, lastline, function_stack, function_name, function_count ); + if (ret != 1 && !cyclomatic_stack.empty() && cyclomatic_stack.size() == function_stack.size()) + { + // remove count stack entry for non-function names + cyclomatic_cnt += cyclomatic_stack.top(); + ignore_cyclomatic_cnt = 0; + cyclomatic_stack.pop(); + } + if (ret != 1 && !cyclomatic_logic_stack.empty() && cyclomatic_logic_stack.size() == function_stack.size()) + { + // remove count stack entry for non-function names + cyclomatic_logic_cnt += cyclomatic_logic_stack.top(); + cyclomatic_logic_stack.pop(); + } + if (ret == 1) + { + // capture count at end of function + lineElement element(cyclomatic_cnt - ignore_cyclomatic_cnt + 1, function_name); + function_map[function_count] = element; + + lineElement n_element(cyclomatic_cnt - ignore_cyclomatic_cnt + cyclomatic_logic_cnt + 1, function_name); + logical_map[function_count] = n_element; + + //there is no embedded functions in DOS_Batch + cyclomatic_cnt = 0; + cyclomatic_logic_cnt = 0; + function_name = ""; + ignore_cyclomatic_cnt = 0; + } + else if (ret == 2) + { + if (main_cyclomatic_cnt < 1) + main_cyclomatic_cnt = 1; // add 1 for main function here in case no other decision points are found in main + // some code doesn't belong to any function + main_cyclomatic_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt; + main_cyclomatic_logic_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt + cyclomatic_logic_cnt; + cyclomatic_cnt = ignore_cyclomatic_cnt = cyclomatic_logic_cnt = 0; + } + else { + if (!function_stack.empty() && (function_stack.size() > cyclomatic_stack.size() + 1 || (cyclomatic_stack.empty() && function_stack.size() > 1))) + { + // capture previous complexity count from open function + cyclomatic_stack.push(cyclomatic_cnt - ignore_cyclomatic_cnt); + cyclomatic_cnt = ignore_cyclomatic_cnt = 0; + } + if (!function_stack.empty() && (function_stack.size() > cyclomatic_logic_stack.size() + 1 || (cyclomatic_logic_stack.empty() && function_stack.size() > 1))) + { + // capture previous complexity count from open function + cyclomatic_logic_stack.push(cyclomatic_logic_cnt); + cyclomatic_logic_cnt = 0; + } + } + } + } + + //process the last function if it exists + if (!function_stack.empty()) + { + function_name = function_stack.back().line; + function_count = function_stack.back().lineNumber + 1; + function_stack.pop_back(); + + lineElement element(cyclomatic_cnt + 1, function_name); + lineElement n_element(cyclomatic_cnt + cyclomatic_logic_cnt + 1, function_name); + function_map[function_count] = element; + logical_map[function_count] = n_element; + } + + // done with a file + if (main_cyclomatic_cnt > 0) + { + // add "main" code + lineElement element(main_cyclomatic_cnt, "main"); + lineElement n_element(main_cyclomatic_logic_cnt, "main"); + function_map[0] = element; + logical_map[0] = n_element; + } + + // process ordered functions + for (map::iterator it = function_map.begin(); it != function_map.end(); ++it) + result->cmplx_cycfunct_count.push_back(it->second); + if(cmplx_cyclomatic_logic_list.size() > 0) + { + for (map::iterator it = logical_map.begin(); it != logical_map.end(); ++it) + result->cmplx_cycfunct_CC2_count.push_back(it->second); + } + + return 1; +} diff --git a/src/CBatchCounter.h b/src/CBatchCounter.h new file mode 100644 index 0000000..4138d65 --- /dev/null +++ b/src/CBatchCounter.h @@ -0,0 +1,50 @@ +//! Code counter class definition for the Batch shell script language. +/*! +* \file CBatchCounter.h +* +* This file contains the code counter class definition for the Batch shell script language. +* This also includes the Korn shell language. +*/ + +#ifndef CBatchCounter_h +#define CBatchCounter_h + +#include "CCodeCounter.h" + +//! Batch shell script code counter class. +/*! +* \class CBatchCounter +* +* Defines the Batch shell script code counter class. +*/ +class CBatchCounter : public CCodeCounter +{ +public: + CBatchCounter(); + +protected: + virtual int PreCountProcess(filemap* fmap); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak = NULL); + void LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, + string &strLSLOCBak, bool &data_continue, unsigned int &temp_lines, unsigned int &phys_exec_lines, + unsigned int &phys_data_lines/*, StringVector &loopLevel*/); // warning fix + int ParseFunctionName(const string &line, string &lastline, filemap &functionStack, string &functionName, + unsigned int &functionCount); + int CountComplexity(filemap* fmap, results* result); + + StringVector continue_keywords; //!< List of keywords to continue to next line + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CBatchCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CBatchCounter(const CBatchCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CBatchCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CBatchCounter operator=(const CBatchCounter); // Declare without implementation +}; + +#endif diff --git a/src/CCCounter.cpp b/src/CCCounter.cpp new file mode 100644 index 0000000..3c23031 --- /dev/null +++ b/src/CCCounter.cpp @@ -0,0 +1,306 @@ +//! Code counter class methods for the C/C++ languages. +/*! +* \file CCCounter.cpp +* +* This file contains the code counter class methods for the C/C++ languages. +* +* Changed UCC 2015.12 release by Randy Maxwell +* Addition of file extensions .inl .inc to support working with Linux and Boost for example +* .cxx .hxx as comments ready for anyone wanting Legacy C++ file support +*/ + +#include "CCCounter.h" + +/*! +* Constructs a CCCounter object. +*/ +CCCounter::CCCounter( string lang ) : CCJavaCsScalaCounter( lang ) +{ + classtype = C_CPP; + language_name = "C_CPP"; + + //Modification: 11.2016 Ext-4 starts + file_extension = CUtil::getExtensionsToLanguage("C_CPP", file_extension); + + /*file_extension.push_back(".c"); + file_extension.push_back(".cc"); + file_extension.push_back(".cpp"); + file_extension.push_back(".cxx"); // Legacy C++ Enable if you want + file_extension.push_back(".inl"); // inline files for example see Boost libraries Modification: 2015.12 + + // These file types are not processed when doing Cyclomatic Complexity calculations + file_extension.push_back(".h"); + file_extension.push_back(".hh"); + file_extension.push_back(".hpp"); + file_extension.push_back(".hxx"); // Legacy C++ Enable if you want + file_extension.push_back(".inc"); // include files for example see Linux Modification: 2015.12 */ + + //Modification: 11.2016 Ext-4 ends + + + directive.push_back("#define"); + directive.push_back("#dictionary"); + directive.push_back("#error"); + directive.push_back("#if"); + directive.push_back("#ifdef"); + directive.push_back("#ifndef"); + directive.push_back("#else"); + directive.push_back("#elif"); + directive.push_back("#endif"); + directive.push_back("#import"); + directive.push_back("#include"); + directive.push_back("#line"); + directive.push_back("#module"); + directive.push_back("#pragma"); + directive.push_back("#undef"); + directive.push_back("#using"); + + directive.push_back("# define"); + directive.push_back("# dictionary"); + directive.push_back("# error"); + directive.push_back("# if"); + directive.push_back("# ifdef"); + directive.push_back("# ifndef"); + directive.push_back("# else"); + directive.push_back("# elif"); + directive.push_back("# endif"); + directive.push_back("# import"); + directive.push_back("# include"); + directive.push_back("# line"); + directive.push_back("# module"); + directive.push_back("# pragma"); + directive.push_back("# undef"); + directive.push_back("# using"); + + data_name_list.push_back("asm"); + data_name_list.push_back("auto"); + data_name_list.push_back("bool"); + data_name_list.push_back("char"); + data_name_list.push_back("class"); + data_name_list.push_back("const"); + data_name_list.push_back("double"); + data_name_list.push_back("enum"); + data_name_list.push_back("explicit"); + data_name_list.push_back("extern"); + data_name_list.push_back("FILE"); + data_name_list.push_back("float"); + data_name_list.push_back("friend"); + data_name_list.push_back("inline"); + data_name_list.push_back("int"); + data_name_list.push_back("long"); + data_name_list.push_back("mutable"); + data_name_list.push_back("namespace"); + data_name_list.push_back("operator"); + data_name_list.push_back("register"); + data_name_list.push_back("short"); + data_name_list.push_back("static"); + data_name_list.push_back("string"); + data_name_list.push_back("struct"); + data_name_list.push_back("template"); + data_name_list.push_back("typedef"); + data_name_list.push_back("union"); + data_name_list.push_back("unsigned"); + data_name_list.push_back("using"); + data_name_list.push_back("virtual"); + data_name_list.push_back("void"); + data_name_list.push_back("volatile"); + data_name_list.push_back("wchar_t"); + + exec_name_list.push_back("break"); + exec_name_list.push_back("case"); + exec_name_list.push_back("catch"); + exec_name_list.push_back("cerr"); + exec_name_list.push_back("cin"); + exec_name_list.push_back("clog"); + exec_name_list.push_back("const_cast"); + exec_name_list.push_back("continue"); + exec_name_list.push_back("cout"); + exec_name_list.push_back("default"); + exec_name_list.push_back("delete"); + exec_name_list.push_back("do"); + exec_name_list.push_back("dynamic_cast"); + exec_name_list.push_back("else"); + exec_name_list.push_back("entry"); + exec_name_list.push_back("for"); + exec_name_list.push_back("goto"); + exec_name_list.push_back("if"); + exec_name_list.push_back("new"); + exec_name_list.push_back("reinterpret_cast"); + exec_name_list.push_back("return"); + exec_name_list.push_back("sizeof"); + exec_name_list.push_back("stderr"); + exec_name_list.push_back("stdin"); + exec_name_list.push_back("stdout"); + exec_name_list.push_back("switch"); + exec_name_list.push_back("static_cast"); + exec_name_list.push_back("throw"); + exec_name_list.push_back("try"); + exec_name_list.push_back("typeid"); + exec_name_list.push_back("while"); + + math_func_list.push_back("abs"); + math_func_list.push_back("cbrt"); + math_func_list.push_back("ceil"); + math_func_list.push_back("copysign"); + math_func_list.push_back("erf"); + math_func_list.push_back("erfc"); + math_func_list.push_back("exp"); + math_func_list.push_back("exp2"); + math_func_list.push_back("expm1"); + math_func_list.push_back("fabs"); + math_func_list.push_back("fdim"); + math_func_list.push_back("floor"); + math_func_list.push_back("fma"); + math_func_list.push_back("fmax"); + math_func_list.push_back("fmin"); + math_func_list.push_back("fmod"); + math_func_list.push_back("frexp"); + math_func_list.push_back("hypot"); + math_func_list.push_back("ilogb"); + math_func_list.push_back("ldexp"); + math_func_list.push_back("lgamma"); + math_func_list.push_back("llrint"); + math_func_list.push_back("lrint"); + math_func_list.push_back("llround"); + math_func_list.push_back("lround"); + math_func_list.push_back("modf"); + math_func_list.push_back("nan"); + math_func_list.push_back("nearbyint"); + math_func_list.push_back("nextafter"); + math_func_list.push_back("nexttoward"); + math_func_list.push_back("pow"); + math_func_list.push_back("remainder"); + math_func_list.push_back("remquo"); + math_func_list.push_back("rint"); + math_func_list.push_back("round"); + math_func_list.push_back("scalbln"); + math_func_list.push_back("scalbn"); + math_func_list.push_back("sqrt"); + math_func_list.push_back("tgamma"); + math_func_list.push_back("trunc"); + + trig_func_list.push_back("cos"); + trig_func_list.push_back("cosh"); + trig_func_list.push_back("sin"); + trig_func_list.push_back("sinh"); + trig_func_list.push_back("tan"); + trig_func_list.push_back("tanh"); + trig_func_list.push_back("acos"); + trig_func_list.push_back("acosh"); + trig_func_list.push_back("asinh"); + trig_func_list.push_back("atanh"); + trig_func_list.push_back("asin"); + trig_func_list.push_back("atan"); + trig_func_list.push_back("atan2"); + + log_func_list.push_back("log"); + log_func_list.push_back("log10"); + log_func_list.push_back("log1p"); + log_func_list.push_back("log2"); + log_func_list.push_back("logb"); + + cmplx_preproc_list.push_back("#define"); + cmplx_preproc_list.push_back("#dictionary"); + cmplx_preproc_list.push_back("#elif"); + cmplx_preproc_list.push_back("#else"); + cmplx_preproc_list.push_back("#endif"); + cmplx_preproc_list.push_back("#error"); + cmplx_preproc_list.push_back("#if"); + cmplx_preproc_list.push_back("#ifdef"); + cmplx_preproc_list.push_back("#ifndef"); + cmplx_preproc_list.push_back("#import"); + cmplx_preproc_list.push_back("#include"); + cmplx_preproc_list.push_back("#line"); + cmplx_preproc_list.push_back("#module"); + cmplx_preproc_list.push_back("#pragma"); + cmplx_preproc_list.push_back("#undef"); + cmplx_preproc_list.push_back("#using"); + + cmplx_pointer_list.push_back("->"); + +/* Below Set in CCJavaCsScalaCounter + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("case"); + cmplx_cyclomatic_list.push_back("while"); + cmplx_cyclomatic_list.push_back("for"); + cmplx_cyclomatic_list.push_back("catch"); + cmplx_cyclomatic_list.push_back("?"); + + + cmplx_cyclomatic_logic_list.push_back("||"); + cmplx_cyclomatic_logic_list.push_back("&&"); + //cmplx_cyclomatic_logic_list.push_back("^"); + + cmplx_cyclomatic_case_list.push_back("case"); + cmplx_cyclomatic_switch_list.push_back("switch"); +*/ + + skip_cmplx_cyclomatic_file_extension_list.push_back(".h"); + skip_cmplx_cyclomatic_file_extension_list.push_back(".hh"); + skip_cmplx_cyclomatic_file_extension_list.push_back(".hpp"); + //skip_cmplx_cyclomatic_file_extension_list.push_back(".hxx"); // Legacy C++ Enable if you want + skip_cmplx_cyclomatic_file_extension_list.push_back(".inc"); + + two_char_operator_list.push_back("->"); + two_char_operator_list.push_back("::"); + + for (StringVector::iterator it = cmplx_cyclomatic_list.begin(); it != cmplx_cyclomatic_list.end(); it++) + keyword_operators.insert(*it); + + for (StringVector::iterator it = cmplx_preproc_list.begin(); it != cmplx_preproc_list.end(); it++) + keyword_operators.insert(*it); + + for (StringVector::iterator it = directive.begin(); it != directive.end(); it++) + keyword_operators.insert(*it); + + for (StringVector::iterator it = data_name_list.begin(); it != data_name_list.end(); it++) + keyword_operators.insert(*it); + + for (StringVector::iterator it = exec_name_list.begin(); it != exec_name_list.end(); it++) + keyword_operators.insert(*it); +} + +/*! +* Returns true if the line ends with '\', which is a way to +* continue a line of code in C++ (and other languages)to the next line. +* +* \param line the string to examine +* \return true if the line ends with '\' else false +*/ +bool CCCounter::EndsWithOpenString(const string &line) { + if (line.size() > 0 && line[line.size() - 1] == '\\') { + return true; + } + return false; +} + +/*! +* Concatenates all the physical lines belonging to a multiline string and stores +* this in the parameter 'line'. If the source line at curr_line_idx does not contain any multiline string +* then this function will store just that one source line into 'line' and return +* the original curr_line_idx that was passed into the function. +* +* \param curr_line_idx the index of the line in fmap from which to begin +* \param line the concatenation of all source lines beginning from curr_line_idx until there is no multistring +* \param fmap the source code for the file +* \param nonfunction_operator_counts the map for counting the number of times a nonfunction operator (e.g. symbolic operators such as '+' and '[') +* +* \return the index into fmap at which the first physical line, at or after curr_line_idx, does not continue to the next source line as a multiline string +*/ +int CCCounter::GetLineUntilEndOfMultistringIfAny(int curr_line_idx, string &line, filemap &fmap, map &nonfunction_operator_counts) { + string line_with_end_of_multistring_if_any = ""; + //Modification: 2018.01 USC, changed data type to size_t + size_t current_line_idx = curr_line_idx ; + + string curr_line = CUtil::TrimString(fmap[current_line_idx].line, 1); + while (EndsWithOpenString(curr_line) && current_line_idx < fmap.size() - 1) { + line_with_end_of_multistring_if_any.append(curr_line.substr(0, curr_line.size() - 1)); + nonfunction_operator_counts["\\"]++; + current_line_idx++; + curr_line = CUtil::TrimString(fmap[current_line_idx].line, 1); + } + line = line_with_end_of_multistring_if_any + curr_line; + + return current_line_idx; +} + diff --git a/src/CCCounter.h b/src/CCCounter.h new file mode 100644 index 0000000..abb2674 --- /dev/null +++ b/src/CCCounter.h @@ -0,0 +1,43 @@ +//! Code counter class definition for the C/C++ languages. +/*! +* \file CCCounter.h +* +* This file contains the code counter class definition for the C/C++ languages. +*/ + +#ifndef CCCounter_h +#define CCCounter_h + +#include "CCJavaCsScalaCounter.h" + +//! C/C++ code counter class. +/*! +* \class CCCounter +* +* Defines the C/C++ code counter class. +*/ +class CCCounter : public CCJavaCsScalaCounter +{ +public: + // Set the language so base class constructors set values as needed + CCCounter( string lang = "C_CPP" ); + + //Modification: 2018.01 Integration starts + bool EndsWithOpenString(const string &line); + int GetLineUntilEndOfMultistringIfAny(int curr_line_idx, string &line, filemap &fmap, map &nonfunction_operator_counts); + + //Modification: 2018.01 Integration ends +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CCCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CCCounter(const CCCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CCCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CCCounter operator=(const CCCounter); // Declare without implementation +}; + +#endif diff --git a/src/CCFScriptCounter.cpp b/src/CCFScriptCounter.cpp new file mode 100644 index 0000000..37ab1c1 --- /dev/null +++ b/src/CCFScriptCounter.cpp @@ -0,0 +1,661 @@ +//! Code counter class methods for the CFScript language. +/*! +* \file CCFScriptCounter.cpp +* +* This file contains the code counter class methods for the CFScript language. +*/ + +#include "CCFScriptCounter.h" + +/*! +* Constructs a CCFScriptCounter object. +*/ +CCFScriptCounter::CCFScriptCounter() +{ + classtype = CFSCRIPT; + language_name = "CFScript"; + casesensitive = false; + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("CFScript", file_extension); + + //file_extension.push_back(".cfs"); + + QuoteStart = "\"'"; + QuoteEnd = "\"'"; + LineCommentStart.push_back("//"); + BlockCommentStart.push_back("/*"); + BlockCommentEnd.push_back("*/"); + + data_name_list.push_back("function"); + data_name_list.push_back("import"); + data_name_list.push_back("include"); + data_name_list.push_back("interface"); + data_name_list.push_back("property"); + data_name_list.push_back("var"); + + exec_name_list.push_back("abort"); + exec_name_list.push_back("break"); + exec_name_list.push_back("case"); + exec_name_list.push_back("catch"); + exec_name_list.push_back("component"); + exec_name_list.push_back("continue"); + exec_name_list.push_back("createobject"); + exec_name_list.push_back("default"); + exec_name_list.push_back("else"); + exec_name_list.push_back("exit"); + exec_name_list.push_back("finally"); + exec_name_list.push_back("for"); + exec_name_list.push_back("if"); + exec_name_list.push_back("location"); + exec_name_list.push_back("lock"); + exec_name_list.push_back("new"); + exec_name_list.push_back("param"); + exec_name_list.push_back("pageecoding"); + exec_name_list.push_back("rethrow"); + exec_name_list.push_back("return"); + exec_name_list.push_back("savecontent"); + exec_name_list.push_back("switch"); + exec_name_list.push_back("thread"); + exec_name_list.push_back("throw"); + exec_name_list.push_back("trace"); + exec_name_list.push_back("transaction"); + exec_name_list.push_back("try"); + exec_name_list.push_back("while"); + exec_name_list.push_back("writedump"); + exec_name_list.push_back("writelog"); + exec_name_list.push_back("writeoutput"); + + math_func_list.push_back("abs"); + math_func_list.push_back("arrayavg"); + math_func_list.push_back("arraysum"); + math_func_list.push_back("ceiling"); + math_func_list.push_back("decrementvalue"); + math_func_list.push_back("exp"); + math_func_list.push_back("fix"); + math_func_list.push_back("incrementvalue"); + math_func_list.push_back("int"); + math_func_list.push_back("max"); + math_func_list.push_back("min"); + math_func_list.push_back("mod"); + math_func_list.push_back("pi"); + math_func_list.push_back("precisionevaluate"); + math_func_list.push_back("rand"); + math_func_list.push_back("randomize"); + math_func_list.push_back("randrange"); + math_func_list.push_back("round"); + math_func_list.push_back("sgn"); + math_func_list.push_back("sqr"); + + trig_func_list.push_back("acos"); + trig_func_list.push_back("asin"); + trig_func_list.push_back("atn"); + trig_func_list.push_back("cos"); + trig_func_list.push_back("sin"); + trig_func_list.push_back("tan"); + + log_func_list.push_back("log"); + log_func_list.push_back("log10"); + + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("/"); + cmplx_calc_list.push_back("**"); + + cmplx_cond_list.push_back("case"); + cmplx_cond_list.push_back("else"); + cmplx_cond_list.push_back("for"); + cmplx_cond_list.push_back("if"); + cmplx_cond_list.push_back("switch"); + cmplx_cond_list.push_back("while"); + cmplx_cond_list.push_back("?"); + + cmplx_logic_list.push_back("=="); + cmplx_logic_list.push_back("!="); + cmplx_logic_list.push_back(">"); + cmplx_logic_list.push_back("<"); + cmplx_logic_list.push_back(">="); + cmplx_logic_list.push_back("=<"); + cmplx_logic_list.push_back("eq"); + cmplx_logic_list.push_back("neq"); + cmplx_logic_list.push_back("gt"); + cmplx_logic_list.push_back("lt"); + cmplx_logic_list.push_back("gte"); + cmplx_logic_list.push_back("lte"); + + cmplx_assign_list.push_back("="); + + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("for"); + cmplx_cyclomatic_list.push_back("case"); + cmplx_cyclomatic_list.push_back("elif"); + cmplx_cyclomatic_list.push_back("while"); + //cmplx_cyclomatic_list.push_back("switch"); + cmplx_cyclomatic_list.push_back("?"); + + //there is no bollean operators in CFScript, so no CC2 results. + + cmplx_cyclomatic_case_list.push_back("case"); + cmplx_cyclomatic_switch_list.push_back("switch"); +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CCFScriptCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + unsigned int paren_count = 0; + bool for_flag = false; + bool found_forifwhile = false; + bool found_while = false; + char prev_char = 0; + bool data_continue = false; + bool inArrayDec = false; + string strLSLOC = ""; + string strLSLOCBak = ""; + unsigned int openBrackets = 0; + + filemap::iterator fit, fitbak; + string line, lineBak; + StringVector loopLevel; + + unsigned int phys_exec_lines = 0; + unsigned int phys_data_lines = 0; + unsigned int temp_lines = 0; + unsigned int cnt = 0; + + // two strings used for string match + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + line = fit->line; + + // insert blank at the beginning(for searching keywords + line = ' ' + line; + lineBak = ' ' + fitbak->line; + + // do not process blank lines + // blank line means blank_line/comment_line/directive + if (!CUtil::CheckBlank(line)) + { + LSLOC(result, line, fit->lineNumber, lineBak, strLSLOC, strLSLOCBak, paren_count, for_flag, found_forifwhile, + found_while, prev_char, data_continue, temp_lines, phys_exec_lines, phys_data_lines, inArrayDec, + openBrackets, loopLevel); + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count, false); + } + + result->exec_lines[PHY] += phys_exec_lines; + phys_exec_lines = 0; + + result->data_lines[PHY] += phys_data_lines; + phys_data_lines = 0; + + } + } + return 1; +} + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results +* \param line processed physical line of code +* \param lineBak original physical line of code +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param paren_cnt count of parenthesis +* \param forflag found for flag +* \param found_forifwhile found for, if, or while flag +* \param found_while found while flag +* \param prev_char previous character +* \param data_continue continuation of a data declaration line +* \param temp_lines tracks physical line count +* \param phys_exec_lines number of physical executable lines +* \param phys_data_lines number of physical data lines +* \param inArrayDec marks an array declaration +* \param openBrackets number of open brackets (no matching close bracket) +* \param loopLevel nested loop level +*/ +void CCFScriptCounter::LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + unsigned int &paren_cnt, bool &forflag, bool &found_forifwhile, bool &found_while, + char &prev_char, bool &data_continue, unsigned int &temp_lines, + unsigned int &phys_exec_lines, unsigned int &phys_data_lines, bool &inArrayDec, + unsigned int &openBrackets, StringVector &loopLevel) +{ + // paren_cnt is used with 'for' statement only + size_t start = 0; //starting index of the working string + size_t i = 0, strSize; + bool found_do, found_try, found_else, trunc_flag = false; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$:"; + unsigned int cnt = 0; + + string tmp = CUtil::TrimString(strLSLOC); + + // do, try + found_do = (CUtil::FindKeyword(tmp, "do", 0, TO_END_OF_STRING, false) != string::npos); + found_try = (CUtil::FindKeyword(tmp, "try", 0, TO_END_OF_STRING, false) != string::npos); + // else is treated differently, else is included in SLOC, do and try are not + found_else = (CUtil::FindKeyword(tmp, "else", 0, TO_END_OF_STRING, false) != string::npos); + + while (i < line.length()) // there may be more than 1 logical SLOC in this line + { + switch (line[i]) + { + case ';': case '{': // LSLOC terminators + // ';' for normal executable or declaration statement + // '{' for starting a function or 'do' statement or a block (which is counted) + + // get the previous logical mark until i-1 index is the new LSLOC + // except 'do' precedes '{' + // except '}' precedes ';' ?? + if (paren_cnt > 0 && line[i] == ';') // do nothing inside 'for' statement + break; + + // record open bracket for nested loop processing + if (print_cmplx) + { + if (line[i] == '{') + { + openBrackets++; + if ((unsigned int)loopLevel.size() < openBrackets) + loopLevel.push_back(""); + } + else + { + if ((unsigned int)loopLevel.size() > openBrackets && openBrackets > 0) + loopLevel.pop_back(); + } + } + + // case 'while(...);', 'while(...) {', and '} while(...);' + // this case is handled in case ')' + if (found_while && found_forifwhile) + { + found_while = false; + found_forifwhile = false; + start = i + 1; + break; + } + + if (line[i] == '{') + { + if (prev_char == '=') inArrayDec = true; + if (inArrayDec) break; // continue until seeing ';' + + // case for(...); and if (...) { + if (found_forifwhile) // these specials are handled + { + found_forifwhile = false; + start = i + 1; + break; + } + + // check if 'do' precedes '{' + if (!found_do && !found_try && !found_else) + { + // find for 'do' in string before tmp string + tmp = CUtil::TrimString(line.substr(start, i - start)); + found_do = (tmp == "do"); // found 'do' statement + found_try = (tmp == "try"); // found 'try' statement + // same as else + found_else = (tmp == "else"); // found 'else' statement + } + if (found_do || found_try || found_else) + { + if (found_do && print_cmplx) + { + if (loopLevel.size() > 0) loopLevel.pop_back(); + loopLevel.push_back("do"); + } + found_do = false; + found_try = false; + if (!found_else) + { + // everything before 'do', 'try' are cleared + strLSLOC = ""; + strLSLOCBak = ""; + start = i + 1; + } + break; // do not store '{' following 'do' + } + } + + if (line[i] == ';' && prev_char == '}') // wrong, e.g., a[]={1,2,3}; + { + // check if in array declaration or not + // if no, skip, otherwise, complete the SLOC containing array declaration + if (!inArrayDec) + { + start = i + 1; + break; + } + } + + inArrayDec = false; + + // check for empty statement (=1 LSLOC) + if (CUtil::TrimString(line.substr(start, i + 1 - start)) == ";" && strLSLOC.length() < 1) + { + strLSLOC = ";"; + strLSLOCBak = ";"; + } + else + { + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + { + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", &result->data_name_count, false); + + temp_lines++; + if (data_continue == true && line[i] == ';') + { + result->data_lines[LOG]++; + phys_data_lines = temp_lines; + } + else + { + if (cnt > 0 && line[i] == ';') + { + result->data_lines[LOG]++; + phys_data_lines = temp_lines; + } + else + { + result->exec_lines[LOG]++; + phys_exec_lines = temp_lines; + } + } + } + else if (data_continue == true && line[i] == ';') + phys_data_lines = temp_lines; + else + phys_exec_lines = temp_lines; + data_continue = false; + temp_lines = 0; + strLSLOC = strLSLOCBak = ""; + start = i + 1; + + break; + case '(': + if (forflag) + paren_cnt++; + else + { + // handle 'for', 'while', 'if' the same way + tmp = CUtil::TrimString(line.substr(start,i)); + if (CUtil::FindKeyword(tmp, "for", 0, TO_END_OF_STRING, false) != string::npos + || CUtil::FindKeyword(tmp, "while", 0, TO_END_OF_STRING, false)!= string::npos + || CUtil::FindKeyword(tmp, "if", 0, TO_END_OF_STRING, false) != string::npos) + { + forflag = true; + paren_cnt++; + + if (print_cmplx && (unsigned int)loopLevel.size() > openBrackets && openBrackets > 0) + loopLevel.pop_back(); + + if (CUtil::FindKeyword(tmp, "while", 0, TO_END_OF_STRING, false)!= string::npos) + { + if (print_cmplx) + loopLevel.push_back("while"); + found_while = true; + } + else if (print_cmplx && CUtil::FindKeyword(tmp, "for", 0, TO_END_OF_STRING, false) != string::npos) + { + loopLevel.push_back("for"); + } + + // record nested loop level + if (print_cmplx) + { + if (CUtil::FindKeyword(tmp, "if", 0, TO_END_OF_STRING, false) == string::npos) + { + unsigned int loopCnt = 0; + for (StringVector::iterator lit = loopLevel.begin(); lit < loopLevel.end(); lit++) + { + if ((*lit) != "") + loopCnt++; + } + if ((unsigned int)result->cmplx_nestloop_count.size() < loopCnt) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopCnt-1]++; + } + } + } + } + break; + case ')': + if (forflag) + { + if (paren_cnt > 0) + paren_cnt--; + if (paren_cnt == 0) + { + // handle 'for', 'foreach', 'while', 'if' + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + strLSLOCBak = strLSLOC = ""; + phys_exec_lines = temp_lines; + temp_lines = 0; + start = i + 1; + found_forifwhile = true; + forflag = false; + } + } + break; + case '}': + // skip '}' when found ';' and then '}' because '{' is counted already + // also, {} is also skipped, counted + if (prev_char == ';' || prev_char == '{' || prev_char == '}') + if (!inArrayDec) start = i + 1; + + // record close bracket for nested loop processing + if (print_cmplx) + { + // record close bracket for nested loop processing + if (openBrackets > 0) + openBrackets--; + if (loopLevel.size() > 0) + loopLevel.pop_back(); + } + + break; + } + + if (line[i] != ' ' && line[i] != '\t') + { + // if ;}}} --> don't count }}} at all + // also, if {}}} --> don't count }}} at all + //if ( !(line[i] == '}' && (prev_char == ';' || prev_char == '{'))) // see case '}' above + prev_char = line[i]; + + // change to not found if a char appears before + if (line[i] != ')' && found_forifwhile) + found_forifwhile = false; + } + i++; + + } + + tmp = CUtil::TrimString(line.substr(start, i - start)); + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + tmp = CUtil::TrimString(lineBak.substr(start, i - start)); + strLSLOCBak += tmp.substr(0, strSize); + } + + // make sure that we are not beginning to process a new data line + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", NULL, false); + + if (cnt > 0) + data_continue = true; + if (data_continue) + temp_lines++; + if (temp_lines == 0 && phys_data_lines == 0 && phys_exec_lines == 0) + phys_exec_lines = 1; +} + +/*! +* Parses lines for function/method names. +* +* \param line line to be processed +* \param lastline last line processed +* \param functionStack stack of functions +* \param functionName function name found +* \param functionCount function count found +* +* \return 1 if function name is found +*/ +int CCFScriptCounter::ParseFunctionName(const string &line, string &lastline, + filemap &functionStack, string &functionName, unsigned int &functionCount) +{ + string tline, str; + size_t idx, tidx, cnt, cnt2; + unsigned int fcnt; + unsigned int cyclomatic_cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + tline = CUtil::TrimString(line); + idx = tline.find('{'); + if (idx != string::npos) + { + // check whether it is at first index, if yes then function name is at above line + if (idx == 0) + { + lineElement element(++functionCount, lastline); + functionStack.push_back(element); + lastline.erase(); + } + else + { + str = tline.substr(0, idx); + tidx = cnt = cnt2 = 0; + if (str[0] != '(' && str[0] != ':' && (lastline.length() < 1 || lastline[lastline.length() - 1] != ':')) + { + while (tidx != string::npos) + { + tidx = str.find('(', tidx); + if (tidx != string::npos) + { + cnt++; + tidx++; + } + } + if (cnt > 0) + { + tidx = 0; + while (tidx != string::npos) + { + tidx = str.find(')', tidx); + if (tidx != string::npos) + { + cnt2++; + tidx++; + } + } + } + } + // make sure parentheses are closed and no parent class listed + if ((cnt > 0 && cnt == cnt2) || (lastline.length() > 0 && lastline[lastline.length() - 1] == ';')) + lastline = str; + else + lastline += " " + str; + lineElement element(++functionCount, CUtil::TrimString(lastline)); + functionStack.push_back(element); + lastline.erase(); + } + } + else if (tline.length() > 0 && tline[tline.length() - 1] != ';' && + lastline.length() > 0 && lastline[lastline.length() - 1] != ';') + { + // append until all parentheses are closed + tidx = lastline.find('('); + if (tidx != string::npos) + { + cnt = 1; + while (tidx != string::npos) + { + tidx = lastline.find('(', tidx + 1); + if (tidx != string::npos) + cnt++; + } + tidx = lastline.find(')'); + while (tidx != string::npos) + { + cnt++; + tidx = lastline.find(')', tidx + 1); + } + if (cnt % 2 != 0) + lastline += " " + tline; + else + lastline = tline; + } + else + lastline = tline; + } + else + lastline = tline; + + idx = line.find('}'); + if (idx != string::npos && !functionStack.empty()) + { + str = functionStack.back().line; + fcnt = functionStack.back().lineNumber; + functionStack.pop_back(); + idx = str.find('('); + + if (idx != string::npos) + { + // search for cyclomatic complexity keywords and other possible keywords + CUtil::CountTally(str, cmplx_cyclomatic_list, cyclomatic_cnt, 1, exclude, "", "", 0, casesensitive); + if (cyclomatic_cnt <= 0 && CUtil::FindKeyword(str, "switch") == string::npos && + CUtil::FindKeyword(str, "try") == string::npos && CUtil::FindKeyword(str, "finally") == string::npos && + CUtil::FindKeyword(str, "return") == string::npos && str.find('=') == string::npos) + { + functionName = CUtil::ClearRedundantSpaces(str.substr(0, idx)); + functionCount = fcnt; + lastline.erase(); + return 1; + } + } + lastline.erase(); + } + return 0; +} diff --git a/src/CCFScriptCounter.h b/src/CCFScriptCounter.h new file mode 100644 index 0000000..ee323b0 --- /dev/null +++ b/src/CCFScriptCounter.h @@ -0,0 +1,46 @@ +//! Code counter class definition for the CFScript language. +/*! +* \file CCFScriptCounter.h +* +* This file contains the code counter class definition for the CFScript language. +*/ + +#ifndef CCFScriptCounter_h +#define CCFScriptCounter_h + +#include "CCodeCounter.h" + +//! CFScript code counter class. +/*! +* \class CCFScriptCounter +* +* Defines the CFScript code counter class. +*/ +class CCFScriptCounter : public CCodeCounter +{ +public: + CCFScriptCounter(); + +protected: + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak = NULL); + void LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + unsigned int &paren_cnt, bool &forflag, bool &found_forifwhile, bool &found_while, char &prev_char, + bool &data_continue, unsigned int &temp_lines, unsigned int &phys_exec_lines, unsigned int &phys_data_lines, + bool &inArrayDec, unsigned int &openBrackets, StringVector &loopLevel); + int ParseFunctionName(const string &line, string &lastline, filemap &functionStack, string &functionName, + unsigned int &functionCount); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CCFScriptCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CCFScriptCounter(const CCFScriptCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CCFScriptCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CCFScriptCounter operator=(const CCFScriptCounter); // Declare without implementation +}; + +#endif diff --git a/src/CCJavaCsScalaCounter.cpp b/src/CCJavaCsScalaCounter.cpp new file mode 100644 index 0000000..e05d123 --- /dev/null +++ b/src/CCJavaCsScalaCounter.cpp @@ -0,0 +1,1396 @@ +//! Code counter class methods for the C/C++, Java, C# and Scala languages. +/*! +* \file CCJavaCsScalaCounter.cpp +* +* This file contains the code counter class methods for the C/C++, Java, C# and Scala languages. +*/ + +#include "UCCBeforeLibraryIncludes.h" +#include //Modification: 2018.01 +#include +#include //Modification: 2018.01 +#include "UCCAfterLibraryIncludes.h" + +#include "CCJavaCsScalaCounter.h" + +/*! +* Constructs a CCJavaCsScalaCounter object. +*/ +CCJavaCsScalaCounter::CCJavaCsScalaCounter( string lang ) +{ + QuoteStart = "\"'"; + QuoteEnd = QuoteStart; + quote_start_is_end = true; + + if ( "SCALA" == lang ) + { + // Either one double Quote character to start and end a string literal + // as above + // Or 3 double Quote characters to start and end a multiline string literal + QuoteMultiStart = "\"\"\""; + QuoteMultiEnd = QuoteMultiStart; + } + + quote_multi_start_is_end = true; + + QuoteEscapeFront = '\\'; + + if ( "SCALA" == lang ) // Scala does not have a line continuation symbol + ContinueLine = ""; + else + ContinueLine = "\\"; + + BlockCommentStart.push_back("/*"); + BlockCommentEnd.push_back("*/"); + + LineCommentStart.push_back("//"); + + // these properties are common for C/C++, Java, C# and Scala (specific where noted) + cmplx_calc_list.push_back("%"); + cmplx_calc_list.push_back("^"); + cmplx_calc_list.push_back("++"); + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("--"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("/"); + cmplx_calc_list.push_back(">>"); + cmplx_calc_list.push_back("<<"); + + cmplx_cond_list.push_back("case"); + cmplx_cond_list.push_back("else"); + cmplx_cond_list.push_back("else if"); + cmplx_cond_list.push_back("for"); + cmplx_cond_list.push_back("if"); + + if ( "SCALA" == lang ) + cmplx_cond_list.push_back("match"); // switch not in Scala + else + cmplx_cond_list.push_back("switch"); + + cmplx_cond_list.push_back("while"); + + if ( "SCALA" != lang ) // Scala does not have trinary compare operator + cmplx_cond_list.push_back("?"); + + cmplx_logic_list.push_back("&&"); // 1 pass parser uses this for Cyclomatic complexity ring 2 CC2 + cmplx_logic_list.push_back("||"); // 1 pass parser uses this for Cyclomatic complexity ring 2 CC2 + cmplx_logic_list.push_back("=="); + cmplx_logic_list.push_back("!"); + cmplx_logic_list.push_back(">"); + cmplx_logic_list.push_back("<"); + cmplx_logic_list.push_back(">="); + cmplx_logic_list.push_back("=<"); + + cmplx_assign_list.push_back("="); + + if ( "SCALA" == lang ) + cmplx_assign_list.push_back("<-"); // Scala also uses left pointing arrow for assignment + +// Set up Auxillary meanings list of Keywords or Operations and related list of values +// +// This is different from using cmplx_cyclomatic_list as the multiple pass parser does. +// Instead an extra field aux is set from the below that gives Cyclomatic, OO, FP info. +// + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_listVals.push_back( CYCLOMATIC_CC_ALL ); + + cmplx_cyclomatic_list.push_back("case"); + cmplx_cyclomatic_listVals.push_back( CYCLOMATIC_EXCEPT_CC3 ); + + cmplx_cyclomatic_list.push_back("while"); + cmplx_cyclomatic_listVals.push_back( CYCLOMATIC_CC_ALL ); + + cmplx_cyclomatic_list.push_back("for"); + cmplx_cyclomatic_listVals.push_back( CYCLOMATIC_CC_ALL ); + + if ( "C_SHARP" == lang ) + { + cmplx_cyclomatic_list.push_back("foreach"); + cmplx_cyclomatic_listVals.push_back( CYCLOMATIC_CC_ALL ); + } + + // Exception handler clause + cmplx_cyclomatic_list.push_back("catch"); + cmplx_cyclomatic_listVals.push_back( CYCLOMATIC_CC_ALL ); + + // ? trinary compare is not used for CC1 to CC4 metrics + if ( "SCALA" != lang ) // Scala does not have trinary compare operator + cmplx_cyclomatic_list.push_back("?"); + + + cmplx_cyclomatic_logic_list.push_back("||"); + cmplx_cyclomatic_logic_list.push_back("&&"); + + + cmplx_cyclomatic_case_list.push_back("case"); + cmplx_cyclomatic_switch_list.push_back("switch"); + + //Modification: 2018.01 Integration Starts + three_char_operator_list.push_back("<<="); + three_char_operator_list.push_back(">>="); + + two_char_operator_list.push_back("||"); + two_char_operator_list.push_back("&&"); + two_char_operator_list.push_back("=="); + two_char_operator_list.push_back("!="); + two_char_operator_list.push_back("<="); + two_char_operator_list.push_back(">="); + two_char_operator_list.push_back("<<"); + two_char_operator_list.push_back(">>"); + two_char_operator_list.push_back("++"); + two_char_operator_list.push_back("--"); + two_char_operator_list.push_back("+="); + two_char_operator_list.push_back("-="); + two_char_operator_list.push_back("&="); + two_char_operator_list.push_back("|="); + two_char_operator_list.push_back("+="); + two_char_operator_list.push_back("-="); + two_char_operator_list.push_back("*="); + two_char_operator_list.push_back("/="); + two_char_operator_list.push_back("^="); + two_char_operator_list.push_back("%="); + + one_char_operator_list.push_back("="); + one_char_operator_list.push_back("+"); + one_char_operator_list.push_back("-"); + one_char_operator_list.push_back("*"); + one_char_operator_list.push_back("/"); + one_char_operator_list.push_back("~"); + one_char_operator_list.push_back("^"); + one_char_operator_list.push_back("&"); + one_char_operator_list.push_back("|"); + one_char_operator_list.push_back(">"); + one_char_operator_list.push_back("<"); + + one_char_operator_list.push_back("("); + one_char_operator_list.push_back(")"); + one_char_operator_list.push_back("["); + one_char_operator_list.push_back("]"); + one_char_operator_list.push_back("{"); + one_char_operator_list.push_back("}"); + one_char_operator_list.push_back("!"); + one_char_operator_list.push_back("%"); + one_char_operator_list.push_back(":"); + one_char_operator_list.push_back("?"); + one_char_operator_list.push_back(";"); + one_char_operator_list.push_back(","); + one_char_operator_list.push_back("."); + one_char_operator_list.push_back("!"); + one_char_operator_list.push_back("%"); + one_char_operator_list.push_back("\\"); + + //Modification: 2018.01 Integration ends + +// TODO: Add more keywords/values for Object Orientation and Functional Programming metrics collection + +} + +/*! +* Counts directive lines of code. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CCJavaCsScalaCounter::CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak) +{ + bool contd = false, trunc_flag = false; + size_t idx, strSize; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string strDirLine = ""; + + // Because of Polymorphism change step above to show running here. + ENTER_1( "CCJavaCsScalaCounter::CountDirectiveSLOC" ); + SAVE_TO_2( "CCJavaCsScalaCounter::CountDirectiveSLOC loop start" ); + currentPhyLine = 0; + filemap::iterator itfmBak = fmapBak->begin(); + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++, itfmBak++) + { + currentPhyLine++; + SAVE_TO_2( "CUtil::CheckBlank" ); + if (CUtil::CheckBlank(iter->line)) + continue; + size_t lineNumber = iter->lineNumber; + + if (print_cmplx) + { + cnt = 0; + SAVE_TO_2( "CUtil::CountTally directive" ); + CUtil::CountTally(" " + iter->line, directive, cnt, 1, exclude, "", "", &result->directive_count); + } + + if (!contd) + { + // if not a continuation of a previous directive + for (vector::iterator viter = directive.begin(); viter != directive.end(); viter++) + { + // ensures the keyword stands alone, avoid, e.g., #ifabc + SAVE_TO_2( "CUtil::FindKeyword directive" ); + if (((idx = CUtil::FindKeyword(iter->line, *viter)) != string::npos) && idx == 0) + { + contd = true; + break; + } + } + if (contd) + { + SAVE_TO_2( "CUtil::TruncateLine Not continuation" ); + strSize = CUtil::TruncateLine(itfmBak->line.length(), 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine = itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + } + else + { + // continuation of a previous directive + SAVE_TO_2( "CUtil::TruncateLine Continuation" ); + strSize = CUtil::TruncateLine(itfmBak->line.length(), strDirLine.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine += "\n" + itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + + if (contd) + { + // drop continuation symbol + if (strDirLine[strDirLine.length()-1] == '\\') + strDirLine = strDirLine.substr(0, strDirLine.length()-1); + + // if a directive or continuation of a directive (no continuation symbol found) + if (iter->line[iter->line.length()-1] != ',' && iter->line[iter->line.length()-1] != '\\') + { + contd = false; + SAVE_TO_2( "result->addSLOC" ); + if (result->addSLOC(strDirLine, lineNumber, trunc_flag)) + result->directive_lines[LOG]++; + } + iter->line = ""; + } + } + + SAVE_TO_2( "Exit CCJavaCsScalaCounter::CountDirectiveSLOC" ); + return 1; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CCJavaCsScalaCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + unsigned int paren_count = 0; + bool for_flag = false; + bool found_for = false; + bool found_forifwhile = false; + bool found_while = false; + char prev_char = 0; + bool data_continue = false; + bool inArrayDec = false; + string strLSLOC = ""; + string strLSLOCBak = ""; + unsigned int openBrackets = 0; + + filemap::iterator fit, fitbak; + string line, lineBak; + StringVector loopLevel; + + unsigned int phys_exec_lines = 0; + unsigned int phys_data_lines = 0; + unsigned int temp_lines = 0; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + // Because of Polymorphism change step above to show running here. + ENTER_1( "Start" ); + SAVE_TO_2( "Loop start" ); + currentPhyLine = 0; + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + currentPhyLine++; + line = fit->line; + size_t lineNumber = fit->lineNumber; + + // insert blank at the beginning (for searching keywords) + line = ' ' + line; + lineBak = ' ' + fitbak->line; + + // do not process blank lines + // blank line means blank_line/comment_line/directive + SAVE_TO_2( "CUtil::CheckBlank" ); + if (!CUtil::CheckBlank(line)) + { + SAVE_TO_2( "CCJavaCsCounter::LSLOC" ); + LSLOC(result, line, lineNumber, lineBak, strLSLOC, strLSLOCBak, paren_count, for_flag, found_forifwhile, found_while, + prev_char, data_continue, temp_lines, phys_exec_lines, phys_data_lines, inArrayDec, found_for, + openBrackets, loopLevel); + + if (print_cmplx) + { + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Exec name list" ); + CUtil::CountTally(line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count); + } + + result->exec_lines[PHY] += phys_exec_lines; + phys_exec_lines = 0; + + result->data_lines[PHY] += phys_data_lines; + phys_data_lines = 0; + } + } + + SAVE_TO_2( "Exit CCJavaCsScalaCounter::LanguageSpecificProcess" ); + return 1; +} + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results +* \param line processed physical line of code +* \param lineBak original physical line of code +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param paren_cnt count of parenthesis +* \param forflag found for flag +* \param found_forifwhile found for, if, or while flag +* \param found_while found while flag +* \param prev_char previous character +* \param data_continue continuation of a data declaration line +* \param temp_lines tracks physical line count +* \param phys_exec_lines number of physical executable lines +* \param phys_data_lines number of physical data lines +* \param inArrayDec marks an array declaration +* \param found_for found for loop +* \param openBrackets number of open brackets (no matching close bracket) +* \param loopLevel nested loop level +*/ +void CCJavaCsScalaCounter::LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, + string &strLSLOCBak, unsigned int &paren_cnt, + bool &forflag, bool &found_forifwhile, bool &found_while, char &prev_char, bool &data_continue, + unsigned int &temp_lines, unsigned int &phys_exec_lines, unsigned int &phys_data_lines, + bool &inArrayDec, bool &found_for, unsigned int &openBrackets, StringVector &loopLevel) +{ + // paren_cnt is used with 'for' statement only + size_t start = 0; //starting index of the working string + size_t i = 0, strSize; + bool found_do, found_try, found_else, trunc_flag = false; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$:"; + string dataExclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$:()."; // avoid double count of casts as data and executable lines (e.g. set { m_uiValue = (uint)value; } + + unsigned int cnt = 0; + + SAVE_TO_3( "CUtil::TrimString" ); + string tmp = CUtil::TrimString(strLSLOC); + + // do, try + SAVE_TO_3( "CUtil::FindKeyword" ); + found_do = (CUtil::FindKeyword(tmp, "do") != string::npos); + found_try = (CUtil::FindKeyword(tmp, "try") != string::npos); + // else is treated differently, else is included in SLOC, do and try are not + found_else = (CUtil::FindKeyword(tmp, "else") != string::npos); + + // there may be more than 1 logical SLOC in this line + while (i < line.length()) + { + switch (line[i]) + { + case ';': case '{': // LSLOC terminators + // ';' for normal executable or declaration statement + // '{' for starting a function or 'do' stmt or a block (which is counted) + // get the previous logical mark until i-1 index is the new LSLOC + // except 'do' precedes '{' + // except '}' precedes ';' ?? + // do nothing inside 'for' statement + if (found_for == true && paren_cnt > 0 && line[i] == ';') + break; + + // record open bracket for nested loop processing + if (print_cmplx) + { + if (line[i] == '{') + { + openBrackets++; + if ((unsigned int)loopLevel.size() < openBrackets) + loopLevel.push_back(""); + } + else + { + if ((unsigned int)loopLevel.size() > openBrackets && openBrackets > 0) + loopLevel.pop_back(); + } + } + + // case 'while(...);', 'while(...) {', and '} while(...);' + // this case is handled in case ')' + if (found_while && found_forifwhile) + { + found_while = false; + found_forifwhile = false; + start = i + 1; + break; + } + + if (line[i] == '{') + { + if (prev_char == '=') + inArrayDec = true; + + // continue until seeing ';' + if (inArrayDec) + break; + + // case for(...); and if (...) { + // these specials are handled + if (found_forifwhile) + { + found_forifwhile = false; + start = i + 1; + break; + } + + // check if 'do' precedes '{' + if (!found_do && !found_try && !found_else) + { + // find for 'do' in string before tmp string + SAVE_TO_3( "CUtil::TrimString_2" ); + tmp = CUtil::TrimString(line.substr(start, i - start)); + found_do = (tmp == "do"); // found 'do' statement + found_try = (tmp == "try"); // found 'try' statement + // same as else + found_else = (tmp == "else"); // found 'else' statement + } + if (found_do || found_try || found_else) + { + if (found_do && print_cmplx) + { + if (loopLevel.size() > 0) + loopLevel.pop_back(); + loopLevel.push_back("do"); + } + found_do = false; + found_try = false; + if (!found_else) + { + // everything before 'do', 'try' are cleared + strLSLOC = ""; + strLSLOCBak = ""; + start = i + 1; + } + break; // do not store '{' following 'do' + } + } + + // wrong, e.g., a[]={1,2,3}; + if (line[i] == ';' && prev_char == '}') + { + // check if in array declaration or not + // if no, skip, otherwise, complete the SLOC containing array declaration + if (!inArrayDec) + { + start = i + 1; + break; + } + } + + inArrayDec = false; + + // check for empty statement (=1 LSLOC) + SAVE_TO_3( "CUtil::TrimString_3" ); + if (CUtil::TrimString(line.substr(start, i + 1 - start)) == ";" && strLSLOC.length() < 1) + { + strLSLOC = ";"; + strLSLOCBak = ";"; + } + else + { + SAVE_TO_3( "CUtil::TruncateLine" ); + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + } + SAVE_TO_3( "result->addSLOC" ); + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + { + cnt = 0; + SAVE_TO_3( "CUtil::CountTally" ); + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, dataExclude, "", "", &result->data_name_count); + + temp_lines++; + if (data_continue == true && line[i] == ';') + { + result->data_lines[LOG]++; + phys_data_lines = temp_lines; + } + else + { + if (cnt > 0 && line[i] == ';') + { + result->data_lines[LOG]++; + phys_data_lines = temp_lines; + } + else + { + result->exec_lines[LOG]++; + phys_exec_lines = temp_lines; + } + } + } + else if (data_continue == true && line[i] == ';') + phys_data_lines = temp_lines; + else + phys_exec_lines = temp_lines; + data_continue = false; + temp_lines = 0; + strLSLOC = strLSLOCBak = ""; + start = i + 1; + + // reset some flagging parameters + forflag = false; + paren_cnt = 0; + found_while = false; + found_forifwhile = false; + found_for = false; + + break; + case '(': + if (forflag) + paren_cnt++; + else + { + // handle 'for', 'foreach', 'while', 'if' the same way + SAVE_TO_3( "CUtil::TrimString_4" ); + tmp = CUtil::TrimString(line.substr(start, i)); + SAVE_TO_3( "CUtil::FindKeyword_2" ); + if (CUtil::FindKeyword(tmp, "for") != string::npos + || CUtil::FindKeyword(tmp, "foreach") != string::npos + || CUtil::FindKeyword(tmp, "while") != string::npos + || CUtil::FindKeyword(tmp, "if") != string::npos) + { + forflag = true; + paren_cnt++; + + if (print_cmplx && (unsigned int)loopLevel.size() > openBrackets && openBrackets > 0) + loopLevel.pop_back(); + + SAVE_TO_3( "CUtil::FindKeyword_3" ); + if (CUtil::FindKeyword(tmp, "for") != string::npos) + { + if (print_cmplx) + loopLevel.push_back("for"); + found_for = true; + } + else if (CUtil::FindKeyword(tmp, "while") != string::npos) + { + if (print_cmplx) + loopLevel.push_back("while"); + found_while = true; + } + else if (print_cmplx && CUtil::FindKeyword(tmp, "foreach") != string::npos) + loopLevel.push_back("foreach"); + + // record nested loop level + if (print_cmplx) + { + SAVE_TO_3( "CUtil::FindKeyword_4" ); + if (CUtil::FindKeyword(tmp, "if") == string::npos) + { + unsigned int loopCnt = 0; + for (StringVector::iterator lit = loopLevel.begin(); lit < loopLevel.end(); lit++) + { + if ((*lit) != "") + loopCnt++; + } + if ((unsigned int)result->cmplx_nestloop_count.size() < loopCnt) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopCnt-1]++; + } + } + } + } + break; + case ')': + if (forflag) + { + if (paren_cnt > 0) + paren_cnt--; + if (paren_cnt == 0) + { + // handle 'for', 'foreach', 'while', 'if' + SAVE_TO_3( "CUtil::TruncateLine" ); + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + SAVE_TO_3( "result->addSLOC_2" ); + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + strLSLOCBak = strLSLOC = ""; + phys_exec_lines = temp_lines; + temp_lines = 0; + start = i + 1; + found_forifwhile = true; + forflag = false; + found_for = false; + } + } + break; + case '}': + // skip '}' when found ';' and then '}' because '{' is counted already + // also, {} is also skipped, counted + if (prev_char == ';' || prev_char == '{' || prev_char == '}') + if (!inArrayDec) start = i + 1; + + // record close bracket for nested loop processing + if (print_cmplx) + { + if (openBrackets > 0) + openBrackets--; + if (loopLevel.size() > 0) + loopLevel.pop_back(); + } + break; + } + + if (line[i] != ' ' && line[i] != '\t') + { + // if ;}}} --> don't count }}} at all + // also, if {}}} --> don't count }}} at all + // if ( !(line[i] == '}' && (prev_char == ';' || prev_char == '{'))) // see case '}' above + prev_char = line[i]; + + // change to not found if a char appears before + if (line[i] != ')' && found_forifwhile) + found_forifwhile = false; + } + i++; + } + + SAVE_TO_3( "CUtil::TrimString_5" ); + tmp = CUtil::TrimString(line.substr(start, i - start)); + SAVE_TO_3( "CUtil::TruncateLine_2" ); + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + SAVE_TO_3( "CUtil::TrimString_6" ); + tmp = CUtil::TrimString(lineBak.substr(start, i - start)); + strLSLOCBak += tmp.substr(0, strSize); + + // drop continuation symbol + if (strLSLOC[strLSLOC.length()-1] == '\\') + { + strLSLOC = strLSLOC.substr(0, strLSLOC.length()-1); + strLSLOCBak = strLSLOCBak.substr(0, strLSLOCBak.length()-1); + } + } + + // make sure that we are not beginning to process a new data line + cnt = 0; + SAVE_TO_3( "CUtil::CountTally_2" ); + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", NULL); + + if (cnt > 0) + data_continue = true; + if (data_continue) + temp_lines++; + if (temp_lines == 0 && phys_data_lines == 0 && phys_exec_lines == 0) + phys_exec_lines = 1; + + SAVE_TO_3( "Exit CCJavaCsScalaCounter::LSLOC" ); +} + +/*! +* Parses lines for function/method names. +* +* \param line line to be processed +* \param lastline last line processed +* \param functionStack stack of functions +* \param functionName function name found +* \param functionCount function count found +* +* \return 1 if function name is found +*/ +int CCJavaCsScalaCounter::ParseFunctionName(const string &line, string &lastline, + filemap &functionStack, string &functionName, unsigned int &functionCount) +{ + string tline, str; + size_t idx, tidx, cnt, cnt2; + unsigned int fcnt, cyclomatic_cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + // Because of Polymorphism change step above to show running here. + ENTER_2( "CCJavaCsScalaCounter::ParseFunctionName" ); + SAVE_TO_3( "CUtil::TrimString" ); + tline = CUtil::TrimString(line); + idx = tline.find('{'); + if (idx != string::npos) + { + // check whether it is at first index, if yes then function name is at above line + if (idx == 0) + { + lineElement element(++functionCount, lastline); + functionStack.push_back(element); + lastline.erase(); + } + else + { + str = tline.substr(0, idx); + tidx = cnt = cnt2 = 0; + if (str[0] != '(' && str[0] != ':' && (lastline.length() < 1 || lastline[lastline.length() - 1] != ':')) + { + while (tidx != string::npos) + { + tidx = str.find('(', tidx); + if (tidx != string::npos) + { + cnt++; + tidx++; + } + } + if (cnt > 0) + { + tidx = 0; + while (tidx != string::npos) + { + tidx = str.find(')', tidx); + if (tidx != string::npos) + { + cnt2++; + tidx++; + } + } + } + } + // make sure parentheses are closed and no parent class listed + if ((cnt > 0 && cnt == cnt2) || (lastline.length() > 0 && lastline[lastline.length() - 1] == ';')) + lastline = str; + else + lastline += " " + str; + lineElement element(++functionCount, CUtil::TrimString(lastline)); + functionStack.push_back(element); + lastline.erase(); + } + } + else if (tline.length() > 0 && tline[tline.length() - 1] != ';' && + lastline.length() > 0 && lastline[lastline.length() - 1] != ';') + { + // append until all parentheses are closed + tidx = lastline.find('('); + if (tidx != string::npos) + { + cnt = 1; + while (tidx != string::npos) + { + tidx = lastline.find('(', tidx + 1); + if (tidx != string::npos) + cnt++; + } + tidx = lastline.find(')'); + while (tidx != string::npos) + { + cnt++; + tidx = lastline.find(')', tidx + 1); + } + if (cnt % 2 != 0) + lastline += " " + tline; + else + lastline = tline; + } + else + lastline = tline; + } + else + lastline = tline; + + idx = line.find('}'); + if (idx != string::npos && !functionStack.empty()) + { + str = functionStack.back().line; + fcnt = functionStack.back().lineNumber; + functionStack.pop_back(); + idx = str.find('('); + + if (idx != string::npos) + { + // search for cyclomatic complexity keywords and other possible keywords + SAVE_TO_3( "CUtil::CountTally cyclomatic complexity keywords and other" ); + CUtil::CountTally(str, cmplx_cyclomatic_list, cyclomatic_cnt, 1, exclude, "", "", 0, casesensitive); + + SAVE_TO_3( "CUtil::FindKeyword" ); + if (cyclomatic_cnt <= 0 && CUtil::FindKeyword(str, "switch") == string::npos && + CUtil::FindKeyword(str, "try") == string::npos && CUtil::FindKeyword(str, "finally") == string::npos && + CUtil::FindKeyword(str, "return") == string::npos && str.find('=') == string::npos) + { + SAVE_TO_3( "CUtil::ClearRedundantSpaces" ); + string myFunctionName = CUtil::ClearRedundantSpaces(str.substr(0, idx)); + + #ifdef _DEBUG + if ( myFunctionName.size() == 0 ) + { + // PrintCyclomaticComplexity will now skip any empty Function Names. + // Done this way as is easier and less Risk of adding Defects from complex Cyclomatic Complexity code + // TODO: Go through the CC code and the ParseFunctionName code and try to SAFELY improve. + // cout << endl << "CCJavaCsScalaCounter::ParseFunctionName() Empty function name allowed" << endl; + } + #endif + // May have the actual arg list so look for first ( and do not copy + size_t myIdx = myFunctionName.find( '(' ); + if ( myIdx != string::npos ) + myFunctionName = myFunctionName.substr( 0, myIdx ); + functionName = myFunctionName; + functionCount = fcnt; + lastline.erase(); + SAVE_TO_3( "Exit CCJavaCsScalaCounter::ParseFunctionName Found" ); + return 1; + } + } + lastline.erase(); + } + + SAVE_TO_3( "Exit CCJavaCsScalaCounter::ParseFunctionName Not found" ); + return 0; +} + +//Modification: 2018.01 Integration starts + +/*! +* Finds the first unescaped quote occurring in a line, after position start_pos. +* Used to determine the index of the ending quote of a string. +* +* \param line a source line +* \param start_pos the index into 'line' at which a quote character has been found, we start searching from start_pos + 1 +* \param quote the quote character to look for (for our use, it will be a single quote ' or a double quote ") +* \param quote_escape the string that is used to escape the passed in quote (e.g. the string \" or \') +* +* \return the index into line at which the next (after start_pos) unescaped quote is found +*/ +int CCJavaCsScalaCounter::FindIdxOfNextQuote(const string &line, int start_pos, const char "e) { + size_t idx = line.find(quote, start_pos + 1); //Modification: 2018.01, changed data type + int num_backslash; + int curr_idx; + + while (idx != string::npos) { + num_backslash = 0; + + // Count consecutive backslashes before idx + curr_idx = idx - 1; + while (curr_idx >= start_pos + 1 && line[curr_idx] == '\\') { + curr_idx--; + num_backslash++; + } + + // If even number of backslashes then the quote at idx is a quote for ending the string/char + // otherwise it's not and we have to continue looking + if (num_backslash % 2 == 0) { + break; + } else { + idx = line.find(quote, idx + 1); + } + } + + return idx; +} + +/*! +* Removes all strings and chars in 'line' and stores the count of them if add_to_count is true +* +* \param line a source line. line will be modified +* \param string_char_counts the map for storing the counts of strings and chars +* \param add_to_count true to indicate that we should store the detected string and char counts into string_char_counts +* \return 0 if no error and return 1 if 'line' begins a string a string or char but does not end it +*/ +int CCJavaCsScalaCounter::RemoveStringsAndChars(string &line, map &string_char_counts, const bool &add_to_count) { + // escape quotes + char double_quote = '"'; + char single_quote = '\''; + + //Modification: 2018.01, changed data types to size_t + size_t next_double_quote_idx = -1; + size_t next_single_quote_idx = -1; + + size_t idx; + + idx = line.find("'\"'"); + // Count and remove double quotes within single quotes (i.e. '"') + while (idx != string::npos) { + line.replace(idx, 3, " "); + if (add_to_count) + string_char_counts["\""]++; + idx = line.find("'\"'", idx + 1); + } + + idx = line.find("'\\\"'"); + // Count and remove double quotes within single quotes (i.e. '\"') + while (idx != string::npos) { + line.replace(idx, 4, " "); + if (add_to_count) + string_char_counts["\\\""]++; + idx = line.find("'\\\"'", idx + 1); + } + + idx = line.find(double_quote); + + // Count and remove strings + while (idx != string::npos) { + // if next_double_quote is string::npos, error out + // assume fine for now + next_double_quote_idx = FindIdxOfNextQuote(line, idx, double_quote); + if (next_double_quote_idx == string::npos) { + return 1; + } + + if (add_to_count) + string_char_counts[line.substr(idx, next_double_quote_idx - idx + 1)]++; + line.replace(idx, next_double_quote_idx - idx + 1, " "); + idx = line.find(double_quote, idx + 1); + } + + idx = line.find(single_quote); + + // Count and remove chars + while (idx != string::npos) { + next_single_quote_idx = FindIdxOfNextQuote(line, idx, single_quote); + if (next_single_quote_idx == string::npos) { + return 1; + } + + + if (add_to_count) + string_char_counts[line.substr(idx, next_single_quote_idx - idx + 1)]++; + line.replace(idx, next_single_quote_idx - idx + 1, " "); + idx = line.find(single_quote, idx + 1); + } + + return 0; +} + +/*! +* Removes 'symbols' from 'line' except where '(' belongs +* to a function call (e.g. the line "somecall()" would become "somecall( "). +* For this function, a 'symbol' is considered to be those +* in two_char_operator_list or one_char_operator_list. +* +* \param line a source line. line will be modified +* \param string_char_counts the map for storing the counts of nonfunction operators +* \param should_count true to indicate that we should store the detected string and char counts into nonfunction_operator_counts +*/ +void CCJavaCsScalaCounter::RemoveSymbolsExceptOpenRoundBracketOfFunction(string &line, map &nonfunction_operator_counts, const bool &should_count) { + size_t idx; //Modification: 2018.01 changed data type + + for (size_t i = 0; i < three_char_operator_list.size(); i++) { + idx = line.find(three_char_operator_list[i]); + + while (idx != string::npos) { + if (should_count) + nonfunction_operator_counts[three_char_operator_list[i]]++; + + line.replace(idx, 3, " "); + idx = line.find(three_char_operator_list[i], idx + 1); + } + } + + for (size_t i = 0; i < two_char_operator_list.size(); i++) { + idx = line.find(two_char_operator_list[i]); + + // Keep finding the symbol two_char_operator_list[i] + // and replacing it with a space " " until no more are found + while (idx != string::npos) { + if (should_count) + nonfunction_operator_counts[two_char_operator_list[i]]++; + + line.replace(idx, 2, " "); + idx = line.find(two_char_operator_list[i], idx + 1); + } + } + + // Count and replace one character operators + for (size_t i = 0; i < one_char_operator_list.size(); i++) { + idx = line.find(one_char_operator_list[i]); + + // Keep finding the symbol one_char_operator_list[i] + // and replacing it with a space " " until no more are found + while (idx != string::npos) { + if (one_char_operator_list[i] != "(" || + (one_char_operator_list[i] == "(" && (idx == 0 || !IsAlphanumericOrUnderscore(line[idx - 1])))) { + line.replace(idx, 1, " "); + + if (should_count) + nonfunction_operator_counts[one_char_operator_list[i]]++; + } + + idx = line.find(one_char_operator_list[i], idx + 1); + } + } +} + +/*! +* Checks if a source line contains a function call or a function definition +* and stores the name of this function, if any, in function_name +* +* \param line a source line +* \param function_name stores the name of the first function called or defined in 'line', if any is found +* \return true if 'line' has a function call or a function definition +*/ +bool CCJavaCsScalaCounter::IsEitherFunctionDefinitionOrInvocation(string line, string &function_name) { + int idx; + line = CUtil::TrimString(line, 0); + map unused_map; + RemoveStringsAndChars(line, unused_map, false); + + // RemoveSymbolsExceptOpenRoundBracketOfFunction(modified_line, unused_map, false); + idx = line.find_first_of("("); + if (idx > 0 && IsAlphanumericOrUnderscore(line[idx - 1])) { + function_name = line.substr(0, idx); + return true; + } + return false; +} + +/*! +* Checks if a source line contains a function call function definition. +* If it does contain a function definition, then the name of the function +* is stored in function_name. +* The implementation idea is to look for either a semicolon or an opening curly brace. +* +* \param line_idx the index into fmap from which we can extract the source line +* \param fmap the filemap of the file +* \param function_name stores the name of the first function called or defined in 'line', if any is found +* \return true if 'line' has a function call or a function definition +*/ +bool CCJavaCsScalaCounter::IsFunctionDefinition(int line_idx, filemap &fmap, string &function_name) { + unsigned int curr_line_idx = line_idx; + bool result = false; + string tline = CUtil::TrimString(fmap[line_idx].line, 1); + if (IsEitherFunctionDefinitionOrInvocation(tline, function_name)) { + // find first of ';' and '{' + size_t idx_of_semicolon = tline.find(";"); + size_t idx_of_open_curly = tline.find("{"); + while (curr_line_idx < fmap.size() - 1 && + idx_of_semicolon == string::npos && + idx_of_open_curly == string::npos) { + curr_line_idx++; + tline = CUtil::TrimString(fmap[curr_line_idx].line, 1); + idx_of_semicolon = tline.find(";"); + idx_of_open_curly = tline.find("{"); + } + + // the line is a function definition if we found a '{' + // and either 1) we didn't find a semicolon, or + // 2) we found a semicolon but the first one we found was after the first '{' we found + if (idx_of_open_curly != string::npos) + if (idx_of_semicolon == string::npos || idx_of_open_curly < idx_of_semicolon) + result = true; + } + + return result; +} + +/*! +* Adds the counts from add_from_counts to the counts in into_counts +* +* \param add_from_counts the map containing the source data of the counts +* \param into_counts the map that is added to +*/ +void CCJavaCsScalaCounter::SumUp(map > &add_from_counts, map &into_counts) { + for (map >::iterator func_iter = add_from_counts.begin(); func_iter != add_from_counts.end(); func_iter++) { + map op_counts = func_iter->second; + for (map::iterator op_to_count = op_counts.begin(); op_to_count != op_counts.end(); op_to_count++) + into_counts[op_to_count->first] += op_to_count->second; + } +} + +/*! +* Determines if we have arrived at the end of a function (by using the count of open and ending curly braces) +* +* \param nonfunction_operator_counts the map containing the counts of the nonfunction operators seen +* \return true if there is more than one '{' in nonfunction_operator_counts and the number of '{' equals the number of '}' +*/ +bool CCJavaCsScalaCounter::IsLastLineOfFunction(map &nonfunction_operator_counts) { + return nonfunction_operator_counts["{"] > 0 && + nonfunction_operator_counts["{"] == nonfunction_operator_counts["}"]; +} + +/*! +* Calculates Halstead's volume using the counts passed in +* +* \param string_char_counts the map containing the counts of strings and chars seen +* \param word_counts the map containing the counts of items such as variable names (these are considered operators) +* \param bool_counts the map containing the counts of bools (true and false) +* \param number_counts the map containing the counts of numbers seen (e.g. integers, floats, etc) +* \param function_counts the map containing the counts of functions (e.g. printf, user-defined functions invoked, etc) +* \param nonfunction_operator_counts the map containing the counts of the nonfunction operators (e.g. counts of '+', '-', etc) +* \return Halstead's volume or -1.0 if the total number of unique operators plus the total number of unique operands is zero +*/ +double CalculateHalsteadsVolumeFromCounts(map &string_char_counts, + map &word_counts, + map &bool_counts, + map &number_counts, + map &function_counts, + map &nonfunction_operator_counts) { + unsigned int total_operators = 0; + unsigned int total_operands = 0; + + unsigned int unique_operators = 0; + unsigned int unique_operands = 0; + + vector > counts_list; + counts_list.push_back(string_char_counts); + counts_list.push_back(word_counts); + counts_list.push_back(bool_counts); + counts_list.push_back(number_counts); + counts_list.push_back(function_counts); + counts_list.push_back(nonfunction_operator_counts); + + for (vector >::iterator ot = counts_list.begin(); ot != counts_list.end(); ot++) { + for (map::iterator it = ot->begin(); it != ot->end(); it++) { + total_operands += it->second; + unique_operands++; + } + } + + if (unique_operators + unique_operands == 0) + return -1.0; + + return (double)(total_operators + total_operands) * log2((double)(unique_operators + unique_operands)); +} + +/*! +* True if a char is a letter, number, or underscore +* This is used because variable names are a combination of letters, numbers, and underscores +* +* \param c the character in consideration +* \return true if c is a letter, a number, or an underscore, otherwise returns false +*/ +bool CCJavaCsScalaCounter::IsAlphanumericOrUnderscore(const char &c) { + return isalnum(c) || c == '_'; +} + +/*! +* Counts the operators and operands in a source line and stores the counts in the maps +* +* \param string_char_counts the map for storing the counts of strings and chars seen +* \param word_counts the map for storing the counts of items such as variable names (these are considered operators) +* \param bool_counts the map for storing the counts of bools (true and false) +* \param number_counts the map for storing the counts of numbers seen (e.g. integers, floats, etc) +* \param function_counts the map for storing the counts of functions (e.g. printf, user-defined functions invoked, etc) +* \param nonfunction_operator_counts the map for storing the counts of the nonfunction operators (e.g. counts of '+', '-', etc) +* \return 0 if no error and return 1 if 'line' begins a string a string or char but does not end it +*/ +int CCJavaCsScalaCounter::CountOperatorsAndOperands(string &line, + map &string_char_counts, + map &word_counts, + map &bool_counts, + map &number_counts, + map &function_counts, + map &nonfunction_operator_counts) { + + int count = 0; + int status = 0; + + string curr_token; + + + // Count the strings in 'tline' and remove them from 'tline' + // so that we can work on the line without these strings + status = RemoveStringsAndChars(line, string_char_counts, true); + if (status != 0) + return 1; + + RemoveSymbolsExceptOpenRoundBracketOfFunction(line, nonfunction_operator_counts, true); + + // Insert a space after every ( + size_t idx = line.find("("); //Modification: 2018.01, changed data type to size_t + while (idx != string::npos) { + line.replace(idx, 1, "( "); + idx = line.find("(", idx + 2); + } + + // Get tokens using the blank space as the delimiter + // Tokenize line + vector tokens; + istringstream iss(line); + string tmp_token; + while (iss >> tmp_token) { + tokens.push_back(tmp_token); + } + + string token; + // Parse tokens for variables, numbers, and bools + for (vector::iterator it = tokens.begin(); it != tokens.end(); it++) { + token = *it; + if (token.size() == 0) + continue; + + if (keyword_operators.find(token) != keyword_operators.end()) { + nonfunction_operator_counts[token]++; + } else if (token[token.size() - 1] == '(') { + function_counts[(token.substr(0, token.size() - 1))]++; + nonfunction_operator_counts["("]++; + } else if (token[0] >= '0' && token[0] <= '9') { + // number + number_counts[token]++; + count++; + } else if (token == "true" || token == "false") { + // boolean + bool_counts[token]++; + } else if (IsAlphanumericOrUnderscore(token[0])) { + word_counts[token]++; + } + } + return 0; +} + +/*! +* Calculates Halstead's volume for the file and for each function within the file, +* storing the computed values in result. +* +* \param fmapModBak the filemap containing the lines of the file without any comments +* \param result the results structure that contains variables for storing Halstead's volume +* \return 0 if the function did not encounter any errors or return 1 if there was an error +*/ +int CCJavaCsScalaCounter::FindHalsteadsVolume(filemap fmapModBak, results* result) { + size_t curr_line_idx = 0; + bool has_function_name = false; + string line; + string function_name = ""; + map curr_string_and_char_operand_counts; + map curr_word_operand_counts; + map curr_bool_operand_counts; + map curr_number_operand_counts; + map curr_function_operator_counts; + map curr_nonfunction_operator_counts; + bool encountered_error = false; + int status = 0; + + while (curr_line_idx < fmapModBak.size()) { + line = CUtil::TrimString(fmapModBak[curr_line_idx].line, 0); + has_function_name = IsFunctionDefinition(curr_line_idx, fmapModBak, function_name); + + if (has_function_name) { + // process until end of function and calculate halsteads volume and put into class variable + // reset counts for functions + curr_string_and_char_operand_counts.clear(); + curr_word_operand_counts.clear(); + curr_bool_operand_counts.clear(); + curr_number_operand_counts.clear(); + + curr_function_operator_counts.clear(); + curr_nonfunction_operator_counts.clear(); + + while (true) { + //Modification : 2018.05 Function not being used + /*curr_line_idx = GetLineUntilEndOfMultistringIfAny(curr_line_idx, line, fmapModBak, curr_nonfunction_operator_counts);*/ + + status = CountOperatorsAndOperands(line, + curr_string_and_char_operand_counts, + curr_word_operand_counts, + curr_bool_operand_counts, + curr_number_operand_counts, + curr_function_operator_counts, + curr_nonfunction_operator_counts); + if (status != 0) + return 1; + + if (IsLastLineOfFunction(curr_nonfunction_operator_counts) || + curr_line_idx >= fmapModBak.size() - 1) { + // Store per function counts + result->func_string_and_char_operand_counts[function_name] = curr_string_and_char_operand_counts; + result->func_word_operand_counts[function_name] = curr_word_operand_counts; + result->func_bool_operand_counts[function_name] = curr_bool_operand_counts; + result->func_number_operand_counts[function_name] = curr_number_operand_counts; + + result->func_function_operator_counts[function_name] = curr_function_operator_counts; + result->func_nonfunction_operator_counts[function_name] = curr_nonfunction_operator_counts; + + result->func_halsteads_volume[function_name] = CalculateHalsteadsVolumeFromCounts( + curr_string_and_char_operand_counts, + curr_word_operand_counts, + curr_bool_operand_counts, + curr_number_operand_counts, + curr_function_operator_counts, + curr_nonfunction_operator_counts); + if (result->func_halsteads_volume[function_name] < 0) + encountered_error = true; + + break; + } + + curr_line_idx++; + line = fmapModBak[curr_line_idx].line; + } + } else { + //Modification : 2018.05 Function not being used + /*curr_line_idx = GetLineUntilEndOfMultistringIfAny(curr_line_idx, line, fmapModBak, result->file_nonfunction_operator_counts);*/ + + // add to file count only + status = CountOperatorsAndOperands(line, + result->file_string_and_char_operand_counts, + result->file_word_operand_counts, + result->file_bool_operand_counts, + result->file_number_operand_counts, + result->file_function_operator_counts, + result->file_nonfunction_operator_counts); + if (status != 0) + return 1; + } + + curr_line_idx++; + } + + // Calculator halsteads volume for file + // but first sum up all the per-function operators and operands to get the counts for the whole file + SumUp(result->func_string_and_char_operand_counts, result->file_string_and_char_operand_counts); + SumUp(result->func_word_operand_counts, result->file_word_operand_counts); + SumUp(result->func_bool_operand_counts, result->file_bool_operand_counts); + SumUp(result->func_number_operand_counts, result->file_number_operand_counts); + + // eg + - == and keywords + SumUp(result->func_nonfunction_operator_counts, result->file_nonfunction_operator_counts); + + // eg printf + SumUp(result->func_function_operator_counts, result->file_function_operator_counts); + + // // Calculate Halsteads volume of the file + result->halsteads_volume = CalculateHalsteadsVolumeFromCounts(result->file_string_and_char_operand_counts, + result->file_word_operand_counts, + result->file_bool_operand_counts, + result->file_number_operand_counts, + result->file_function_operator_counts, + result->file_nonfunction_operator_counts); + + if (result->halsteads_volume < 0) + encountered_error = true; + + if (encountered_error) + return 1; + + return 0; +} + +//Modification: 2018.01 Integration ends diff --git a/src/CCJavaCsScalaCounter.h b/src/CCJavaCsScalaCounter.h new file mode 100644 index 0000000..349c0c1 --- /dev/null +++ b/src/CCJavaCsScalaCounter.h @@ -0,0 +1,67 @@ +//! Code counter class definition for the C/C++, Java, and C# languages. +/*! +* \file CCJavaCsCounter.h +* +* This file contains the code counter class definition for the C/C++, Java, and C# languages. +*/ + +#ifndef CCJavaCsScalaCounter_h +#define CCJavaCsScalaCounter_h + +#include "CCodeCounter.h" + +//! C/C++, Java, C# and Scala code counter common base class. +/*! +* \class CCJavaCsCounter +* +* Defines the C/C++, Java, C# and Scala code counter class. +*/ +class CCJavaCsScalaCounter : public CCodeCounter +{ +public: + CCJavaCsScalaCounter( string lang = "LANGUAGE_NOT_SET" ); + +protected: + virtual int CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapmBak = NULL); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapmBak = NULL); + void LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + unsigned int &paren_cnt, bool &forflag, bool &found_forifwhile, bool &found_while, char &prev_char, bool &data_continue, + unsigned int &temp_lines, unsigned int &phys_exec_lines, unsigned int &phys_data_lines, + bool &inArrayDec, bool &found_for, unsigned int &openBrackets, StringVector &loopLevel); + virtual int ParseFunctionName(const string &line, string &lastline, + filemap &functionStack, string &functionName, unsigned int &functionCount); + + //Modification: 2018.01 Integration starts + int FindIdxOfNextQuote(const string &line, int start_pos, const char "e); + int RemoveStringsAndChars(string &line, map &string_char_counts, const bool &add_to_count); + bool IsEitherFunctionDefinitionOrInvocation(string line, string &function_name); + bool IsFunctionDefinition(int line_idx, filemap &fmap, string &function_name); + void SumUp(map > &add_from_counts, map &into_counts); + bool IsLastLineOfFunction(map &nonfunction_operator_counts); + int CountOperatorsAndOperands(string &line, + map &string_char_counts, + map &word_counts, + map &bool_counts, + map &number_counts, + map &function_counts, + map &nonfunction_operator_counts); + void RemoveSymbolsExceptOpenRoundBracketOfFunction(string &line, map &nonfunction_operator_counts, const bool &should_count); + bool IsAlphanumericOrUnderscore(const char &c); + virtual int GetLineUntilEndOfMultistringIfAny(int, string &, filemap &, map &) { return -1; } //Modification: 2018.05. Integration + virtual int FindHalsteadsVolume(filemap fmapModBak, results* result); + //Modification: 2018.01 Integration ends + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CCJavaCsScalaCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CCJavaCsScalaCounter(const CCJavaCsScalaCounter& rhs); + + // Take care of warning C4626: 'CCJavaCsScalaCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CCJavaCsScalaCounter operator=(const CCJavaCsScalaCounter); +}; + +#endif diff --git a/src/CCobolCounter.cpp b/src/CCobolCounter.cpp new file mode 100644 index 0000000..c045fe9 --- /dev/null +++ b/src/CCobolCounter.cpp @@ -0,0 +1,979 @@ +//! Code counter class methods for the Cobol language. +/*! +* \file CCobolCounter.cpp +* +* This file contains the code counter class methods for the Cobol language. +* +* References: +* Wikipedia COBOL language +* +* COBOL 85 Grammer Downloaded grammer text file COBOL85.grm +* Revision: 09-13-06: by Devin Cook +* +* COBOL grammar Version 0.1.1.pdf +* 1999 by Ralf Lammel +* +* Created: 2011? (not sure when or who, not released?) Contributors: QINGYU ZHOU, Nipun, PEI-CHEN +* Revised: 2015.12 by Randy Maxwell +*/ + +#include "CCobolCounter.h" + +/*! +* Constructs a CCobolCounter object. +*/ +CCobolCounter::CCobolCounter() +{ + classtype = COBOL; + language_name = "COBOL"; + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("COBOL", file_extension); + /* + file_extension.push_back(".cbl"); + file_extension.push_back(".cob"); + file_extension.push_back(".cpy");*/ + + QuoteStart = "\"'"; + QuoteEnd = "\"'"; + //casesensitive = false; + + exclude_keywords.push_back("ELSE"); + exclude_keywords.push_back("END-EVALUATE"); + exclude_keywords.push_back("END-IF"); + exclude_keywords.push_back("END-IF."); + exclude_keywords.push_back("END-PERFORM"); + exclude_keywords.push_back("END-SEARCH"); + exclude_keywords.push_back("THEN"); + + // added by Nipun + exclude_keywords.push_back("EXCEPTION"); + exclude_keywords.push_back("ON EXCEPTION"); + exclude_keywords.push_back("NOT ON EXCEPTION"); + exclude_keywords.push_back("END-XML"); + // -- FINISHED ADDING + + sort( exclude_keywords.begin(), exclude_keywords.end() ); + + + exclude_start_keywords.push_back("END"); + exclude_start_keywords.push_back("STOP"); + + + //In COBOL, there are only Line Comments + // and they start with either * or / + //An asterisk (*) comment line is printed in the output listing, immediately following the last preceding line. + //A slash (/) comment line is printed on the first line of the next page, and the current page of the output listing is ejected. + // There are no Block comments in COBOL + LineCommentStart.push_back("*"); + LineCommentStart.push_back("/"); + LineCommentStart.push_back("'"); + + // === ADDED BY QINGYU ZHOU === + directive.push_back("BASIS"); + directive.push_back("DELETE"); + directive.push_back("REPLACE"); + directive.push_back("RESET"); + directive.push_back("CBL"); + directive.push_back("EJECT"); + directive.push_back("SERVICE"); + directive.push_back("LABEL"); + directive.push_back("PROCESS"); + directive.push_back("ENTER"); + directive.push_back("TITLE"); + directive.push_back("RELOAD"); + directive.push_back("CONTROL"); + directive.push_back("INSERT"); + directive.push_back("DEBUGGING"); + directive.push_back("COPY"); + directive.push_back("TRACE"); + directive.push_back("READY"); + directive.push_back("PROCEDURE"); + // === FINISHED ADDING === + + // Added by Nipun + directive.push_back("*CONTROL"); + directive.push_back("*CBL"); + directive.push_back("*CONTROL"); + directive.push_back("SKIP1"); + directive.push_back("SKIP2"); + directive.push_back("SKIP3"); + directive.push_back("SERVICE RELOAD"); + directive.push_back("RESET TRACE"); + // FINISHED ADDING + + directive.push_back("USE"); + directive.push_back("USING"); + + // Sort for better presentation of results + sort( directive.begin(), directive.end() ); + + data_name_list.push_back("PIC"); + data_name_list.push_back("PICTURE"); + + exec_name_list.push_back("ACCEPT"); // 2015.12 + exec_name_list.push_back("ACCESS"); + exec_name_list.push_back("ADVANCING"); + exec_name_list.push_back("AFTER"); + exec_name_list.push_back("ALL"); + exec_name_list.push_back("ARE"); // 2015.12 + exec_name_list.push_back("ALPHABETIC"); // 2015.12 + exec_name_list.push_back("ALPHANUMERIC"); // 2015.12 + exec_name_list.push_back("ALPHANUMERIC-EDITED"); // 2015.12 + exec_name_list.push_back("ALTER"); + exec_name_list.push_back("ALTERNATIVE"); // 2015.12 + exec_name_list.push_back("ASCENDING"); // 2015.12 + exec_name_list.push_back("ASSIGN"); + exec_name_list.push_back("AT"); + exec_name_list.push_back("AUTHOR"); + exec_name_list.push_back("BACKGROUND-COLOR"); // 2015.12 + exec_name_list.push_back("BEGIN"); + exec_name_list.push_back("BEFORE"); + exec_name_list.push_back("BINARY"); // 2015.12 + exec_name_list.push_back("BLANK"); // 2015.12 + exec_name_list.push_back("BLINK"); // 2015.12 + exec_name_list.push_back("BLOCK"); + exec_name_list.push_back("BOTTOM"); // 2015.12 + exec_name_list.push_back("BY"); + exec_name_list.push_back("CALL"); + exec_name_list.push_back("CANCEL"); + exec_name_list.push_back("CD"); // 2015.12 + exec_name_list.push_back("CF"); // 2015.12 + exec_name_list.push_back("CH"); // 2015.12 + exec_name_list.push_back("CHARACTER"); // 2015.12 + exec_name_list.push_back("CHARACTERS"); // 2015.12 + exec_name_list.push_back("CLASS"); + exec_name_list.push_back("CLASS-ID"); + exec_name_list.push_back("CLOCK-UNITS"); // 2015.12 + exec_name_list.push_back("CLOSE"); + exec_name_list.push_back("CODE-SET"); + exec_name_list.push_back("COLLATING"); + exec_name_list.push_back("COLUMN"); // 2015.12 + exec_name_list.push_back("COMMON"); + exec_name_list.push_back("COMP"); + exec_name_list.push_back("COMPUTATIONAL"); // 2015.12 + + // ADD BY PEI-CHEN + exec_name_list.push_back("COMPUTE"); + // FINISH ADDING + + exec_name_list.push_back("CONFIGURATION"); + exec_name_list.push_back("CONSOLE"); // 2015.12 + exec_name_list.push_back("CONTAINS"); // 2015.12 + exec_name_list.push_back("CONTENT"); + exec_name_list.push_back("CONTINUE"); // 2015.12 + exec_name_list.push_back("CONTROL"); // 2015.12 + exec_name_list.push_back("CONTROLS"); // 2015.12 + exec_name_list.push_back("CORR"); // 2015.12 + exec_name_list.push_back("CORRESPONDING"); // 2015.12 + exec_name_list.push_back("COUNT"); // 2015.12 + exec_name_list.push_back("CURRENCY"); + exec_name_list.push_back("DATA"); + exec_name_list.push_back("DATE-COMPILED"); + exec_name_list.push_back("DATE-WRITTEN"); + exec_name_list.push_back("DAY"); + exec_name_list.push_back("DAY-OF-WEEK"); // 2015.12 + exec_name_list.push_back("DE"); // 2015.12 + exec_name_list.push_back("DEBUGGING"); + exec_name_list.push_back("DECIMAL-POINT"); + exec_name_list.push_back("DECLARATIVES"); + exec_name_list.push_back("DELETE"); + exec_name_list.push_back("DELIMITED"); // 2015.12 + exec_name_list.push_back("DELIMITER"); + exec_name_list.push_back("DEPENDING"); // 2015.12 + exec_name_list.push_back("DESCENDING"); // 2015.12 + exec_name_list.push_back("DISABLE"); // 2015.12 + exec_name_list.push_back("DISPLAY"); + exec_name_list.push_back("DIVISION"); + exec_name_list.push_back("DUPLICATES"); // 2015.12 + exec_name_list.push_back("ENABLE"); // 2015.12 + exec_name_list.push_back("END"); + exec_name_list.push_back("END-ADD"); // 2015.12 + exec_name_list.push_back("END-CALL"); // 2015.12 + exec_name_list.push_back("END-COMPUTE"); // 2015.12 + exec_name_list.push_back("END-DELETE"); // 2015.12 + exec_name_list.push_back("END-DIVIDE"); // 2015.12 + exec_name_list.push_back("END-EVALUATE"); // 2015.12 + exec_name_list.push_back("END-MULTIPLY"); // 2015.12 + exec_name_list.push_back("END-OF-FILE"); + exec_name_list.push_back("END-OF-PAGE"); // 2015.12 + exec_name_list.push_back("END-REWRITE"); // 2015.12 + exec_name_list.push_back("END-STRING"); // 2015.12 + exec_name_list.push_back("END-SUBTRACT"); // 2015.12 + exec_name_list.push_back("END-UNSTRING"); // 2015.12 + exec_name_list.push_back("ENVIRONMENT"); + exec_name_list.push_back("EOP"); // 2015.12 + exec_name_list.push_back("ERROR"); + exec_name_list.push_back("EVALUATE"); // 2015.12 + exec_name_list.push_back("EVERY"); // 2015.12 + exec_name_list.push_back("EXCEPTION"); // 2015.12 + exec_name_list.push_back("EXIT"); + exec_name_list.push_back("EXTEND"); // 2015.12 + exec_name_list.push_back("EXTERNAL"); // 2015.12 + exec_name_list.push_back("FACTORY"); + exec_name_list.push_back("FD"); // 2015.12 + exec_name_list.push_back("FILE"); + exec_name_list.push_back("FILE-CONTROL"); + exec_name_list.push_back("FILLER"); + exec_name_list.push_back("FINAL"); // 2015.12 + exec_name_list.push_back("FOOTING"); + exec_name_list.push_back("FOR"); // 2015.12 + exec_name_list.push_back("FOREGROUND-COLOR"); // 2015.12 + exec_name_list.push_back("FROM"); + exec_name_list.push_back("FUNCTION"); + exec_name_list.push_back("GENERATE"); + exec_name_list.push_back("GIVING"); // 2015.12 + exec_name_list.push_back("GLOBAL"); // 2015.12 + exec_name_list.push_back("GO"); + exec_name_list.push_back("GOTO"); + exec_name_list.push_back("GROUP"); // 2015.12 + exec_name_list.push_back("HEADING"); // 2015.12 + exec_name_list.push_back("HIGH-VALUE"); // 2015.12 + exec_name_list.push_back("HIGH-VALUES"); + exec_name_list.push_back("HIGHLIGHT"); // 2015.12 + exec_name_list.push_back("ID"); + exec_name_list.push_back("IDENTIFICATION"); + exec_name_list.push_back("IF"); + exec_name_list.push_back("IN"); // 2015.12 + exec_name_list.push_back("INDEXED"); + exec_name_list.push_back("INDICATE"); // 2015.12 + exec_name_list.push_back("INHERITS"); + exec_name_list.push_back("INITIAL"); + exec_name_list.push_back("INITIATE"); + exec_name_list.push_back("INPUT"); // 2015.12 + exec_name_list.push_back("INPUT-OUTPUT"); + exec_name_list.push_back("INSPECT"); // 2015.12 + exec_name_list.push_back("INSTALLATION"); + exec_name_list.push_back("INTERNAL"); // 2015.12 + exec_name_list.push_back("INVALID"); + exec_name_list.push_back("INVOKE"); + exec_name_list.push_back("IS"); + exec_name_list.push_back("I-O"); // 2015.12 + exec_name_list.push_back("I-O-CONTROL"); + exec_name_list.push_back("JUST"); // 2015.12 + exec_name_list.push_back("JUSTIFIED"); // 2015.12 + exec_name_list.push_back("KEY"); + exec_name_list.push_back("LABEL"); + exec_name_list.push_back("LABEL-OF"); + exec_name_list.push_back("LEADING"); // 2015.12 + exec_name_list.push_back("LEFT"); // 2015.12 + exec_name_list.push_back("LIMIT"); // 2015.12 + exec_name_list.push_back("LIMITS"); // 2015.12 + exec_name_list.push_back("LINAGE"); // 2015.12 + exec_name_list.push_back("LINE"); + exec_name_list.push_back("LINES"); + exec_name_list.push_back("LINKAGE"); + exec_name_list.push_back("LOCAL-STORAGE"); + exec_name_list.push_back("LOCK"); // 2015.12 + exec_name_list.push_back("LOW-VALUE"); // 2015.12 + exec_name_list.push_back("LOW-VALUES"); // 2015.12 + exec_name_list.push_back("MERGE"); + exec_name_list.push_back("MESSAGE"); // 2015.12 + exec_name_list.push_back("METHOD-ID"); + exec_name_list.push_back("MODE"); + exec_name_list.push_back("MOVE"); + exec_name_list.push_back("MULTIPLE"); // 2015.12 + exec_name_list.push_back("NATIVE"); // 2015.12 + exec_name_list.push_back("NEXT"); // 2015.12 + exec_name_list.push_back("NOT-END-OF-FILE"); + exec_name_list.push_back("NULL"); // 2015.12 + exec_name_list.push_back("NULLS"); // 2015.12 + exec_name_list.push_back("NUMBER"); // 2015.12 + exec_name_list.push_back("NUMERIC"); // 2015.12 + exec_name_list.push_back("NUMERIC-EDITED"); // 2015.12 + exec_name_list.push_back("OBJECT"); + exec_name_list.push_back("OBJECT-COMPUTER"); + exec_name_list.push_back("OCCURS"); + exec_name_list.push_back("OF"); // 2015.12 + exec_name_list.push_back("OFF"); // 2015.12 + exec_name_list.push_back("OMITTED"); // 2015.12 + exec_name_list.push_back("ON"); // 2015.12 + exec_name_list.push_back("OPEN"); + exec_name_list.push_back("ORDER"); // 2015.12 + exec_name_list.push_back("OUTPUT"); // 2015.12 + exec_name_list.push_back("ORGANIZATION"); + exec_name_list.push_back("OVERFLOW"); // 2015.12 + exec_name_list.push_back("PACKED-DECIMAL"); // 2015.12 + exec_name_list.push_back("PADDING"); + exec_name_list.push_back("PAGE"); // 2015.12 + exec_name_list.push_back("PASSWORD"); + exec_name_list.push_back("PERFORM"); + exec_name_list.push_back("PF"); // 2015.12 + exec_name_list.push_back("PH"); // 2015.12 + exec_name_list.push_back("POINTER"); // 2015.12 <<<< Pointer like C++ ? ? ? + exec_name_list.push_back("POSITION"); // 2015.12 + exec_name_list.push_back("PRINTING"); // 2015.12 + exec_name_list.push_back("PROCEDURE"); + exec_name_list.push_back("PROCEDURES"); // 2015.12 + exec_name_list.push_back("PROGRAM"); + exec_name_list.push_back("PROGRAM-ID"); + exec_name_list.push_back("QUOTE"); // 2015.12 + exec_name_list.push_back("QUOTES"); // 2015.12 + exec_name_list.push_back("READ"); + exec_name_list.push_back("RECORD"); + exec_name_list.push_back("RECORDS"); // 2015.12 + exec_name_list.push_back("RECORDING"); + exec_name_list.push_back("RECURSIVE"); + exec_name_list.push_back("REDEFINES"); // 2015.12 + exec_name_list.push_back("REFERENCE"); + exec_name_list.push_back("REFERENCES"); // 2015.12 + exec_name_list.push_back("RELATIVE"); + exec_name_list.push_back("RELEASE"); + exec_name_list.push_back("REMOVAL"); // 2015.12 + exec_name_list.push_back("RENAMES"); // 2015.12 + exec_name_list.push_back("REPORT"); // 2015.12 + exec_name_list.push_back("REPORTING"); + exec_name_list.push_back("RERUN"); // 2015.12 + exec_name_list.push_back("RESERVE"); + exec_name_list.push_back("RESET"); // 2015.12 + exec_name_list.push_back("RETURN"); + exec_name_list.push_back("REVERSE-VIDEO"); // 2015.12 + exec_name_list.push_back("REWIND"); // 2015.12 + exec_name_list.push_back("REWRITE"); + exec_name_list.push_back("RF"); // 2015.12 + exec_name_list.push_back("RH"); // 2015.12 + exec_name_list.push_back("RIGHT"); // 2015.12 + exec_name_list.push_back("ROUNDED"); // 2015.12 + exec_name_list.push_back("SCREEN"); // 2015.12 + exec_name_list.push_back("SD"); // 2015.12 + + // ADD BY PEI-CHEN + exec_name_list.push_back("SEARCH"); + // FINISH ADDING + + exec_name_list.push_back("SECTION"); + exec_name_list.push_back("SECURITY"); + exec_name_list.push_back("SEGMENT-LIMIT"); + exec_name_list.push_back("SELECT"); + exec_name_list.push_back("SEPARATE"); // 2015.12 + exec_name_list.push_back("SEQUENCE"); + exec_name_list.push_back("SEQUENTIAL"); + exec_name_list.push_back("SIGN"); // 2015.12 + exec_name_list.push_back("SIZE"); // 2015.12 + exec_name_list.push_back("SORT"); + exec_name_list.push_back("SORT-MERGE"); // 2015.12 + exec_name_list.push_back("SOURCE-COMPUTER"); + exec_name_list.push_back("SPACE"); // 2015.12 + exec_name_list.push_back("SPACES"); + exec_name_list.push_back("SPECIAL-NAMES"); // 2015.12 + exec_name_list.push_back("STANDARD"); // 2015.12 + exec_name_list.push_back("STANDARD-1"); // 2015.12 + exec_name_list.push_back("STANDARD-2"); // 2015.12 + exec_name_list.push_back("START"); + exec_name_list.push_back("STATUS"); + exec_name_list.push_back("STRING"); // 2015.12 + exec_name_list.push_back("STOP"); // 2015.12 + exec_name_list.push_back("SUPPRESS"); // 2015.12 + exec_name_list.push_back("SUM"); // 2015.12 + exec_name_list.push_back("SYMBOLIC"); // 2015.12 + exec_name_list.push_back("SYNC"); // 2015.12 + exec_name_list.push_back("SYNCHRONIZED"); // 2015.12 + exec_name_list.push_back("TABLE"); // 2015.12 + exec_name_list.push_back("TABLES"); // 2015.12 + exec_name_list.push_back("TAPE"); // 2015.12 + exec_name_list.push_back("TERMINATE"); + exec_name_list.push_back("THAN"); // 2015.12 + exec_name_list.push_back("THEN"); // 2015.12 + exec_name_list.push_back("THROUGH"); // 2015.12 + exec_name_list.push_back("THRU"); + exec_name_list.push_back("TIME"); + exec_name_list.push_back("TIMES"); + exec_name_list.push_back("TO"); + exec_name_list.push_back("TOP"); // 2015.12 + exec_name_list.push_back("TRAILING"); // 2015.12 + exec_name_list.push_back("TRUE"); // 2015.12 + exec_name_list.push_back("UNDERLINE"); // 2015.12 + exec_name_list.push_back("UNSTRING"); // 2015.12 + exec_name_list.push_back("UNTIL"); + exec_name_list.push_back("UPON"); // 2015.12 + exec_name_list.push_back("USE"); + exec_name_list.push_back("USING"); // 2015.12 + exec_name_list.push_back("VALUE"); + exec_name_list.push_back("VALUES"); // 2015.12 + exec_name_list.push_back("VARYING"); + exec_name_list.push_back("WHEN"); + exec_name_list.push_back("WITH"); + exec_name_list.push_back("WORKING-STORAGE"); + exec_name_list.push_back("WRITE"); + + // ADD BY PEI-CHEN + exec_name_list.push_back("XML-CODE"); + exec_name_list.push_back("XML-EVENT"); + exec_name_list.push_back("XML PARSE"); + exec_name_list.push_back("XML-TEXT"); + // FINISH ADDING + + exec_name_list.push_back("ZERO"); + exec_name_list.push_back("ZEROS"); + exec_name_list.push_back("ZEROES"); + + sort( exec_name_list.begin(), exec_name_list.end() ); + + math_func_list.push_back("-"); + math_func_list.push_back("*"); + math_func_list.push_back("**"); + math_func_list.push_back("/"); + math_func_list.push_back("+"); + math_func_list.push_back("ADD"); + math_func_list.push_back("COMPUTE"); + math_func_list.push_back("DIVIDE"); + math_func_list.push_back("MULTIPLY"); + math_func_list.push_back("SUBTRACT"); + math_func_list.push_back("REMAINDER"); + + cmplx_logic_list.push_back("<"); + cmplx_logic_list.push_back("<="); + cmplx_logic_list.push_back("="); + cmplx_logic_list.push_back(">"); + cmplx_logic_list.push_back(">="); + cmplx_logic_list.push_back("EQUAL"); + cmplx_logic_list.push_back("GREATER"); + cmplx_logic_list.push_back("LESS"); + cmplx_logic_list.push_back("NOT"); + cmplx_logic_list.push_back("NOT="); + cmplx_logic_list.push_back("OR"); + + // added by Nipun + cmplx_logic_list.push_back("XML GENERATE"); + cmplx_logic_list.push_back("XML PARSE"); + //---finished adding + + cmplx_cond_list.push_back("ELSE IF"); + cmplx_cond_list.push_back("EVALUATE"); + cmplx_cond_list.push_back("IF"); + cmplx_cond_list.push_back("PERFORM"); + cmplx_cond_list.push_back("SEARCH"); + cmplx_cond_list.push_back("STOP"); + cmplx_cond_list.push_back("THEN"); + cmplx_cond_list.push_back("WHEN"); + + cmplx_assign_list.push_back("ACCEPT"); + cmplx_assign_list.push_back("COMPUTE"); + cmplx_assign_list.push_back("GENERATE"); + cmplx_assign_list.push_back("INITIALIZE"); + cmplx_assign_list.push_back("INSPECT"); + cmplx_assign_list.push_back("MOVE"); + cmplx_assign_list.push_back("PARSE"); + cmplx_assign_list.push_back("SET"); + cmplx_assign_list.push_back("STRING"); + cmplx_assign_list.push_back("UNSTRING"); + cmplx_assign_list.push_back("XML"); + + + cmplx_cyclomatic_list.push_back("ELSE IF"); + cmplx_cyclomatic_list.push_back("END-EVALUATE"); + cmplx_cyclomatic_list.push_back("END-IF"); + cmplx_cyclomatic_list.push_back("END-PERFORM"); + cmplx_cyclomatic_list.push_back("END-SEARCH"); + cmplx_cyclomatic_list.push_back("EVALUATE"); + cmplx_cyclomatic_list.push_back("IF"); + cmplx_cyclomatic_list.push_back("PERFORM"); + cmplx_cyclomatic_list.push_back("SEARCH"); + cmplx_cyclomatic_list.push_back("STOP RUN"); + cmplx_cyclomatic_list.push_back("STOP"); + + +// These used to be in ProcessComplexityMeasures in the old 2011 (never released) COBOL parser +// + // subtract "End If" since "If" captured within "End If" and we also have in-line "If" + // subtract "Case Else" because it is captured by "Case" and should not be counted + ignore_cmplx_cyclomatic_list.push_back("END-EVALUATE"); + ignore_cmplx_cyclomatic_list.push_back("END-PERFORM"); + ignore_cmplx_cyclomatic_list.push_back("END-SEARCH"); + ignore_cmplx_cyclomatic_list.push_back("END-IF"); + ignore_cmplx_cyclomatic_list.push_back("STOP RUN"); + + + // EVALUATE is like switch + + + +} +/*! +* Counts directive lines of code. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CCobolCounter::CountDirectiveSLOC(filemap* fmap, results* result, filemap* /* fmapBak */) +{ + bool contd = false, trunc_flag = false; + size_t idx, strSize; + if (classtype == DATAFILE) return 0; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string strDirLine = ""; + + for (filemap::iterator iter = fmap->begin(); iter!=fmap->end(); iter++) + { + if ( CUtil::CheckBlank( iter->line ) ) + continue; + + // === ADDED BY QINGYU ZHOU === + TrimSourceLine( iter->line ); + // === FINISHED ADDING === + + if ( print_cmplx ) + { + cnt = 0; + CUtil::CountTally(" " + iter->line, directive, cnt, 1, exclude, "", "", &result->directive_count); + } + + if (!contd) + { + // if not a continuation of a previous directive + for(vector::iterator viter = directive.begin(); viter != directive.end(); viter++) + { + if ((idx = iter->line.find((*viter), 0)) != string::npos && idx == 0) + { + contd = true; + break; + } + } + if (contd) + { + result->directive_lines[PHY]++; + strSize = CUtil::TruncateLine(iter->line.length(), 0, result->trunc_lines, trunc_flag); + if (strSize > 0) + strDirLine = iter->line.substr(0, strSize); + } + } + else + { + // continuation of a previous directive + strSize = CUtil::TruncateLine(iter->line.length(), strDirLine.length(), result->trunc_lines, trunc_flag); + if (strSize > 0) + strDirLine += "\n" + iter->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + + if (contd) + { + // if a directive or continuation of a directive (no continuation symbol found) + if (iter->line[iter->line.length()-1] != '_') + { + contd = false; + result->directive_lines[LOG]++; + result->addSLOC(strDirLine, trunc_flag); + } + iter->line = ""; + } + } + return 1; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CCobolCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + int retVal = 1; + string strLSLOC = ""; + string strLSLOCBak = ""; + + filemap::iterator fit, fitbak; + string line, lineBak; + size_t prev_pos, strSize, tmpLoc; + unsigned int cnt = 0; + StringVector loopEnd; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string special = "[]()+/*<>=,@&~!^?:%{}"; + + string tmpstr; + bool isDataLine = false; +// bool line_continued = false; + bool line_skipped; + bool trunc_flag = false; + bool new_loop = false; + + SAVE_TO_2( "Start CCobolCounter::CountComplexity" ); + currentPhyLine = 0; + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + currentPhyLine++; + line = fit->line; + if (CUtil::CheckBlank(line)) + continue; + + lineBak = fitbak->line; + prev_pos = 0; + + // check for inline If..Then + tmpstr = CUtil::TrimString(line); + tmpLoc = CUtil::FindKeyword(tmpstr, "THEN"); + if (tmpLoc != string::npos) + { + if (tmpLoc < tmpstr.length() - 4) + tmpLoc += 3; + else + tmpLoc = string::npos; + } + + // record nested loops + if ( print_cmplx ) + { + new_loop = false; + if (CUtil::FindKeyword(tmpstr, "PERFORM") == 0) + { + loopEnd.push_back("END-PERFORM"); + new_loop = true; + } + else if (loopEnd.size() > 0) + { + if (CUtil::FindKeyword(tmpstr, loopEnd.back()) == 0) + loopEnd.pop_back(); + } + if (new_loop) + { + if ( result->cmplx_nestloop_count.size() < loopEnd.size() ) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopEnd.size()-1]++; + } + } + + for (size_t i = 0; i < line.size(); i++) + { + if ((line[i] == ':') || (i == line.size() - 1) || tmpLoc != string::npos) + { + if (tmpLoc != string::npos) + { + i = tmpLoc; + tmpLoc = string::npos; + } + else + tmpstr = CUtil::TrimString(line.substr(prev_pos, i - prev_pos + 1)); + + // exclude SLOC defined in the exclude_keywords + line_skipped = false; + for (vector::iterator stri = exclude_keywords.begin(); stri != exclude_keywords.end(); stri++) + { + if (tmpstr.compare(*stri) == 0) + { + line_skipped = true; + break; + } + } + if (line_skipped) + continue; + + // exclude SLOC starting with Next, End + for (StringVector::iterator stri = exclude_start_keywords.begin(); stri != exclude_start_keywords.end(); stri++) + { + if (CUtil::FindKeyword(tmpstr, *stri) == 0) + { + line_skipped = true; + break; + } + } + if (line_skipped) + continue; + + strSize = CUtil::TruncateLine(i + 1 - prev_pos, strLSLOC.length(), result->trunc_lines, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(prev_pos, strSize); + strLSLOCBak += lineBak.substr(prev_pos, strSize); + } + + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", &result->data_name_count); + if (cnt > 0) + { + isDataLine = true; + result->data_lines[LOG]++; + } + else + { + if ( print_cmplx ) + { + cnt = 0; + CUtil::CountTally(strLSLOC, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count); + } + isDataLine = false; + result->exec_lines[LOG]++; + } + + result->addSLOC( strLSLOCBak, trunc_flag ); // add one logical SLOC to the list + strLSLOCBak = ""; + strLSLOC = ""; + + prev_pos = i + 1; + + if (tmpLoc != string::npos) + { + tmpLoc = string::npos; + prev_pos++; + } + } + + if (special.find_first_of(line[i]) != string::npos) + line[i] = ' '; + } + + if (isDataLine) + result->data_lines[PHY]++; + else + result->exec_lines[PHY]++; + } + +/* Not needed? Done as ignore_cmplx_cyclomatic_list + if ( print_cmplx ) + retVal = ProcessComplexityMeasures( fmap, result ); +*/ + + return retVal; +} + +/*! +* Processes file language complexity measures. +* +* \param fmap list of processed file lines +* \param result counter results +* +* \return method status +*/ +/* COMMENTED OUT. Now the keywords are in ignore_cmplx_cyclomatic_list + +int CCobolCounter::ProcessComplexityMeasures( filemap* fmap, results* result ) +{ + // process Cyclomatic Complexity (CC) from above COBOL has Cyclomatic Complexity keywords with no skip + + SAVE_TO_3( "Start CCobolCounter::ProcessComplexityMeasures" ); + + result->cyclomatic_complexity = 1; + StringVector::iterator icyc = cmplx_cyclomatic_list.begin(); + for (vector::iterator icycc = result->cmplx_cyclomatic_count.begin(); icycc != result->cmplx_cyclomatic_count.end(); icycc++, icyc++) + { + if ((*icyc) == "END-EVALUATE" || (*icyc) == "END-PERFORM" || (*icyc) == "END-SEARCH" || (*icyc) == "END-IF" || (*icyc) == "STOP RUN") + { + // subtract "End If" since "If" captured within "End If" and we also have in-line "If" + // subtract "Case Else" because it is captured by "Case" and should not be counted + result->cyclomatic_complexity -= (*icycc); + } + else + result->cyclomatic_complexity += (*icycc); + } + if ( result->cyclomatic_complexity < 0 ) + result->cyclomatic_complexity = 0; + + return 1; +} */ + + +int CCobolCounter::TrimSourceLine(string& line) +{ + for (unsigned int i = 0; i < line.length() - 1; i++) + { + if (line.at(i) == ' ') + { + int countOfSpaces; + for ( countOfSpaces = 1; i + countOfSpaces < line.length(); countOfSpaces++ ) + { + if (line.at(i + countOfSpaces) != ' ') + break; + } + line.erase( i, (unsigned)countOfSpaces - 1 ); + } + } + TrimSequenceNumber( line ); + if ( TrimIndicator( line ) == 1 ) + TrimAreaA( line ); + return 1; +} + +int CCobolCounter::TrimSequenceNumber( string& line ) +{ + if ( line.length() < 6) + return 0; + + for (int i = 0; i < 6; i++) + { + if ( isdigit( line.at( (unsigned)i ) ) == false ) + return 0; + } + + line.erase( 0, 6 ); + return 1; +} + +int CCobolCounter::TrimIndicator( string& line ) +{ + if ( line.length() > 0 && line.at( 0 ) == ' ' ) + { + line.erase( 0, 1 ); + return 1; + } + + return 0; + +} + +int CCobolCounter::TrimAreaA( string& line ) +{ + if ( line.length() < 3 ) + return 0; + + if ( line.at(2) == ' ' && isalnum( line.at( 0 ) ) && isalnum( line.at( 1 ) ) ) + line.erase( 0, 3 ); + + return 1; +} + +/*! +* Counts file language complexity based on specified language keywords/characters. +* +* \param fmap list of processed file lines +* \param result counter results +* +* \return method status +*/ +int CCobolCounter::CountComplexity( filemap* fmap, results* result ) +{ + filemap::iterator fit; + unsigned int cnt, cyclomatic_cnt = 0, cyclomatic_logic_cnt = 0, ignore_cyclomatic_cnt = 0; + //int tmpc; + string line; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + bool line_skipped = false; +// bool process_cyclomatic_complexity = true; // from above COBOL has Complexity keywords and no skip + + ENTER_1( "Start" ); // Due to Polymorphism change level above +// try +// { + + SAVE_TO_2( "Start CCobolCounter::CountComplexity" ); + currentPhyLine = 0; + for ( fit = fmap->begin(); fit != fmap->end(); fit++ ) + { + currentPhyLine++; + line = fit->line; + SAVE_TO_2( "CUtil::CheckBlank" ); + if ( CUtil::CheckBlank( line ) ) + continue; + + // exclude keywords in exclude_keywords + line_skipped = false; + for (StringVector::iterator stri = exclude_keywords.begin(); stri != exclude_keywords.end(); stri++) + { + if (CUtil::FindKeyword(line, *stri) == 0) + { + line_skipped = true; + break; + } + } + + if ( line_skipped ) + continue; + + line = " " + line; + + // mathematical functions + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Math" ); + CUtil::CountTally(line, math_func_list, cnt, 1, exclude, "", "", &result->math_func_count, casesensitive); + result->cmplx_math_lines += cnt; + + // trigonometric functions + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Trigonometric" ); + CUtil::CountTally(line, trig_func_list, cnt, 1, exclude, "", "", &result->trig_func_count, casesensitive); + result->cmplx_trig_lines += cnt; + + // logarithmic functions + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Logarithmic" ); + CUtil::CountTally(line, log_func_list, cnt, 1, exclude, "", "", &result->log_func_count, casesensitive); + result->cmplx_logarithm_lines += cnt; + + // calculations + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Calculations" ); + CUtil::CountTally(line, cmplx_calc_list, cnt, 1, exclude, "", "", &result->cmplx_calc_count, casesensitive); + result->cmplx_calc_lines += cnt; + + // conditionals + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Conditionals" ); + CUtil::CountTally(line, cmplx_cond_list, cnt, 1, exclude, "", "", &result->cmplx_cond_count, casesensitive); + result->cmplx_cond_lines += cnt; + + //tmpc = (int)CUtil::FindKeyword( line, "END-IF" ); + //result->cmplx_cond_lines = 0; + + + // logical operators + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Logical" ); + CUtil::CountTally(line, cmplx_logic_list, cnt, 1, exclude, "", "", &result->cmplx_logic_count, casesensitive); + result->cmplx_logic_lines += cnt; + + // preprocessor directives + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Preprocessor" ); + CUtil::CountTally(line, cmplx_preproc_list, cnt, 1, exclude, "", "", &result->cmplx_preproc_count, casesensitive); + result->cmplx_preproc_lines += cnt; + + // assignments + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Assignments" ); + CUtil::CountTally(line, cmplx_assign_list, cnt, 1, exclude, "", "", &result->cmplx_assign_count, casesensitive); + result->cmplx_assign_lines += cnt; + + /* Pointers not in COBOL + // pointers + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Pointers" ); + // Pointers are embedded syntax so there is NO exclude string or include strings + CUtil::CountTally(line, cmplx_pointer_list, cnt, 1, "", "", "", &result->cmplx_pointer_count, casesensitive); + result->cmplx_pointer_lines += cnt; + */ + + // Cyclomatic Complexity keywords + //cnt = 0; + //CUtil::CountTally(line, cmplx_cyclomatic_list, cnt, 1, exclude, "", "", &result->cmplx_cyclomatic_count, casesensitive); + // search for Cyclomatic Complexity keywords + SAVE_TO_2( "CUtil::CountTally Cyclomatic Complexity" ); + CUtil::CountTally(line, cmplx_cyclomatic_list, cyclomatic_cnt, 1, exclude, "", "", 0, casesensitive); + + // COBOL has Cyclomatic Complexity keywords to ignore + if ( ignore_cmplx_cyclomatic_list.size() > 0 ) + { + SAVE_TO_2( "CUtil::CountTally exclude keywords" ); + CUtil::CountTally( line, ignore_cmplx_cyclomatic_list, ignore_cyclomatic_cnt, 1, exclude, "", "", 0, casesensitive ); + } + + // COBOL has cyclomatic complexity logical keywords + SAVE_TO_2( "CUtil::CountTally complexity Logical" ); + CUtil::CountTally(line, cmplx_cyclomatic_logic_list, cyclomatic_logic_cnt, 1, exclude, "", "", 0, casesensitive); + + + + } + +/* + } + catch(const std::exception& e) + { + string dumpInfo; + string what = e.what(); + StackDump( dumpInfo, EXCEPTION_HANDLER_PARSER, this->parse_threadIdx, EXCEPTION_STD_EXCEPTION, what, this ); + throw( e ); + } +*/ + return 1; +} diff --git a/src/CCobolCounter.h b/src/CCobolCounter.h new file mode 100644 index 0000000..3f1b020 --- /dev/null +++ b/src/CCobolCounter.h @@ -0,0 +1,50 @@ +//! Code counter class definition for the Cobol language. +/*! +* \file CCobolCounter.h +* +* This file contains the code counter class definition for the Cobol language. +*/ + +#ifndef CCobolCounter_h +#define CCobolCounter_h + +#include "CCodeCounter.h" + +//! Cobol code counter class. +/*! +* \class CCobolCounter +* +* Defines the Cobol code counter class. +*/ +class CCobolCounter : public CCodeCounter +{ +public: + CCobolCounter(); + +protected: + + virtual int CountDirectiveSLOC( filemap* fmap, results* result, filemap* fmapBak = NULL ); + virtual int LanguageSpecificProcess( filemap* fmap, results* result, filemap* fmapBak = NULL ); +// virtual int ProcessComplexityMeasures( filemap* fmap, results* result ); + virtual int CountComplexity( filemap* fmap, results* result ); + virtual int TrimSourceLine( string& line ); + virtual int TrimSequenceNumber( string& line ); + virtual int TrimAreaA( string& line ); + virtual int TrimIndicator( string& line ); + + StringVector exclude_start_keywords; //!< SLOC lines excluded from counts starting with keywords + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CCobolCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CCobolCounter(const CCobolCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CCobolCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CCobolCounter operator=(const CCobolCounter); // Declare without implementation +}; + +#endif \ No newline at end of file diff --git a/src/CCodeCounter.cpp b/src/CCodeCounter.cpp new file mode 100644 index 0000000..f3cb895 --- /dev/null +++ b/src/CCodeCounter.cpp @@ -0,0 +1,2840 @@ +//! Common code counter methods for sub-classing individual languages. +/*! +* \file CCodeCounter.h +* +* This file contains the common code counter methods for sub-classing individual languages. +* +* Modified: 2015.12 by Randy Maxwell +* Support for 1 pass language parsers (Scala) +*/ + +#include "UCCBeforeLibraryIncludes.h" // Modification: 2015.12 +//#include +#include +//#include "stdio.h" +#include +//#include "sstream" +#include +#include +#include +#include "UCCAfterLibraryIncludes.h" // Modification: 2015.12 + +#include "UCCGlobals.h" // Modification: 2015.12 +#include "UCCFilesOut.h" // Modification: 2015.12 +#include "CCodeCounter.h" + + +#ifdef _DEBUG +// Enable this to get more detailed information (likely using Debugger to look at structures) +// MAY improve details provided in Stack Dump +// This will slow processing speed somewhat +// +// #define _DEBUG_1_PASS_PARSE +#endif +/*! +* 1.File Description: +* Constructs a CCodeCounter object. +* +* 2.Parameter: +* None +* +* 3.Creation Time and Owner: +* version 2009.01 +*/ +CCodeCounter::CCodeCounter() +{ +#ifdef ENABLE_1_PASS_PARSER + // 1 pass through the source file support + use_1pass_parser = false; // Modification: 2015.12 + parse_alloc_count = 0; // Modification: 2015.12 + pe = (parseElement *) NULL; // Modification: 2015.12 + pe_dup_keywords = false; // Modification: 2015.12 + pe_max_idx = 0; // Modification: 2015.12 + pe_mid_idx = 0; // Modification: 2015.12 + pe_mid_keyword = ""; // Modification: 2015.12 +#endif + + print_cmplx = false; //modified in version 2014.08 + cc4enable = false; // Modification: 2015.12 + lsloc_truncate = DEFAULT_TRUNCATE; + QuoteStart = ""; //modified in version 2009.01 + QuoteEnd = ""; + quote_start_is_end = true; // Modification: 2015.12 + QuoteMultiStart = ""; // Modification: 2015.12 + QuoteMultiEnd = ""; // Modification: 2015.12 + quote_multi_start_is_end = true; // Modification: 2015.12 + QuoteEscapeFront = 0; + QuoteEscapeRear = 0; + ContinueLine = ""; + classtype = UNKNOWN; //modified in version 2011.10B + language_name = DEF_LANG_NAME; + language_version = ""; // Modification: 2015.12 + language_version_authority = ""; // Modification: 2015.12 + casesensitive = true; //modified in version 2009.01 + total_filesA = 0; //modified in version 2011.05 + total_filesB = 0; + total_dupFilesA = 0; + total_dupFilesB = 0; + + // Support for simple Stack Dump Modified: 2015.12 + // Keep track of the current line of the source file being parsed. + currentPhyLine = (unsigned int)(-1); // Set to invalid value to start + currentLSrcLine = 0; + ENTER_1( "" ); + ENTER_2( "" ); + ENTER_3( "" ); + ENTER_4( "" ); + //Modification: 11.2016 Ext-4 + CUtil::allocate(); +} + +/*! +* 1.File Description: +* Destroys a CCodeCounter object. +* +* 2.Parameters: +* None +* +* 3.Creation Time and Owner: +* version 2009.01 +* Revised 2015.12 Free allocated resources +*/ +CCodeCounter::~CCodeCounter() +{ +#ifdef ENABLE_1_PASS_PARSER + if ( 0 < parse_alloc_count ) + { + // Free memory used by parse array + if ( NULL != pe ) + { + for ( unsigned int i = 0; i < parse_alloc_count; i++ ) + pe[i].keyword.resize( 0 ); + + delete [] pe; + pe = (parseElement *) NULL; + } + parse_alloc_count = 0; + } +#endif +} + +/*! +* 1.File Description: +* This method removes the existing content of the vectors and assigns them all zeros and initializes the count vectors. +* +* 2.Parameters: +* None +* +* 3.Creation Time and Owner: +* version 2011.05 +*/ +void CCodeCounter::InitializeCounts() +{ + ENTER_1( "Start" ); + unsigned int i = 0; //modified in version 2013.04 + counted_files = 0; + counted_dupFiles = 0; + + directive_count.assign(directive.size(), make_pair(i, i)); + data_name_count.assign(data_name_list.size(), make_pair(i, i)); + exec_name_count.assign(exec_name_list.size(), make_pair(i, i)); + math_func_count.assign(math_func_list.size(), make_pair(i, i)); + trig_func_count.assign(trig_func_list.size(), make_pair(i, i)); + log_func_count.assign(log_func_list.size(), make_pair(i, i)); + + cmplx_calc_count.assign(cmplx_calc_list.size(), make_pair(i, i)); + cmplx_cond_count.assign(cmplx_cond_list.size(), make_pair(i, i)); + cmplx_logic_count.assign(cmplx_logic_list.size(), make_pair(i, i)); + cmplx_preproc_count.assign(cmplx_preproc_list.size(), make_pair(i, i)); + cmplx_assign_count.assign(cmplx_assign_list.size(), make_pair(i, i)); + cmplx_pointer_count.assign(cmplx_pointer_list.size(), make_pair(i, i)); +} + +/*! +* 1.File Description: +* This method removes the existing content of the vectors and assigns them all zeros and initializes the count vectors for a result +* +* 2.Parameters: +* result: counter results +* +* 3.Creation Time and Owner: +* version 2011.05 +*/ +void CCodeCounter::InitializeResultsCounts(results* result) +{ + ENTER_1( "Start" ); + result->directive_count.assign(directive.size(), 0); + result->data_name_count.assign(data_name_list.size(), 0); + result->exec_name_count.assign(exec_name_list.size(), 0); + result->math_func_count.assign(math_func_list.size(), 0); + result->trig_func_count.assign(trig_func_list.size(), 0); + result->log_func_count.assign(log_func_list.size(), 0); + + result->cmplx_calc_count.assign(cmplx_calc_list.size(), 0); + result->cmplx_cond_count.assign(cmplx_cond_list.size(), 0); + result->cmplx_logic_count.assign(cmplx_logic_list.size(), 0); + result->cmplx_preproc_count.assign(cmplx_preproc_list.size(), 0); + result->cmplx_assign_count.assign(cmplx_assign_list.size(), 0); + result->cmplx_pointer_count.assign(cmplx_pointer_list.size(), 0); +} + +/*! +* 1.File Description: +* Processes and counts the source file +* 2015.12 Added details if Exception happens, currentPhyLine +* Always returns 0 +* +* 2.Parameter: +* fmap: list of file lines and result counter results +* +* 3.Creation Time and Owner: +* version 2009.01 +* Revised 2015.12 Added support for Stack Dump if Exception happens: SAVE_TO_n +*/ +int CCodeCounter::CountSLOC(filemap* fmap, results* result) +{ + // backup file content before modifying it (comments and directive lines are cleared) + // fmapBak is same as fmap except that it stores unmodified quoted strings + // fmap has quoted strings replaced with '$' + filemap fmapMod = *fmap; //added in version 2011.05 + filemap fmapModBak = *fmap; + + AddTraceLine(__LINE__, UCC_FUNC_NAME, ""); + + // Set up lightweight support for Stack Dump + SAVE_TO_2( "" ); + SAVE_TO_3( "" ); + SAVE_TO_4( "" ); + currentPhyLine = 1; + currentLSrcLine = 0; + + SAVE_TO_1( "InitializeResultsCounts" ); + InitializeResultsCounts(result); + + SAVE_TO_1( "PreCountProcess" ); + PreCountProcess(&fmapMod); + + int parse_result = 1; + +#ifdef ENABLE_1_PASS_PARSER + if ( true == use_1pass_parser ) + { + SAVE_TO_1( "ParseFile" ); + parse_result = ParseFile( &fmapMod, result, &fmapModBak ); + } + + if ( ( 0 != parse_result ) + || ( false == use_1pass_parser ) ) +#else + if ( 0 != parse_result ) +#endif + { + SAVE_TO_1( "CountBlankSLOC" ); + CountBlankSLOC(&fmapMod, result); + + SAVE_TO_1( "CountCommentsSLOC" ); + CountCommentsSLOC(&fmapMod, result, &fmapModBak); + + if (print_cmplx) + { + SAVE_TO_1( "CountComplexity" ); + CountComplexity(&fmapMod, result); + } + + SAVE_TO_1( "CountDirectiveSLOC" ); + CountDirectiveSLOC(&fmapMod, result, &fmapModBak); + + SAVE_TO_1( "FindHalsteadsVolume" ); + FindHalsteadsVolume(fmapModBak, result); + + SAVE_TO_1( "LanguageSpecificProcess" ); + LanguageSpecificProcess(&fmapMod, result, &fmapModBak); + } + + SAVE_TO_1( "Exit CountSLOC" ); + return 0; +} + +/*! +* 1.File Description: +* Checks whether the file extension is supported by the language counter.Return whether file extension is supported +* +* 2.Parameters: +* file_name: file name +* +* 3.Creation Time and Owner: +* version 2009.01 +*/ +bool CCodeCounter::IsSupportedFileExtension(const string &file_name) +{ + // if Makefile, check whether name equals MAKEFILE since no extension exists + if (classtype == MAKEFILE && file_name.size() >= 8) + { + if (CUtil::ToLower(file_name.substr(file_name.size() - 8)) == "makefile") + return true; + } + for (StringVector::iterator vit = file_extension.begin(); vit != file_extension.end(); vit++) + { + // to check if one of the extensions does not contain a . + if((*vit).find_last_of(".") == string::npos) { + // to check for a no extension + if((*vit).empty()) { + size_t idx = file_name.find_last_of("."); + if (idx == string::npos) + return true; + } + } else { + if (CUtil::EndsWith(file_name, (*vit), false)) + { + // if X-Midas/NeXtMidas, parse file to check for startmacro or endmacro (needed since Midas can use .txt or .mm) + if (classtype == XMIDAS || classtype == NEXTMIDAS) + { + string oneline; + ifstream fr(file_name.c_str(), ios::in); + if (!fr.is_open()) + return false; + + // search for "startmacro" (optional) or "endmacro" (required) + while (fr.good() || fr.eof()) + { + getline(fr, oneline); + if ((!fr.good() && !fr.eof()) || (fr.eof() && oneline.empty())) + break; + oneline = CUtil::ToLower(CUtil::TrimString(oneline)); + if (oneline.compare(0, 10, "startmacro") == 0 || oneline.compare(0, 8, "endmacro") == 0) + { + fr.clear(); + fr.close(); + return true; + } + if (!fr.good()) + break; + } + fr.clear(); + fr.close(); + } + else + return true; + break; + } + } + } + return false; +} + +/*! +* 1.Function Description: + Retrieves the language output file stream and Opens a new stream if it has not been opened already.And return output file stream +* +* 2.Parameters: +* outputFileNamePrePend: name to prepend to the output file; cmd current command line string; +* csvOutput: CSV file stream? (otherwise ASCII text file); legacyOutput legacy format file stream? (otherwise standard text file) +* +* 3.Creation Time and Owner: +* version 2009.01 +*/ +ofstream* CCodeCounter::GetOutputStream(const string &outputFileNamePrePend, const string &cmd, bool csvOutput, bool legacyOutput) +{ + if (csvOutput) //modified in version 2011.05 + { + if (!output_file_csv.is_open()) + { + string fname = outputFileNamePrePend + language_name + OUTPUT_FILE_NAME_CSV; + output_file_csv.open(fname.c_str(), ofstream::out); + + if (!output_file_csv.is_open()) return NULL; + + PrintFileHeader(output_file_csv, "SLOC COUNT RESULTS", cmd); + + PrintFileHeaderLine(output_file_csv, "RESULTS FOR " + language_name + " FILES"); + output_file_csv << endl; + output_file_csv << "Total,Blank,Comments,,Compiler,Data,Exec.,Logical,Physical,File,Module" << endl; + output_file_csv << "Lines,Lines,Whole,Embedded,Direct.,Decl.,Instr.,SLOC,SLOC,Type,Name" << endl; + } + return &output_file_csv; + } + else + { + if (!output_file.is_open()) + { + string fname = outputFileNamePrePend + language_name + OUTPUT_FILE_NAME; + output_file.open(fname.c_str(), ofstream::out); + + if (!output_file.is_open()) return NULL; + + PrintFileHeader(output_file, "SLOC COUNT RESULTS", cmd); + + PrintFileHeaderLine(output_file, "RESULTS FOR " + language_name + " FILES"); + output_file << endl; + if (legacyOutput) + { + output_file << " Total Blank | Comments | Compiler Data Exec. | Logical | File Module" << endl; + output_file << " Lines Lines | Whole Embedded | Direct. Decl. Instr. | SLOC | Type Name" << endl; + output_file << "-----------------+------------------+-------------------------+---------+---------------------------" << endl; + } + else + { + output_file << " Total Blank | Comments | Compiler Data Exec. | Logical Physical | File Module" << endl; + output_file << " Lines Lines | Whole Embedded | Direct. Decl. Instr. | SLOC SLOC | Type Name" << endl; + output_file << "-----------------+------------------+-------------------------+------------------+---------------------------" << endl; + } + } + return &output_file; + } +} + +/*! +* 1.Function Description: +* Closes the language output file stream. +* +* 2.Parameters: +* None +* +* 3.Creation Time and Owner: +* version 2009.01 +* 4. Modification : 2016.12 +* Before closing the language output files, check if copyright footer needs to be printed +*/ +void CCodeCounter::CloseOutputStream() +{ + if (output_file.is_open()) { + PrintFileFooter(output_file); //Modification 2016.12 + output_file.close(); + } + if (output_file_csv.is_open()) { + PrintFileFooter(output_file_csv); //Modification 2016.12 + output_file_csv.close(); + } + +} + +/*! +* 1.Function Description: +* Finds the first index of one of the characters of strQuote in strline.Return value is index of strQuote character in strline +* +* 2.Parameters: +* strline: string line +* strQuote: string of character(s) to find in strline +* idx_start: index of line character to start search +* QuoteEscapeFront: quote escape character +* +* 3.Creation Time and Owner: +* version 2009.01 +*/ +size_t CCodeCounter::FindQuote(string const &strline, string const &strQuote, size_t idx_start, char QuoteEscapeFront) +{ + size_t min_idx, idx; //modified in version 2009.01 + min_idx = strline.length(); + + for (size_t i = 0; i < strQuote.length(); i++) + { + idx = CUtil::FindCharAvoidEscape(strline, strQuote[i], idx_start, QuoteEscapeFront); + if (idx != string::npos && idx < min_idx) + min_idx = idx; + } + if (min_idx < strline.length()) + return min_idx; + return string::npos; +} + +/*! +* 1.Function Description: +* Replaces up to ONE quoted string inside a string starting at idx_start.Return value is the method status +* +* 2.Parameters: +* strline: string to be processed +* idx_start: index of line character to start search +* contd: specifies the quote string is continued from the previous line +* CurrentQuoteEnd: end quote character of the current status +* +* 3.Creation Time and Owner: +* version 2009.01 +* +*/ +int CCodeCounter::ReplaceQuote(string &strline, size_t &idx_start, bool &contd, char &CurrentQuoteEnd) +{ + size_t idx_end, idx_quote; + AddTraceLine( __LINE__, UCC_FUNC_NAME, strline ); // Modified: 2015.12 + if (contd) + { + idx_start = 0; //modified in version 2009.01 + if (strline[0] == CurrentQuoteEnd) //modified in version 2011.05 + { + idx_start = 1; + contd = false; + return 1; + } + strline[0] = '$'; //modified in version 2009.01 + } + else + { + // handle two quote chars in some languages, both " and ' may be accepted + idx_start = FindQuote(strline, QuoteStart, idx_start, QuoteEscapeFront); + if (idx_start != string::npos) + { + idx_quote = QuoteStart.find_first_of(strline[idx_start]); + CurrentQuoteEnd = QuoteEnd[idx_quote]; + } + else + { + idx_start = strline.length(); + return 0; + } + } + + idx_end = CUtil::FindCharAvoidEscape(strline, CurrentQuoteEnd, idx_start + 1, QuoteEscapeFront); + if (idx_end == string::npos) + { + idx_end = strline.length() - 1; + strline.replace(idx_start + 1, idx_end - idx_start, idx_end - idx_start, '$'); + contd = true; + idx_start = idx_end + 1; //modified in version 2011.05 + } + else + { + if ((QuoteEscapeRear) && (strline.length() > idx_end + 1) && (strline[idx_end+1] == QuoteEscapeRear)) //modified in version 2009.01 + { + strline[idx_end] = '$'; + strline[idx_end+1] = '$'; + } + else + { + contd = false; + strline.replace(idx_start + 1, idx_end - idx_start - 1, idx_end - idx_start - 1, '$'); + idx_start = idx_end + 1; //modified in version 2011.10 + } + } + + return 1; +} + +/*! +* 1.Function Description: +* Counts blank lines in a file.Return value is method status +* +* 2.Parameters: +* fmap: list of file lines +* result: counter results +* +* 3.Creation Time and Owner: +* version 2009.01 +* Revised 2015.12 Some support for Stack Dump if exception +*/ +int CCodeCounter::CountBlankSLOC(filemap* fmap, results* result) +{ + ENTER_1( "Start" ); // Due to Polymorphism change level above + SAVE_TO_2( "CheckBlank" ); + currentPhyLine = 0; + for (filemap::iterator i = fmap->begin(); i != fmap->end(); i++) //modified in version 2009.01 + { + currentPhyLine++; + if (CUtil::CheckBlank(i->line)) // from version 2009.01 + result->blank_lines++; + } + + return 1; +} + +/*! +* 1.Function Description: +* Counts the number of comment lines, removes comments, and +* replaces quoted strings by special chars, e.g., $.All arguments are modified by the method.Return value is the method status +* +* 2.Parameters: +* fmap: list of processed file lines +* result: counter results +* fmapBak: list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* 3.Creation Time and Owner: +* fversion 2009.01 +* Revised 2015.12 Added lightweight support for Stack Dump +*/ +int CCodeCounter::CountCommentsSLOC(filemap* fmap, results* result, filemap *fmapBak) +{ + // Check Preconditions + if (BlockCommentStart.empty() && LineCommentStart.empty()) + return 0; + if (classtype == UNKNOWN || classtype == DATAFILE) + return 0; + + bool contd = false; + bool contd_nextline; + int comment_type = 0; + /* + comment_type: + 0 : not a comment + 1 : line comment, whole line + 2 : line comment, embedded + 3 : block comment, undecided + 4 : block comment, embedded + */ + + size_t idx_start, idx_end, comment_start; + size_t quote_idx_start; + string curBlckCmtStart, curBlckCmtEnd; + char CurrentQuoteEnd = 0; + bool quote_contd = false; + filemap::iterator itfmBak = fmapBak->begin(); + + quote_idx_start = 0; + + ENTER_1( "Start" ); // Due to Polymorphism change level above + SAVE_TO_2( "Loop start" ); + currentPhyLine = 0; + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++, itfmBak++) + { + currentPhyLine++; + contd_nextline = false; + + quote_idx_start = 0; + idx_start = 0; + + SAVE_TO_2( "CUtil::CheckBlank" ); + if (CUtil::CheckBlank(iter->line)) + continue; + + if (quote_contd) + { + AddTraceLine( __LINE__, UCC_FUNC_NAME, iter->line ); // Modified: 2015.12 + + // Replace quote until next character + SAVE_TO_2( "ReplaceQuote" ); + ReplaceQuote(iter->line, quote_idx_start, quote_contd, CurrentQuoteEnd); + if (quote_contd) + continue; + } + + if (contd) + comment_type = 3; + + while (!contd_nextline && idx_start < iter->line.length()) + { + AddTraceLine( __LINE__, UCC_FUNC_NAME, iter->line ); // Modified: 2015.12 + + // need to handle multiple quote chars in some languages, both " and ' may be accepted + SAVE_TO_2( "FindQuote" ); + quote_idx_start = FindQuote(iter->line, QuoteStart, quote_idx_start, QuoteEscapeFront); + comment_start = idx_start; + if (!contd) + { + SAVE_TO_2( "FindCommentStart" ); + FindCommentStart(iter->line, comment_start, comment_type, curBlckCmtStart, curBlckCmtEnd); + } + + if (comment_start == string::npos && quote_idx_start == string::npos) + break; + + if (comment_start != string::npos) + idx_start = comment_start; + + // if found quote before comment, e.g., "this is quote");//comment + if (quote_idx_start != string::npos && (comment_start == string::npos || quote_idx_start < comment_start)) + { + AddTraceLine( __LINE__, UCC_FUNC_NAME, iter->line ); // Modified: 2015.12 + + SAVE_TO_2( "ReplaceQuote_2" ); + ReplaceQuote(iter->line, quote_idx_start, quote_contd, CurrentQuoteEnd); + if (quote_idx_start > idx_start && quote_idx_start != iter->line.length()) + { + // comment delimiter inside quote + idx_start = quote_idx_start; + continue; + } + } + else if (comment_start != string::npos) + { + // comment delimiter starts first + switch (comment_type) + { + case 1: // line comment, definitely whole line + AddTraceLine( __LINE__, UCC_FUNC_NAME, iter->line ); // Modified: 2015.12 + + iter->line = ""; + itfmBak->line = ""; + result->comment_lines++; + contd_nextline = true; + break; + case 2: // line comment, possibly embedded + AddTraceLine( __LINE__, UCC_FUNC_NAME, iter->line ); // Modified: 2015.12 + + iter->line = iter->line.substr(0, idx_start); + itfmBak->line = itfmBak->line.substr(0, idx_start); + + // trim trailing space + SAVE_TO_2( "CUtil::TrimString" ); + iter->line = CUtil::TrimString(iter->line, 1); + itfmBak->line = CUtil::TrimString(itfmBak->line, 1); + if (iter->line.empty()) + result->comment_lines++; // whole line + else + result->e_comm_lines++; // embedded + contd_nextline = true; + break; + case 3: // block comment + case 4: + AddTraceLine( __LINE__, UCC_FUNC_NAME, iter->line ); // Modified: 2015.12 + + if (contd) + idx_end = iter->line.find(curBlckCmtEnd); + else + idx_end = iter->line.find(curBlckCmtEnd, idx_start + curBlckCmtStart.length()); + + if (idx_end == string::npos) + { + if (comment_type == 3) + { + iter->line = ""; + itfmBak->line = ""; + result->comment_lines++; + } + else if (comment_type == 4) + { + iter->line = iter->line.substr(0, idx_start); + itfmBak->line = itfmBak->line.substr(0, idx_start); + + // trim trailing space + SAVE_TO_2( "CUtil::TrimString_2" ); + iter->line = CUtil::TrimString(iter->line, 1); + itfmBak->line = CUtil::TrimString(itfmBak->line, 1); + if (iter->line.empty()) //block added in 2011.10 + result->comment_lines++; // whole line + else + result->e_comm_lines++; // embedded + } + contd = true; + contd_nextline = true; + break; + } + else + { + contd = false; + iter->line.erase(idx_start, idx_end - idx_start + curBlckCmtEnd.length()); + itfmBak->line.erase(idx_start, idx_end - idx_start + curBlckCmtEnd.length()); + if (iter->line.empty()) + result->comment_lines++; + else + { + // trim trailing space + SAVE_TO_2( "CUtil::TrimString_3" ); + iter->line = CUtil::TrimString(iter->line, 1); + itfmBak->line = CUtil::TrimString(itfmBak->line, 1); + if (iter->line.empty()) //block added in 2011.10 + result->comment_lines++; // whole line + else + result->e_comm_lines++; // embedded + } + + // quote chars found may be erased as it is inside comment + quote_idx_start = idx_start; + } + break; + default: + cout << "Error in CountCommentsSLOC()" << endl; + break; + } + } + } + } + SAVE_TO_2( "Exit" ); + return 1; +} + + +/*! +* Per Line code that Counts the number of comment lines, removes comments, and +* replaces quoted strings by special chars, e.g., $ +* All arguments are modified by the method. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* \param lots of other args +* \return 0 if no errors +*/ +int CCodeCounter::CountCommentsSLOCperLine( filemap::iterator iter, + results* result, + filemap::iterator itfmBak, + bool & contd, + int & comment_type, + bool & contd_nextline, + size_t & idx_start, + size_t & idx_end, + size_t & quote_idx_start, + size_t & comment_start, + string & curBlckCmtStart, + string & curBlckCmtEnd, + bool & quote_contd, + char & CurrentQuoteEnd ) +{ + // NOTE: + // Kept the args the same as if was NOT a function call + // so below code is unchanged (except for less indentation) + // + // After use and peer review, + // strongly suggest using a struct to hold the args (maybe except for first 3) + + int retVal = 0; + + // This block of code was originally in a loop in CountCommentsSLOC + // Extracted to here so can also be called when 1 pass parser needs + // + if (contd) + comment_type = 3; + + while (!contd_nextline && idx_start < iter->line.length()) + { + // need to handle multiple quote chars in some languages, both " and ' may be accepted + quote_idx_start = FindQuote(iter->line, QuoteStart, quote_idx_start, QuoteEscapeFront); + comment_start = idx_start; + if (!contd) + FindCommentStart(iter->line, comment_start, comment_type, curBlckCmtStart, curBlckCmtEnd); + + if (comment_start == string::npos && quote_idx_start == string::npos) + break; + + if (comment_start != string::npos) + idx_start = comment_start; + + // if found quote before comment, e.g., "this is quote");//comment + if (quote_idx_start != string::npos && (comment_start == string::npos || quote_idx_start < comment_start)) + { + ReplaceQuote(iter->line, quote_idx_start, quote_contd, CurrentQuoteEnd); + if (quote_idx_start > idx_start && quote_idx_start != iter->line.length()) + { + // comment delimiter inside quote + idx_start = quote_idx_start; + continue; + } + } + else if (comment_start != string::npos) + { + // comment delimiter starts first + switch (comment_type) + { + case 1: // line comment, definitely whole line + iter->line = ""; + itfmBak->line = ""; + result->comment_lines++; + contd_nextline = true; + break; + case 2: // line comment, possibly embedded + iter->line = iter->line.substr(0, idx_start); + itfmBak->line = itfmBak->line.substr(0, idx_start); + // trim trailing space + iter->line = CUtil::TrimString(iter->line, 1); + itfmBak->line = CUtil::TrimString(itfmBak->line, 1); + if (iter->line.empty()) + result->comment_lines++; // whole line + else + result->e_comm_lines++; // embedded + contd_nextline = true; + break; + case 3: // block comment + case 4: + if (contd) + idx_end = iter->line.find(curBlckCmtEnd); + else + idx_end = iter->line.find(curBlckCmtEnd, idx_start + curBlckCmtStart.length()); + + if (idx_end == string::npos) + { + if (comment_type == 3) + { + iter->line = ""; + itfmBak->line = ""; + result->comment_lines++; + } + else if (comment_type == 4) + { + iter->line = iter->line.substr(0, idx_start); + itfmBak->line = itfmBak->line.substr(0, idx_start); + // trim trailing space + iter->line = CUtil::TrimString(iter->line, 1); + itfmBak->line = CUtil::TrimString(itfmBak->line, 1); + if (iter->line.empty()) + result->comment_lines++; // whole line + else + result->e_comm_lines++; // embedded + } + contd = true; + contd_nextline = true; + break; + } + else + { + contd = false; + iter->line.erase(idx_start, idx_end - idx_start + curBlckCmtEnd.length()); + itfmBak->line.erase(idx_start, idx_end - idx_start + curBlckCmtEnd.length()); + if (iter->line.empty()) + result->comment_lines++; + else + { + // trim trailing space + iter->line = CUtil::TrimString(iter->line, 1); + itfmBak->line = CUtil::TrimString(itfmBak->line, 1); + if (iter->line.empty()) + result->comment_lines++; // whole line + else + result->e_comm_lines++; // embedded + } + + // quote chars found may be erased as it is inside comment + quote_idx_start = idx_start; + } + break; + default: + cout << "Error in CountCommentsSLOCPerLine()" << endl; + retVal = -7; + break; + } + } + } + + return retVal; +} + + +/*! +* 1.Function Description: + Finds a starting position of a comment in a string starting at idx_start.Return value is the method status +* +* 2.Parameters: +* strline: string to be processed +* idx_start: index of line character to start search +* comment_type: comment type (0=not a comment, 1=whole line, 2=embedded line, 3=whole line block, 4=embedded block) +* curBlckCmtStart: current block comment start string +* curBlckCmtEnd: current block comment end string +* +* 3.Creation Time and Owner: +* version 2009.01 +*/ +int CCodeCounter::FindCommentStart(string strline, size_t &idx_start, int &comment_type, + string &curBlckCmtStart, string &curBlckCmtEnd) +{ + size_t idx_line, idx_tmp, idx_block; + string line = strline; + comment_type = 0; + + ENTER_2( "Start" ); // Due to Polymorphism change level above + + if (!casesensitive) + { + SAVE_TO_3( "CUtil::ToLower" ); + line = CUtil::ToLower(line); + } + + // searching for starting of line comment + idx_line = string::npos; + for (StringVector::iterator i = LineCommentStart.begin(); i != LineCommentStart.end(); i++) + { + idx_tmp = line.find((casesensitive ? (*i) : CUtil::ToLower(*i)), idx_start); + if (idx_tmp < idx_line) + idx_line = idx_tmp; + } + + // searching for starting of block comment + idx_block = string::npos; + for (StringVector::iterator i = BlockCommentStart.begin(); i != BlockCommentStart.end(); i++) + { + idx_tmp = strline.find(*i, idx_start); + if (idx_tmp < idx_block) + { + idx_block = idx_tmp; + curBlckCmtStart = *i; + curBlckCmtEnd = *(BlockCommentEnd.begin() + (i - BlockCommentStart.begin())); + } + } + + // see what kind of comment appears first + if (idx_line == string::npos && idx_block == string::npos) + { + comment_type = 0; + idx_start = idx_line; + } + else if (idx_block > idx_line) + { + idx_start = idx_line; + comment_type = idx_start == 0 ? 1 : 2; + } + else + { + idx_start = idx_block; + comment_type = idx_start == 0 ? 3 : 4; + } + return 1; +} + +/*! +* 1.Function Description: +* Counts file language complexity based on specified language keywords/characters.Return value is the method status +* +* 2.Parameters: +* fmap: list of processed file lines +* result: counter results +* +* 3.Creation Time and Owner: +* version 2009.01 +* Revised 2015.12 Added lightweight support for Stack Dump +*/ +int CCodeCounter::CountComplexity(filemap* fmap, results* result) +{ + //AddTraceLine( __LINE__, UCC_FUNC_NAME, "" ); // Modified: 2015.12 + + if (classtype == UNKNOWN || classtype == DATAFILE) // modified in version 2011.10 + return 0; + + ENTER_1( "Start" ); // Due to Polymorphism change level above + SAVE_TO_2( "Start CCodeCounter::CountComplexity" ); + + filemap::iterator fit; //modified in version 2009.01 + size_t idx; //modified in version 2013.04 + unsigned int cnt, ret, cyclomatic_cnt = 0, ignore_cyclomatic_cnt = 0, main_cyclomatic_cnt = 0, function_count = 0, cyclomatic_logic_cnt = 0, main_cyclomatic_logic_cnt = 1, cyclomatic_case_cnt = 0, main_cyclomatic_case_cnt = 1, cyclomatic_default_cnt = 0, cyclomatic_switch_cnt = 0;//modified in version 2014.08 + + unsigned int main_cyclomatic_distinct_cond_cnt = 1; // integrate from CC4 2015:06:26 + unsigned int cyclomatic_repeated_cond_cnt = 0; //integrate from CC4 2015:06:26 + + string line, lastline, file_ext, function_name = ""; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; //modified in version 2009.01 + filemap function_stack; //modified in version 2013.04 + + stack cyclomatic_repeated_cond_stack;// integrate from CC4 2015:06:26 + + stack cyclomatic_stack; + stack cyclomatic_logic_stack; //modified in version 2014.08 + stack cyclomatic_case_stack; + + //////Integrate from CC4 2015:06:26 + stack > cyclomatic_distinct_cond_stack; // at the end of a function, the size of this stack is CC4 value of this function + set cyclomatic_distinct_cond_set; + string cc4_valid_if; // the vaild if get from current line + stack cc4_parent_stack; + int cc4_nested_dup=0; // in a function + stack cc4_nested_dup_stack; + set nested_set; + map cond_CC4_map; + ///////// + + map function_map; //modified in version 2013.04 + map logical_map; //modified in version 2014.08 + map case_map; + bool process_cyclomatic_complexity = false; // modified in version 2013.04 + + // check whether to process cyclomatic complexity + if (cmplx_cyclomatic_list.size() > 0) + { + //AddTraceLine( __LINE__, UCC_FUNC_NAME, "Process Cyclomatic Complexity" ); // Modified: 2015.12 + + process_cyclomatic_complexity = true; + if (skip_cmplx_cyclomatic_file_extension_list.size() > 0) + { + idx = result->file_name.find_last_of("."); + if (idx != string::npos) + { + file_ext = result->file_name.substr(idx); + file_ext = CUtil::ToLower(file_ext); + if (find(skip_cmplx_cyclomatic_file_extension_list.begin(), skip_cmplx_cyclomatic_file_extension_list.end(), file_ext) != skip_cmplx_cyclomatic_file_extension_list.end()) + process_cyclomatic_complexity = false; + } + } + } + + // process each line + SAVE_TO_2( "CCodeCounter::CountComplexity loop start" ); + currentPhyLine = 0; + for (fit = fmap->begin(); fit != fmap->end(); fit++) // added in version 2009.01 + { + currentPhyLine++; + + //AddTraceLine( __LINE__, UCC_FUNC_NAME, fit->line ); // Modified: 2015.12 + + line = fit->line; + + SAVE_TO_2( "CUtil::CheckBlank" ); + if (CUtil::CheckBlank(line)) + continue; + + line = " " + line; + // mathematical functions + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Math" ); + CUtil::CountTally(line, math_func_list, cnt, 1, exclude, "", "", &result->math_func_count, casesensitive); + result->cmplx_math_lines += cnt; + + // trigonometric functions + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Trigonometric" ); + CUtil::CountTally(line, trig_func_list, cnt, 1, exclude, "", "", &result->trig_func_count, casesensitive); + result->cmplx_trig_lines += cnt; + + // logarithmic functions + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Logrithmic" ); + CUtil::CountTally(line, log_func_list, cnt, 1, exclude, "", "", &result->log_func_count, casesensitive); + + // calculations + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Calculations" ); + CUtil::CountTally(line, cmplx_calc_list, cnt, 1, exclude, "", "", &result->cmplx_calc_count, casesensitive); + result->cmplx_calc_lines += cnt; + + // conditionals + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Conditionals" ); + CUtil::CountTally(line, cmplx_cond_list, cnt, 1, exclude, "", "", &result->cmplx_cond_count, casesensitive); + result->cmplx_cond_lines += cnt; + + // logical operators + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Logical" ); + CUtil::CountTally(line, cmplx_logic_list, cnt, 1, exclude, "", "", &result->cmplx_logic_count, casesensitive); + result->cmplx_logic_lines += cnt; + + // preprocessor directives + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Preprocessor" ); + CUtil::CountTally(line, cmplx_preproc_list, cnt, 1, exclude, "", "", &result->cmplx_preproc_count, casesensitive); + result->cmplx_preproc_lines += cnt; + + // assignments + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Assignments" ); + CUtil::CountTally(line, cmplx_assign_list, cnt, 1, exclude, "", "", &result->cmplx_assign_count, casesensitive); + result->cmplx_assign_lines += cnt; + + // pointers + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Pointers" ); + // Pointers are embedded syntax so there is NO exclude string or include strings + CUtil::CountTally(line, cmplx_pointer_list, cnt, 1, "", "", "", &result->cmplx_pointer_count, casesensitive); + result->cmplx_pointer_lines += cnt; + //AddTraceLine( __LINE__, UCC_FUNC_NAME, fit->line ); // Modified: 2015.12 + + // cyclomatic complexity + if (process_cyclomatic_complexity) //added in version 2013.04 + { + // search for cyclomatic complexity keywords + SAVE_TO_2( "CUtil::CountTally Cyclomatic" ); + CUtil::CountTally(line, cmplx_cyclomatic_list, cyclomatic_cnt, 1, exclude, "", "", 0, casesensitive); + + SAVE_TO_2( "CUtil::CountTally Cyclomatic switch" ); + CUtil::CountTally(line, cmplx_cyclomatic_switch_list, cyclomatic_switch_cnt, 1, exclude, "", "", 0, casesensitive); //added in version 2014.08 + + // Allow User to control this as the RECURSIVE implementation uses extra RAM and runs SLOWER. About 17% slower than 2014 ! + if ( cc4enable ) + { + //////Integrate from CC4 2015:06:26 + //AddTraceLine( __LINE__, UCC_FUNC_NAME, line ); // Modified: 2015.12 + SAVE_TO_2( "CUtil::CountDistinctCond" ); + CUtil::CountDistinctCond(cc4_valid_if, line, cmplx_cyclomatic_list,cyclomatic_repeated_cond_cnt , 1, exclude, "", "", cyclomatic_distinct_cond_set, 0, casesensitive); + + SAVE_TO_2( "CUtil::SemanticDeduplicate" ); + cc4_nested_dup += CUtil::SemanticDeduplicate(cc4_valid_if, cc4_parent_stack, cyclomatic_distinct_cond_stack, nested_set); + + //std::ostringstream ss; + //ss << "cc4_nested_dup " << cc4_nested_dup; + //AddTraceLine( __LINE__, UCC_FUNC_NAME, ss.str() ); // Modified: 2015.12 + /////////////// + } // END if ( cc4enable ) + + // search for keywords to exclude + if (ignore_cmplx_cyclomatic_list.size() > 0) + { + SAVE_TO_2( "CUtil::CountTally Exclude Keywords" ); + CUtil::CountTally(line, ignore_cmplx_cyclomatic_list, ignore_cyclomatic_cnt, 1, exclude, "", "", 0, casesensitive); + } + + // search for cyclomatic complexity logical keywords + if (cmplx_cyclomatic_logic_list.size() > 0) + { + SAVE_TO_2( "CUtil::CountTally Cyclomatic Complexity Logical Keywords" ); + CUtil::CountTally(line, cmplx_cyclomatic_logic_list, cyclomatic_logic_cnt, 1, exclude, "", "", 0, casesensitive); + } + + // search for cyclomatic complexity case keywords + if (cmplx_cyclomatic_case_list.size() > 0) + { + SAVE_TO_2( "CUtil::CountTally Cyclomatic Complexity Case Keywords" ); + CUtil::CountTally(line, cmplx_cyclomatic_case_list, cyclomatic_case_cnt, 1, exclude, "", "", 0, casesensitive); + } + + // search for cyclomatic complexity case default keywords + if (cmplx_cyclomatic_default_list.size() > 0) + { + SAVE_TO_2( "CUtil::CountTally Cyclomatic Complexity Case Default Keywords" ); + CUtil::CountTally(line, cmplx_cyclomatic_default_list, cyclomatic_default_cnt, 1, exclude, "", "", 0, casesensitive); + } + + if(cyclomatic_default_cnt > 0) + { + cyclomatic_cnt -= cyclomatic_default_cnt; + cyclomatic_case_cnt -= cyclomatic_default_cnt; + cyclomatic_default_cnt = 0; + } + + // parse function name if found + SAVE_TO_2( "ParseFunctionName" ); + ret = (unsigned int)ParseFunctionName(line, lastline, function_stack, function_name, function_count); //added in version 2013.04 + if (ret != 1 && !cyclomatic_stack.empty() && cyclomatic_stack.size() == function_stack.size()) + { + // remove count stack entry for non-function names + cyclomatic_cnt += cyclomatic_stack.top(); + ignore_cyclomatic_cnt = 0; + cyclomatic_stack.pop(); + } + if (ret != 1 && !cyclomatic_logic_stack.empty() && cyclomatic_logic_stack.size() == function_stack.size()) //added in version 2014.08 + { + // remove count stack entry for non-function names + cyclomatic_logic_cnt += cyclomatic_logic_stack.top(); + cyclomatic_logic_stack.pop(); + } + if (ret != 1 && !cyclomatic_case_stack.empty() && cyclomatic_case_stack.size() == function_stack.size()) //added in version 2014.08 + { + // remove count stack entry for non-function names + cyclomatic_case_cnt += cyclomatic_case_stack.top(); + cyclomatic_case_stack.pop(); + } + + if ( cc4enable ) + { + ///////////Integrate from CC4 2015:06:26 + if (ret != 1 && !cyclomatic_distinct_cond_stack.empty() && cyclomatic_distinct_cond_stack.size() == function_stack.size()) + { + // remove count stack entry for non-function names + + set temp_set = cyclomatic_distinct_cond_stack.top(); + if (temp_set.size() > 0){ + //cout << "-----------line796"<::iterator it; + string temp; + for(it=cyclomatic_distinct_cond_set.begin(); it!=cyclomatic_distinct_cond_set.end(); it++){ + temp = *it; + if(temp.find("&&")==string::npos){ + cc4_counter++; + }else{ + cc4_counter+=CUtil::CountNestedNum(temp); + + } + } + + /*** COMMENTED OUT as AddTraceLine is not to be used + std::ostringstream ss; + ss << "cc4_counter " << cc4_counter; + AddTraceLine( __LINE__, UCC_FUNC_NAME, ss.str() ); // Modified: 2015.12 + ss.str(""); + ss.clear(); + + ss << "cc4_nested_dup " << cc4_nested_dup; + AddTraceLine( __LINE__, UCC_FUNC_NAME, ss.str() ); // Modified: 2015.12 + ss.str(""); + ss.clear(); + + ss << "ignore_cyclomatic_cnt " << ignore_cyclomatic_cnt; + AddTraceLine( __LINE__, UCC_FUNC_NAME, ss.str() ); // Modified: 2015.12 + ss.str(""); + ss.clear(); + ************ END of COMMENTED OUT */ + + lineElement cc4_element(cc4_counter -cc4_nested_dup -ignore_cyclomatic_cnt + 1, function_name); + + /*** COMMENTED OUT as AddTraceLine is not to be used + ss << "function name: " << function_name << " CC4: " << cc4_element.lineNumber; + AddTraceLine( __LINE__, UCC_FUNC_NAME, ss.str() ); // Modified: 2015.12 + ss.str(""); + ss.clear(); + ************ END of COMMENTED OUT */ + + + nested_set.clear(); + cc4_nested_dup=0; + + cond_CC4_map[function_count] = cc4_element; + //////////////////// + } + + if (!function_stack.empty()) //added in version 2013.04 + { + // grab previous function from stack to continue + if (!cyclomatic_stack.empty()) + { + cyclomatic_cnt = cyclomatic_stack.top(); + cyclomatic_stack.pop(); + } + if (!cyclomatic_logic_stack.empty()) //added in version 2014.08 + { + cyclomatic_logic_cnt = cyclomatic_logic_stack.top(); + cyclomatic_logic_stack.pop(); + } + if (!cyclomatic_case_stack.empty()) //added in version 2014.08 + { + cyclomatic_case_cnt = cyclomatic_case_stack.top(); + cyclomatic_case_stack.pop(); + } + + if ( cc4enable ) + { + ///////Integrate from CC4 2015:06:26 + if (!cyclomatic_distinct_cond_stack.empty()) + { + AddTraceLine( __LINE__, UCC_FUNC_NAME, "CC4: go back to leavl" ); // Modified: 2015.12 + cyclomatic_distinct_cond_set = cyclomatic_distinct_cond_stack.top(); + cyclomatic_distinct_cond_stack.pop(); + } + //////// + } + } + else { + cyclomatic_cnt = 0; + cyclomatic_logic_cnt = 0; + cyclomatic_case_cnt = 0; + cyclomatic_distinct_cond_set.clear();///Integrate from CC4 2015:06:26 + } + function_name = ""; + ignore_cyclomatic_cnt = 0; + cyclomatic_switch_cnt = 0; + } + else if (ret == 2) //added in version 2013.04 + { + if (main_cyclomatic_cnt < 1) + main_cyclomatic_cnt = 1; // add 1 for main function here in case no other decision points are found in main + // some code doesn't belong to any function + main_cyclomatic_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt; //added in version 2014.08 + main_cyclomatic_logic_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt + cyclomatic_logic_cnt; + main_cyclomatic_case_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt - cyclomatic_case_cnt + cyclomatic_switch_cnt; + if ( cc4enable ) + { + main_cyclomatic_distinct_cond_cnt += cyclomatic_distinct_cond_set.size() - ignore_cyclomatic_cnt;/////Integrate from CC4 2015:06:26 + cyclomatic_distinct_cond_set.clear();/////Integrate from CC4 2015:06:26 + } + cyclomatic_cnt = ignore_cyclomatic_cnt = cyclomatic_logic_cnt = cyclomatic_case_cnt = cyclomatic_switch_cnt = 0; + } + else { + if (!function_stack.empty() && (function_stack.size() > cyclomatic_stack.size() + 1 || (cyclomatic_stack.empty() && function_stack.size() > 1))) //added in version 2013.04 + { + // capture previous complexity count from open function + cyclomatic_stack.push(cyclomatic_cnt - ignore_cyclomatic_cnt); + cyclomatic_cnt = ignore_cyclomatic_cnt = 0; + } + if (!function_stack.empty() && (function_stack.size() > cyclomatic_logic_stack.size() + 1 || (cyclomatic_logic_stack.empty() && function_stack.size() > 1))) //added in version 2014.08 + { + // capture previous complexity count from open function + cyclomatic_logic_stack.push(cyclomatic_logic_cnt); + cyclomatic_logic_cnt = 0; + } + if (!function_stack.empty() && (function_stack.size() > cyclomatic_case_stack.size() + 1 || (cyclomatic_case_stack.empty() && function_stack.size() > 1))) //added in version 2014.08 + { + // capture previous complexity count from open function + cyclomatic_case_stack.push(cyclomatic_case_cnt - cyclomatic_switch_cnt); + cyclomatic_case_cnt = 0; + cyclomatic_switch_cnt = 0; + } + + if ( cc4enable ) + { + ////Integrate from CC4 2015:06:26 + if (!function_stack.empty() && (function_stack.size() > cyclomatic_distinct_cond_stack.size() + 1 || (cyclomatic_distinct_cond_stack.empty() && function_stack.size() > 1))) + { + // capture previous complexity count from open function + cyclomatic_distinct_cond_stack.push(cyclomatic_distinct_cond_set); + cyclomatic_distinct_cond_set.clear(); + std::ostringstream ss; + cc4_parent_stack.push(cc4_valid_if); + ss << "cc4_parent_stack push " << cc4_valid_if; + AddTraceLine( __LINE__, UCC_FUNC_NAME, ss.str() ); // Modified: 2015.12 + ss.str(""); + ss.clear(); + } + ///////////////// + } + } + } + } + + // done with a file + SAVE_TO_2( "CCodeCounter::CountComplexity done with a file" ); + if (main_cyclomatic_cnt > 0) //added in version 2013.04 + { + // add "main" code + lineElement element(main_cyclomatic_cnt, "main"); + lineElement n_element(main_cyclomatic_logic_cnt, "main"); //added in version 2014.08 + lineElement c_element(main_cyclomatic_case_cnt, "main"); + + function_map[0] = element; //added in version 2013.04 + logical_map[0] = n_element; //added in version 2014.08 + case_map[0] = c_element; + if ( cc4enable ) + { + lineElement cc4_element(main_cyclomatic_distinct_cond_cnt, "main"); //Integrate from CC4 2015:06:26 + cond_CC4_map[0] = cc4_element; //Integrate from CC4 2015:06:26 + } + } + else //added in version 2013.04 + { + // finish the first function if not closed + SAVE_TO_2( "CCodeCounter::CountComplexity finish the first function if not closed" ); + while (!function_stack.empty()) //added in version 2013.04 + { + function_name = function_stack.back().line; + function_count = function_stack.back().lineNumber; + function_stack.pop_back(); + + if (!function_stack.empty()) //added in version 2013.04 + { + // grab previous function from stack to continue + if (!cyclomatic_stack.empty()) + { + cyclomatic_cnt = cyclomatic_stack.top(); + cyclomatic_stack.pop(); + } + + if ( cc4enable ) + { + /////Integrate from CC4 2015:06:26 + if (!cyclomatic_distinct_cond_stack.empty()) + { + set temp_set = cyclomatic_distinct_cond_stack.top(); + cyclomatic_distinct_cond_set.insert(temp_set.begin(), temp_set.end()); + cyclomatic_distinct_cond_stack.pop(); + } + //////// + } + } + else //added in version 2013.04 + { + // capture count at end of function + lineElement element(cyclomatic_cnt - ignore_cyclomatic_cnt + 1, function_name); //added in version 2014.08 + lineElement n_element(cyclomatic_cnt - ignore_cyclomatic_cnt + cyclomatic_logic_cnt + 1, function_name); + lineElement c_element(cyclomatic_cnt - ignore_cyclomatic_cnt - cyclomatic_case_cnt + cyclomatic_switch_cnt + 1, function_name); + + function_map[function_count] = element; + logical_map[function_count] = n_element; + case_map[function_count] = c_element; + + if ( cc4enable ) + { + lineElement cc4_element(cyclomatic_distinct_cond_set.size() -cc4_nested_dup- ignore_cyclomatic_cnt + 1, function_name); //Integrate from CC4 2015:06:26 + cond_CC4_map[function_count] = cc4_element; //Integrate from CC4 2015:06:26 + } + } + } + } + + // process ordered functions + SAVE_TO_2( "CCodeCounter::CountComplexity process ordered functions" ); + for (map::iterator it = function_map.begin(); it != function_map.end(); ++it) //added in version 2013.04 + result->cmplx_cycfunct_count.push_back(it->second); + if(cmplx_cyclomatic_logic_list.size() > 0) //added in version 2014.08 + { + for (map::iterator it = logical_map.begin(); it != logical_map.end(); ++it) + result->cmplx_cycfunct_CC2_count.push_back(it->second); + } + if(cmplx_cyclomatic_case_list.size() > 0) + { + for (map::iterator it = case_map.begin(); it != case_map.end(); ++it) + result->cmplx_cycfunct_CC3_count.push_back(it->second); + } + + if ( cc4enable ) + { + /////Integrate from CC4 2015:06:26 + if (cmplx_cyclomatic_list.size() > 0) + { + //cout << "CC4 output" << endl; + for (map::iterator it = cond_CC4_map.begin(); it != cond_CC4_map.end(); ++it) + { + //cout << it->second.line << endl << it->second.lineNumber<cmplx_cycfunct_CC4_count.push_back(it->second); + //AddTraceLine( __LINE__, UCC_FUNC_NAME, "CC4: push into result " ); // Modified: 2015.12 + } + } + ////////////////// + } + + SAVE_TO_2( "Exit CCodeCounter::CountComplexity" ); + return 1; +} + + +/*! +* See if information is available to do Cyclomatic Complexity parsing +* +* \param result counter results +* +* \return true if OK to do Cyclomatic checks (call depths) +*/ +bool CCodeCounter::CanProcessCyclomaticComplexity( const results* result ) +{ + bool process_cyclomatic_complexity = false; + + // check whether to process cyclomatic complexity + if ( cmplx_cyclomatic_list.size() > 0 ) + { + process_cyclomatic_complexity = true; + if (skip_cmplx_cyclomatic_file_extension_list.size() > 0) + { + size_t idx = result->file_name.find_last_of("."); + if (idx != string::npos) + { + string file_ext = result->file_name.substr( idx ); + file_ext = CUtil::ToLower( file_ext ); + if ( find( skip_cmplx_cyclomatic_file_extension_list.begin(), skip_cmplx_cyclomatic_file_extension_list.end(), file_ext ) != skip_cmplx_cyclomatic_file_extension_list.end() ) + { + // Skip the extension of this file + process_cyclomatic_complexity = false; + } + } + } + } + + return process_cyclomatic_complexity; +} + + +/*! +* 1.Function Description: +* Processes physical and logical lines. +* This method is typically implemented in the specific language sub-class.Return value is the method status +* +* 2.Parameters: +* fmap: list of processed file lines +* result: counter results +* fmapBak: list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* 3.Creation Time and Owner: +* version 2011.05 +*/ +int CCodeCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* /*fmapBak*/) +{ + SAVE_TO_2( "Start CCodeCounter::LanguageSpecificProcess" ); + + // Set to check blank here as that is only call in loop + SAVE_TO_2( "CUtil::CheckBlank" ); + currentPhyLine = 0; + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++) + { + currentPhyLine++; + if (!CUtil::CheckBlank(iter->line)) + result->exec_lines[PHY]++; + + AddTraceLine( __LINE__, UCC_FUNC_NAME, iter->line ); // Modified: 2015.12 + } + + SAVE_TO_2( "Exit CCodeCounter::LanguageSpecificProcess" ); + return 1; +} + + +////////////////////////////////////////////////////////////////////////////// +// +// ALL below is support for 1 Pass parsing +// +////////////////////////////////////////////////////////////////////////////// +#ifdef ENABLE_1_PASS_PARSER +/*! +* Parse values of interest and find counts in the source file. +* +* \param fmap list of file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status (whatever that is) +*/ +int CCodeCounter::ParseFile( filemap* fmap, results* result, filemap* fmapBak ) +{ +/* Alternate Parsing Approach: +* -------------------------- +* Ideally each Physical line in a file should be parsed just once. +* Instead of multiple passes to maybe find special keywords/symbols +* the code should just work through the Physical line once. +* +* How to implement this new single pass parse approach? +* For each Physical line in a file: +* Code is needed to get parts of a given Physical line so that: +* An API like strtok would be needed (using whitespace as delimiter?) +* Then for each piece of text found: +* Try to pick it apart based on various special characters. +* When no more smaller pieces can be found, +* Search a data structure for matches of a small piece. +* Based on the match found, set various flags, counters, etc. +* Keep going until no more pieces of the Physical line remain. +* Loop back to parse another Physical line. +* +* What Data structure(s) are useful to support the single pass? +* +* How about an Array of all the keywords and all the special symbols? +* The keyword/symbol Array would be sorted so a Binary search may be used. +* +* If the keyword/symbol Array has a match, then the Type of the match is needed. +* Consider a Type array with a 1 to 1 correspondence to the keyword array. +* The Type array would give guidance on how to proceed with parsing. +* Types may be: Math function(s), Data declarations, Directives (preprocessor), etc. +*/ + int retVal = 0; + + // check for file types (languages) that don't get parsed here + if (classtype == UNKNOWN || classtype == DATAFILE) + return NO_PARSE_DONE; + +// DEBUG + //print_cmplx = false; // true adds duplicate keywords such as: while + + // Set up some parse keyword search support for single pass parsing + ENTER_1( "Start" ); + retVal = PreParseFile( result ); + +// Set up initial values needed across multiple lines + unsigned int lines_with_errors = 0; + currentPhyLine = 0; + +// COMMENTS code support + bool check_comments = true; + if (BlockCommentStart.empty() && LineCommentStart.empty()) + check_comments = false; + + bool contd = false; + bool contd_nextline; + /* + comment_type: + 0 : not a comment + 1 : line comment, whole line + 2 : line comment, embedded + 3 : block comment, undecided + 4 : block comment, embedded + */ + int comment_type = 0; + + size_t idx_start, idx_end, comment_start; + size_t quote_idx_start; + string curBlckCmtStart, curBlckCmtEnd; + char CurrentQuoteEnd = 0; + bool quote_contd = false; + + quote_idx_start = 0; + + +// PARSE the file one line at a time + filemap::iterator itfmBak = fmapBak->begin(); + + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++, itfmBak++) + { + // Keep track of current line for use in Error or other messages + currentPhyLine++; + + // Always check for a blank line first; compare with code in CountBlankSLOC + if ( CUtil::CheckBlank(iter->line) ) + { + result->blank_lines++; + continue; + } + + result->exec_lines[PHY]++; // Not blank, so counts as Physical line + +#ifdef _DEBUG_1_PASS_PARSE + parse_physical_line = iter->line; + parse_physical_line_comments = itfmBak->line; +#endif + + if ( true == check_comments ) + { + // Compare this with code in CountCommentsSLOC + contd_nextline = false; + + quote_idx_start = 0; + idx_start = 0; + + if (quote_contd) + { + // Replace quote until next character + ReplaceQuote(iter->line, quote_idx_start, quote_contd, CurrentQuoteEnd); + if (quote_contd) + { + parse_physical_line = iter->line; + parse_physical_line_comments = itfmBak->line; + continue; // OK to continue here as rest is in a Comment + } + } + + int comment_error = CountCommentsSLOCperLine( iter, + result, + itfmBak, + contd, + comment_type, + contd_nextline, + idx_start, + idx_end, + quote_idx_start, + comment_start, + curBlckCmtStart, + curBlckCmtEnd, + quote_contd, + CurrentQuoteEnd ); + if ( comment_error ) + lines_with_errors++; + + } // END of processing Comments + + // Here we know this Line is not Blank and any Comments were handled + // Line could now be an Empty string from Comments handling + if ( 0 < iter->line.size() ) + { + parse_physical_line = iter->line; + parse_physical_line_comments = itfmBak->line; + int parse_error = ParseLine( currentPhyLine, iter, result, itfmBak ); + + if ( parse_error ) + lines_with_errors++; + } + } + + //if ( lines_with_errors ) + + return retVal; +} + + +/*! +* Populate data structures needed by ParseFile +* +* \param result counter results +* \class global parse_alloc_count +* \class global pe_keyword_max_size +* \class global pe_op_max_size +* \class global pe +* \class global pe_max_idx +* \return 0 = no errors, negative = severe errors, positive = recoverable errors +*/ +int CCodeCounter::PreParseFile( const results* result ) +{ + int retVal = 0; + + if ( 0 < parse_alloc_count ) + return retVal; // already done + + ENTER_2( "Start" ); + bool process_cyclomatic_complexity = false; + + // Find total number of parse helper structs to allocate + parse_alloc_count = 0; + pe_keyword_max_size = 0; // This will also cover op max size + pe_op_max_size = 0; // for Calc, Logic, Assign, Pointer + + parse_alloc_count += BlockCommentStart.size(); + parse_alloc_count += BlockCommentEnd.size(); + parse_alloc_count += LineCommentStart.size(); + parse_alloc_count += directive.size(); + parse_alloc_count += data_name_list.size(); + parse_alloc_count += exec_name_list.size(); + parse_alloc_count += math_func_list.size(); + parse_alloc_count += trig_func_list.size(); + parse_alloc_count += log_func_list.size(); + + if ( true == print_cmplx ) + { + parse_alloc_count += cmplx_calc_list.size(); + // parse_alloc_count += cmplx_cond_list.size(); done after sort + parse_alloc_count += cmplx_logic_list.size(); + // parse_alloc_count += cmplx_preproc_list.size(); done after sort + parse_alloc_count += cmplx_assign_list.size(); + parse_alloc_count += cmplx_pointer_list.size(); + + // check whether to process cyclomatic complexity + if ( 0 < aux_meaning_keys.size() ) + { + process_cyclomatic_complexity = CanProcessCyclomaticComplexity( result ); + if ( true == process_cyclomatic_complexity ) + { + // DO NOT add to parse list! + // The auxillary meanings will be done after parse list sort + // parse_alloc_count += cmplx_cyclomatic_list.size(); + } + } + } + + // Allocate the array of parse helper structures + pe = (parseElement *) new parseElement[ parse_alloc_count ]; + + if ( (parseElement *) NULL == pe ) + return -1; // Memory allocation error + + // Populate the parse element array + pe_max_idx = 0; + unsigned int empty_string_count = 0; + + AddToParse( pe_max_idx, pe_keyword_max_size, empty_string_count, BLK_COMMENT_START, BlockCommentStart ); + AddToParse( pe_max_idx, pe_keyword_max_size, empty_string_count, BLK_COMMENT_END, BlockCommentEnd ); + AddToParse( pe_max_idx, pe_keyword_max_size, empty_string_count, LINE_COMMENT_START, LineCommentStart ); + AddToParse( pe_max_idx, pe_keyword_max_size, empty_string_count, DIRECTIVE, directive ); + AddToParse( pe_max_idx, pe_keyword_max_size, empty_string_count, DATA_NAME, data_name_list ); + AddToParse( pe_max_idx, pe_keyword_max_size, empty_string_count, EXEC_NAME, exec_name_list ); + AddToParse( pe_max_idx, pe_keyword_max_size, empty_string_count, MATH_FUNC, math_func_list ); + AddToParse( pe_max_idx, pe_keyword_max_size, empty_string_count, TRIG_FUNC, trig_func_list ); + AddToParse( pe_max_idx, pe_keyword_max_size, empty_string_count, LOG_FUNC, log_func_list ); + + if ( true == print_cmplx ) + { + AddToParse( pe_max_idx, pe_op_max_size, empty_string_count, CMPLX_CALC, cmplx_calc_list ); + //AddToParse( pe_max_idx, pe_keyword_max_size, empty_string_count, CMPLX_COND, cmplx_cond_list ); done after sort + AddToParse( pe_max_idx, pe_op_max_size, empty_string_count, CMPLX_LOGIC, cmplx_logic_list ); + // AddToParse( pe_max_idx, pe_keyword_max_size, empty_string_count, CMPLX_PREPROC, cmplx_preproc_list ); done after sort + AddToParse( pe_max_idx, pe_op_max_size, empty_string_count, CMPLX_ASSIGN, cmplx_assign_list ); + AddToParse( pe_max_idx, pe_op_max_size, empty_string_count, CMPLX_POINTER, cmplx_pointer_list ); + + if ( true == process_cyclomatic_complexity ) + { + // DO NOT add these to the parse list! Auxillary meanings is now used. + // AddToParse( pe_max_idx, pe_keyword_max_size, empty_string_count, CMPLX_CYCLOMATIC, cmplx_cyclomatic_list ); + } + } + + if ( pe_keyword_max_size < pe_op_max_size ) + pe_keyword_max_size = pe_op_max_size; + + // NOTE: If there were any empty strings the pe_max_idx value is less than the parse_alloc_count. + +// SORT + // the array by keyword name or symbol and then by parse type if same name + bool swap_needed = false; + unsigned int dup_keywords = 0; + unsigned int total_swaps = 0; + + // Temporary variables for swap kept outside loops + ParseType type = PARSE_UNKNOWN; + string keyword = ""; + unsigned int idx = 0; + + for ( unsigned int i = 0; i < pe_max_idx; i++ ) + { + for ( unsigned int j = 0; j < pe_max_idx; j++ ) + { + if ( i == j ) + continue; + + if ( pe[i].keyword <= pe[j].keyword ) + { + swap_needed = true; + if ( pe[i].keyword == pe[j].keyword ) + { + // In the 1 pass approach, this is a coding table definition error + pe_dup_keywords = true; + cout << endl << "Error: PreParseFile given duplicate Keyword " << pe[i].keyword << endl; + + // NOT according to design but allow continuing to maybe find other coding errors. + // Keywords same, sort by type + dup_keywords++; + if ( pe[i].type > pe[j].type ) + { + swap_needed = false; + } + } + + if ( true == swap_needed ) + { + type = pe[i].type; + keyword = pe[i].keyword; + idx = pe[i].idx; + + pe[i].type = pe[j].type; + pe[i].keyword = pe[j].keyword; + pe[i].idx = pe[j].idx; + + pe[j].type = type; + pe[j].keyword = keyword; + pe[j].idx = idx; + total_swaps++; + } + } + } + } + + // Set up for successful searching of parse elements + pe_mid_idx = pe_max_idx / 2; + pe_mid_keyword = pe[ pe_mid_idx ].keyword; + + if ( 0 != empty_string_count ) + retVal = (int) empty_string_count; + + if ( true == process_cyclomatic_complexity ) + { + // Keep keywords (or operators) unique in the parse element array by + // setting extra meaning bit flags (and sometimes indexes) as needed. + if ( aux_meaning_keys.size() ) + { + // Put in values to support Cyclomatic Complexity rings 1 to 4 + SetInPe( aux_meaning_keys, aux_meaning_vals, false ); + } + + if ( cmplx_cond_list.size() ) + { + UIntVector cmplx_cond_vars; + for ( unsigned int k = 0; k < cmplx_cond_list.size(); k++ ) + cmplx_cond_vars.push_back( COMPLEX_CALC ); + + SetInPe( cmplx_cond_list, cmplx_cond_vars, true ); + } + + if ( cmplx_preproc_list.size() ) + { + UIntVector cmplx_preproc_vars; + for ( unsigned int k = 0; k < cmplx_preproc_list.size(); k++ ) + cmplx_preproc_vars.push_back( COMPLEX_PREPROC ); + + SetInPe( cmplx_preproc_list, cmplx_preproc_vars, true ); + } + } + + return retVal; +} + + +/*! +* Set up extra meanings in data structures needed by ParseFile +* +*/ +void CCodeCounter::SetInPe( StringVector & keys, UIntVector & vals, const bool useExtraIdx ) +{ + unsigned int match_count = 0; + ParseType keyword_type = PARSE_UNKNOWN; + unsigned int idx = 0; + unsigned int keys_idx = 0; + + UIntVector::iterator itVal = vals.begin(); + for ( StringVector::iterator it = keys.begin(); it != keys.end(); it++, itVal++ ) + { + match_count = 0; + keyword_type = PARSE_UNKNOWN; + idx = 0; + int idx_found = FindInPe( *it, match_count, keyword_type, idx ); + if ( 0 > idx_found ) + { + // This is a UCC source file editing error. Every entry in keys should be found! + cout << endl << "Error: keyword or operator not found " << *it << endl; + keys_idx++; + continue; + } + + if ( 1 != match_count ) + { + // This is a UCC source file editing error. Match count should only be 1. + // Set breakpoint at start of this and see the 2 or more places where the keyword is declared. + // Resolve by using aux_meanings or extra_idx or similar. + cout << endl << "Error: Parse element; " << *it << " has more than 1 match. Wrong set up." << endl; + } + + // Assign special meanings as needed + pe[ idx_found ].aux |= *itVal; + + if ( useExtraIdx ) + { + // Set extra index to be index of the provided keys + // The bitmask ORed in above will indicate to use this extra index + pe[ idx_found ].extra_idx = keys_idx; + } + + keys_idx++; + } + + return; +} + + +/*! +* Set up data structures (already in this class) needed by ParseFile +* +* \param index, IN/OUT beginning position in the array to change +* \param max_str_size, IN/OUT size of longest string in pe array +* \param empty_string_count, IN/OUT number of empty strings found (should be Zero) +* \param type, IN enum of what is the type of these parse keywords +* \param sv, IN a string vector with 1 or more keywords that will be added +*/ +void CCodeCounter::AddToParse( unsigned int & index, + unsigned int & max_str_size, + unsigned int & empty_string_count, + const ParseType type, + StringVector sv ) +{ + unsigned int originating_index = 0; // index of the sv array + + for ( StringVector::iterator it = sv.begin(); it != sv.end(); it++, originating_index++ ) + { + if ( "" != *it ) + { + pe[ index ].keyword = *it; + pe[ index ].type = type; + pe[ index ].idx = originating_index; + pe[ index ].aux = 0; + pe[ index ].extra_idx = 0; + + if ( max_str_size < pe[ index ].keyword.size() ) + max_str_size = pe[ index ].keyword.size(); + + index++; + } + else + { + // Found an empty string where it should NOT be + // This is a coding table error. + cout << endl << "Error: Empty string given to AddToParse()" << endl; + empty_string_count++; + } + } +} + +/*! +* See if a string value is in an array of well known keywords +* +* Precondition: parse element array is already in sorted order +* +* \param value, IN is this value a well known keyword ? +* \param match_count, OUT number of same keywords found +* \param type_found, OUT type of parse keyword +* \param original_idx, OUT index of the name array holding the keyword +* \returns index value of first match or NO_MATCH (-1) +*/ +int CCodeCounter::FindInPe( const string value, unsigned int & match_count, + ParseType & type_found, unsigned int & original_idx ) +{ + int retVal = NO_MATCH; + match_count = 0; + + if ( pe_keyword_max_size < value.size() ) + return NO_MATCH; + + // This uses a simple Linear Search as the number of array elements is not large (< 150?) + // + // We can plug in a Binary Search later if we can see a difference in time + // + + // Limit search to half of the array + int cmp_result = pe[ pe_mid_idx ].keyword.compare( value ); + if ( 0 == cmp_result ) + retVal = (int) pe_mid_idx; // found + else + { + unsigned int start = 0; + unsigned int end = pe_mid_idx; + if ( -1 == cmp_result ) + { + start = pe_mid_idx; + end = pe_max_idx - 1; + } + + // Linear Search + // with check for finding a array keyword above value + for ( unsigned int j = start; j <= end; j++ ) + { + cmp_result = pe[ j ].keyword.compare( value ); + if ( cmp_result >= 0 ) + { + // Either went too far or found it + if ( 0 == cmp_result ) + retVal = (int)j; + break; + } + } + } + + if ( NO_MATCH != retVal ) + { + match_count = 1; + if ( true == pe_dup_keywords ) + { + // This is considered an ERROR as duplicate entries happened + // rather than using extra decision/attribute/meaning flags and offsets + match_count++; + } + type_found = pe[ retVal ].type; + original_idx = pe[ retVal ].idx; + } + + return retVal; +} + +/*! +* See if first part of a string value is in an array of well known keywords +* +* \param value, IN is the first part of this value a well known keyword ? +* \param match_count, OUT number of same keywords found +* \param type_found, OUT type of parse keyword +* \param original_idx, OUT index of the name array holding the keyword +* \returns index value of first match or NO_MATCH (-1) +*/ +int CCodeCounter::FindSpecialInPe( const string value, unsigned int & match_count, + ParseType & type_found, unsigned int & original_idx, unsigned int & size_found ) +{ + int retVal = NO_MATCH; + + // This is a helper to see if the first few characters of a given string + // match a well known keyword. + + // Start with longest value and decrease to 0 size + size_t size = value.size(); + if ( size > pe_op_max_size ) + size = pe_op_max_size; + do + { + string to_find = value.substr( 0, size ); + + int pe_idx = FindInPe( to_find, match_count, type_found, original_idx ); + + if ( match_count ) + { + size_found = size; + retVal = pe_idx; + break; + } + + size--; + } while ( size > 0 ); + + return retVal; +} + +/*! +* Find possible smaller strings in a string based on given separators. +* Similar to clib strtok. +* +* \param input, IN string to consider +* \param separators, IN array of separator characters between tokens +* \param tokens, IN/OUT list of token strings found +* +* \returns OUT number of tokens found, there are no error values +*/ +unsigned int CCodeCounter::StrTok( const string input, const char * separators, StringVector & tokens ) +{ + unsigned int num_found = 0; + + tokens.clear(); + if ( 0 == input.size() ) + { + // May be an error in the caller. + cout << endl << "StrTok called to process Empty string" << endl; + return 0; + } + + size_t idx_start, idx_end; + string frag; + string working = input; + + for ( ; ; ) + { + idx_start = working.find_first_not_of( separators ); + if ( idx_start == string::npos ) + break; + + if ( 0 < idx_start ) + { + working = working.substr( idx_start ); + idx_start = 0; + } + idx_end = working.find_first_of( separators ); + + frag = working.substr( idx_start, idx_end ); + tokens.push_back( frag ); + num_found++; + + if ( idx_end == string::npos ) + break; + + idx_start = idx_end; + working = working.substr( idx_start ); + } + return num_found; +} + +/*! +* Get separate tokens (some have syntax meanings) from a given source line. +* The syntax meanings are like the results of a first pass Lexical scan. +* +* \param input, IN string to consider +* \param unchanged, IN original string with Quoted string literals intact +* \param parse_elements, IN/OUT list of partially parsed token strings found +* +* \returns OUT number of partially parsed token found, there are no error values +*/ +unsigned int CCodeCounter::GetTokens( const string input, const string unchanged, ParseElementVector & parse_elements ) +{ + unsigned int token_count_total = 0; + unsigned int idx_end; + StringVector my_tokens; + string frag; + string quote_start; + string quote_end; + string temp; + parseElement temp_parse; + size_t idx_start; + char buf[128]; + + parse_elements.clear(); + +// Define fixed arrays of chars that have specific meanings +#define WHITESPACE " \t\n\r\f" + +// Calculation operations characters +//#define CALC_OPS "%^*+-/<>" + +// Logical operations characters +//#define LOGIC_OPS "&|=!<>" + +// These are in the Calculations list and the Logic list +//#define AMBIG_OPS "<>" + +// Any character NOT in the below means the token is NOT a single ID but may have other parts +#define ID_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZ_$abcdefghijklmnopqrstuvwxyz0123456789" + +// Characters that could represent numbers (Scala) +#define NUMERIC_CHARS "+-ABCDEFabcdefLlXx0123456789." + + // First get (possibly compound) tokens using whitespace as the separators. + // When vector string is returned there will be no embedded white space in a token. + unsigned int token_count = StrTok( input, WHITESPACE, my_tokens ); + if ( token_count != my_tokens.size() ) + { + #ifdef _DEBUG_1_PASS_PARSE + cout << endl << "ERROR: StrTok count mismatch. Using actual size." << endl; + #endif + token_count = my_tokens.size(); + } + + unsigned int parse_pos = 0; // Keep track of parse position to recover Literal strings + + for ( unsigned int j = 0; j < token_count; j++ ) + { + // Observation: there are no number digit values as tokens in the pe array. + // So if there are any embedded numeric digits then + // it may be an identifier like a variable, procedure, or class name, etc. + frag = my_tokens[ j ]; + do + { // Keep going until all of this fragment has been parsed + temp_parse.keyword = frag; + temp_parse.idx = 0; + temp_parse.type = PARSE_UNKNOWN; + + // See if this is a composite fragment with more than 1 kind of parse type + + // Look for characters not found in a Number + idx_start = frag.find_first_not_of( NUMERIC_CHARS ); + if ( 0 < idx_start ) + { + bool is_long = false; + size_t last_pos = idx_start - 1; + if ( idx_start == string::npos ) + last_pos = (size_t)(frag.size() - 1); + + // fragment may be a number + // Check if last character is L or l + if ( ( 'L' == frag[ last_pos ] ) + || ( 'l' == frag[ last_pos ] ) ) + is_long = true; + + // Check to see if this is a number + if ( last_pos + 1 < ( sizeof( buf )/sizeof( buf[0] ) ) ) + { + memset( buf, 0, sizeof( buf ) ); + frag.copy( buf, last_pos + 1 ); + char * p_afterwards; + errno = 0; + // Try converting string to double + strtod( buf, &p_afterwards ); + if ( 0 == errno ) + { + if ( p_afterwards != &( buf[0] ) ) + { + if ( true == is_long ) + temp_parse.type = NUMBER_LONG; + else + temp_parse.type = NUMBER_PART; + + temp_parse.keyword = frag.substr( 0, last_pos + 1 ); + parse_elements.push_back( temp_parse ); + parse_pos += temp_parse.keyword.size(); + frag = frag.substr( last_pos + 1 ); + token_count_total++; + continue; + } + } + } + } + + // Look for characters not found in an Identifier + idx_start = frag.find_first_not_of( ID_CHARS ); + if ( idx_start == string::npos ) + { + // Check for Scala Wildcard (match any found) _ Underline + if ( ( 1 == frag.size() ) + && ( "_" == frag ) ) + { + temp_parse.type = WILDCARD; + idx_start = 0; + } + else + { + // NO characters that did not belong in an ID (variable, procedure, class name) + // OR this may be a well known token. + // In either case, this token string is ready for higher level processing + if ( PARSE_UNKNOWN == temp_parse.type ) + temp_parse.type = IDENTIFIER; // May be found to be well know token later. + + parse_elements.push_back( temp_parse ); + parse_pos += temp_parse.keyword.size(); + frag.erase(); + token_count_total++; + continue; + } + } + + // Not a simple identifier so separate into parts. + // idx_start gives the position of the first non ID character + if ( 0 < idx_start ) + { + // The string up to idx_start may be a useable ID token. See comments above + // Put the leading part in the output + temp_parse.keyword = frag.substr( 0, idx_start ); + temp_parse.type = IDENTIFIER; // May be found to be well know token later. + parse_elements.push_back( temp_parse ); + parse_pos += temp_parse.keyword.size(); + token_count_total++; + + // Trim the saved leading part and fall through + frag = frag.substr( idx_start ); + idx_start = 0; + temp_parse.type = PARSE_UNKNOWN; + } + + // Check what the first character is. + // These are hard coded now but could be provided by language specific strings + temp = frag.substr( 0, 1 ); + bool keyword_set = false; + switch ( frag[0] ) + { + // check for Grouping symbols + case '{': + temp_parse.type = OPEN_CURLY_BRACE; + break; + case '}': + temp_parse.type = CLOSE_CURLY_BRACE; + break; + case '(': + temp_parse.type = OPEN_PAREN; + break; + case ')': + temp_parse.type = CLOSE_PAREN; + break; + case '[': + temp_parse.type = OPEN_SQUARE_BRACKET; + break; + case ']': + temp_parse.type = CLOSE_SQUARE_BRACKET; + break; + + // check for other symbols of interest + case '_': // Underline Wildcard (match any found) in Scala + // Do nothing here as would be handled above if applicable + break; + case ';': // Semicolon + temp_parse.type = LOGICAL_END; + break; + + case '.': // Period or dot + temp_parse.type = SCOPE_RESOLUTION; + break; + case ',': // Comma + temp_parse.type = DELIM_COMMA; + break; + case ':': // Colon + if ( 1 < frag.size() ) + { + if ( ':' == frag[1] ) + { + temp_parse.type = DOUBLE_COLON; + temp_parse.keyword = "::"; + keyword_set = true; + } + } + + if ( PARSE_UNKNOWN == temp_parse.type ) + temp_parse.type = TYPE_PREFIX_COLON; + break; + + // Double or Single Quote mark characters + case '"': + // TODO: Fix this to use flag set from previous line parsing + // about multiline string literals + if ( ( 0 < QuoteMultiStart.size() ) + && ( QuoteMultiStart.size() <= frag.size() ) ) + { + // See if multiline Start or End + quote_start = QuoteMultiStart; + quote_end = QuoteMultiEnd; + int cmp_result = quote_start.compare( frag.substr( 0, quote_start.size() ) ); + if ( 0 == cmp_result ) + { + temp_parse.type = STRING_MULTILINE_START; + if ( false == quote_multi_start_is_end ) + { + // Look for the end on this line + + } + } + else + { + cmp_result = quote_end.compare( frag.substr( 0, quote_end.size() ) ); + if ( 0 == cmp_result ) + { + temp_parse.type = STRING_MULTILINE_END; + temp = QuoteMultiEnd; + } + } + } + // fall through + case '\'': // Single Quote mark symbol + + // TODO: Improve this to also handle Python style of ''' for start/end + if ( PARSE_UNKNOWN == temp_parse.type ) + { + temp_parse.type = STRING_LITERAL; + quote_start = frag.substr( 0, 1 ); + quote_end = quote_start; + // Get the rest of the string literal + temp = frag.substr( 1 ); + } + + + idx_end = temp.find_first_of( quote_end ); + if ( string::npos == (size_t)idx_end ) + { + // Expected to find the following Quote mark + // So just output an Information message and use the rest of the string + cout << endl << "Information: Unable to find ending Quote: " + quote_end << endl; + temp_parse.keyword = frag; + frag.clear(); + } + else + { + // Get Literal string from given unchanged string + temp = unchanged.substr( parse_pos, parse_pos + idx_end + quote_end.size() ); + temp.resize( quote_start.size() + idx_end + quote_end.size() ); + temp_parse.keyword = temp; + idx_end += quote_end.size() + 1; + if ( idx_end >= frag.size() ) + frag.clear(); + else + frag = frag.substr( idx_end ); + } + + keyword_set = true; + break; + + default: + // See if the first few characters are well known + unsigned int match_count; + ParseType type_found; + unsigned int original_idx; + unsigned int size_found; + + // int pe_idx = FindSpecialInPe( frag, match_count, type_found, original_idx, size_found ); + FindSpecialInPe( frag, match_count, type_found, original_idx, size_found ); + + if ( 0 < match_count ) + { + temp_parse.type = type_found; + temp_parse.keyword = frag.substr( 0, size_found ); + temp_parse.idx = original_idx; + // Remove it from the start of the string + frag = frag.substr( size_found ); + idx_start = 0; + keyword_set = true; + } + else + { + // Show the first character of what was not done + temp = frag.substr( 0, 1 ); + cout << endl << "GetTokens did NOT do " + temp << endl; + + // Remove it from the start of the string + frag = frag.substr( 1 ); + idx_start = 0; + } + break; + } + + if ( PARSE_UNKNOWN != temp_parse.type ) + { + // Save it as a parse element + if ( false == keyword_set ) + { + temp_parse.keyword = frag.substr( 0, 1 ); + // Remove it from the start of the string + frag = frag.substr( 1 ); + idx_start = 0; + } + + parse_elements.push_back( temp_parse ); + parse_pos += temp_parse.keyword.size(); + token_count_total++; + } + + if ( 0 == frag.size() ) + continue; + + // Insert any other special token parsing logic here + + } while ( 0 < frag.size() ); + + if ( j < token_count - 1 ) + { + // Put in a blank space between non blank tokens as some + // languages (like Scala) use whitespace as delimiters. + // MAY interfere when parser gets more complete or for more languages + temp_parse.keyword = " "; + temp_parse.idx = 0; + temp_parse.type = DLIM_BLANK; + parse_elements.push_back( temp_parse ); + parse_pos += temp_parse.keyword.size(); + token_count_total++; + } + + } // END loop processing tokens extracted from whitespace + + return token_count_total; +} + + +// Increment related count for the keyword +// Returns 0 if count updated +int CCodeCounter::UpdateKeywordCount( const ParseType type_found, + const unsigned int pe_idx, + results * result, + unsigned int & direct_items, + unsigned int & data_items, + unsigned int & exec_items, + unsigned int & math_items, + unsigned int & trig_items, + unsigned int & logarithm_items, + unsigned int & cmplx_calc_items, + unsigned int & cmplx_cond_items, + unsigned int & cmplx_logic_items, + unsigned int & cmplx_preproc_items, + unsigned int & cmplx_assign_items, + unsigned int & cmplx_pointer_items ) +{ + int retVal = 0; + + unsigned int org_idx = pe[ pe_idx ].idx; + +#ifdef _MSC_VER + // VC++ using -Wall still get the below so explicitly set to show 1 time ONLY HERE and NOT for all UCC sources + // The switch statement has a "good enough" default handler. + // There are many other enums in place of 'ADDRESS_OF' for the warning messages. + #ifdef _DEBUG + //#pragma warning( once: 4061 ) // show 1 time only: enumerator 'ADDRESS_OF' in switch of enum 'CCodeCounter::ParseType' is not explicitly handled by a case label + #pragma warning( disable: 4061 ) + #else + // For Release build just totally suppress this Warning + #pragma warning( disable: 4061 ) + #endif +#endif + + switch ( type_found ) + { + // Comments were already handled ? + case BLK_COMMENT_START: + case BLK_COMMENT_END: + case LINE_COMMENT_START: + break; + case DIRECTIVE: + direct_items++; + result->directive_count[org_idx]++; + if ( 1 == direct_items ) + result->directive_lines[PHY]++; + result->directive_lines[LOG]++; + result->SLOC_lines[LOG]++; + if ( print_cmplx ) + { + unsigned int idx_type = UseExtraIdx( pe[ pe_idx ].aux); + unsigned int extra_idx = pe[ pe_idx ].extra_idx; + if ( COMPLEX_CALC == idx_type ) + { + cmplx_calc_items++; + result->cmplx_calc_count[ extra_idx ]++; + } + else if ( COMPLEX_PREPROC == idx_type ) + { + cmplx_preproc_items++; + result->cmplx_preproc_count[ extra_idx ]++; + } + else + { + if ( 0 != idx_type ) + printf( "\nError: UpdateTokenCount Unknown index type found %d\n", idx_type ); + break; + } + } + break; + case DATA_NAME: + data_items++; + result->data_name_count[org_idx]++; + if ( 1 == data_items ) + result->data_lines[PHY]++; + result->data_lines[LOG]++; + result->SLOC_lines[LOG]++; + if ( print_cmplx ) + { + unsigned int idx_type = UseExtraIdx( pe[ pe_idx ].aux); + unsigned int extra_idx = pe[ pe_idx ].extra_idx; + if ( COMPLEX_CALC == idx_type ) + { + cmplx_calc_items++; + result->cmplx_calc_count[ extra_idx ]++; + } + else if ( COMPLEX_PREPROC == idx_type ) + { + cmplx_preproc_items++; + result->cmplx_preproc_count[ extra_idx ]++; + } + else + { + if ( 0 != idx_type ) + printf( "\nError: UpdateTokenCount Unknown index type found %d\n", idx_type ); + break; + } + } + break; + case EXEC_NAME: + exec_items++; + result->exec_name_count[org_idx]++; + if ( 1 == exec_items ) + result->exec_lines[PHY]++; + result->exec_lines[LOG]++; + result->SLOC_lines[LOG]++; + if ( print_cmplx ) + { + unsigned int idx_type = UseExtraIdx( pe[ pe_idx ].aux ); + unsigned int extra_idx = pe[ pe_idx ].extra_idx; + if ( COMPLEX_CALC == idx_type ) + { + cmplx_calc_items++; + result->cmplx_calc_count[ extra_idx ]++; + } + else if ( COMPLEX_PREPROC == idx_type ) + { + cmplx_preproc_items++; + result->cmplx_preproc_count[ extra_idx ]++; + } + else + { + if ( 0 != idx_type ) + printf( "\nError: UpdateTokenCount Unknown index type found %d\n", idx_type ); + break; + } + } + break; + case MATH_FUNC: + math_items++; + result->math_func_count[org_idx]++; + break; + case TRIG_FUNC: + trig_items++; + result->trig_func_count[org_idx]++; + break; + case LOG_FUNC: + logarithm_items++; + result->log_func_count[org_idx]++; + break; + case CMPLX_CALC: + cmplx_calc_items++; + result->cmplx_calc_count[org_idx]++; + break; + case CMPLX_COND: + cmplx_cond_items++; + result->cmplx_cond_count[org_idx]++; + break; + case CMPLX_LOGIC: + cmplx_logic_items++; + result->cmplx_logic_count[org_idx]++; + break; + case CMPLX_PREPROC: + // from cmplx_preproc_list, Preprocessor directives (complexity) + cmplx_preproc_items++; + result->cmplx_preproc_count[org_idx]++; + break; + case CMPLX_ASSIGN: + // from cmplx_assign_list, Assignments (complexity) + cmplx_assign_items++; + result->cmplx_assign_count[org_idx]++; + break; + case CMPLX_POINTER: + // from cmplx_pointer_list, Pointers (complexity) + cmplx_pointer_items++; + result->cmplx_pointer_count[org_idx]++; + break; + case CMPLX_CYCLOMATIC: + // from cmplx_cyclomatic_list + // This is processed elsewhere + // result->cmplx_cycfunct_count[org_idx]++; + break; + case PARSE_UNKNOWN: + default: + retVal = -1; + + #ifdef _DEBUG + int unk_type = type_found; + printf( "\nERROR: UpdateTokenCount() Found unexpected type: %d\n", unk_type ); + #endif + break; + } + + return retVal; +} + + +// Here we know that this Line is not Blank and any Comments were handled +int CCodeCounter::ParseLine( const unsigned long cur_line, + filemap::iterator iter, + results * result, + filemap::iterator itfmBak ) +{ + int parse_error_count = 0; + + string to_parse = iter->line; + string unchanged = itfmBak->line; + string keyword; + string type_str; + + ParseElementVector parse_found; + + // First get (possibly compound) tokens using whitespace + unsigned int num_tokens = GetTokens( to_parse, unchanged, parse_found ); + unsigned int token_count = parse_found.size(); + if ( num_tokens != token_count ) // DEBUG helper + { + printf( "\nERROR: GetTokens count mismatch\n" ); + } + + unsigned int direct_items = 0; + unsigned int data_items = 0; + unsigned int exec_items = 0; + unsigned int math_items = 0; + unsigned int trig_items = 0; + unsigned int logarithm_items = 0; + unsigned int cmplx_calc_items = 0; + unsigned int cmplx_cond_items = 0; + unsigned int cmplx_logic_items = 0; + unsigned int cmplx_preproc_items = 0; + unsigned int cmplx_assign_items = 0; + unsigned int cmplx_pointer_items = 0; + + for ( unsigned int j = 0; j < token_count; j++ ) + { + // Observation: there are no number digit values as tokens in the pe array. + // So if there are any embedded numeric digits then + // it may be an identifier like a variable, procedure, or class name, etc. + + unsigned int match_count; + unsigned int org_idx; + ParseType type_found = parse_found[ j ].type; + + if ( ( PARSE_UNKNOWN == type_found ) + || ( IDENTIFIER == type_found ) ) + { + keyword = parse_found[ j ].keyword; + int pe_idx = FindInPe( keyword, match_count, type_found, org_idx ); + + if ( match_count ) + { + // Increment related count for the keyword + int update_OK = UpdateKeywordCount( type_found, (unsigned int)pe_idx, result, + direct_items, + data_items, + exec_items, + math_items, + trig_items, + logarithm_items, + cmplx_calc_items, + cmplx_cond_items, + cmplx_logic_items, + cmplx_preproc_items, + cmplx_assign_items, + cmplx_pointer_items ); + + if ( 0 != update_OK ) + { + // This is a failure in the parse code or the parse data structs + // TODO: show update_OK error value and pe_idx value + printf( "ERROR: ParseLine on line: %ld UpdateTokenCount() Finding: %s had an ERROR.\n", cur_line, keyword.c_str() ); + } + parse_found[ j ].idx = org_idx; + parse_found[ j ].type = type_found; + } + else + { + // cout << "Information: Not found: " + keyword << endl; + + // + + } + } + + } // END of getting token types resolved + +#ifdef _DEBUG_1_PASS_PARSE + // DEBUG output the tokens and types from this line + char buf[16]; + for ( int j = 0; j < token_count; j++ ) + { + memset( buf, 0, sizeof( buf ) ); + itoa( parse_found[ j ].type, buf, 10 ); + type_str = buf; + if ( PARSE_UNKNOWN == parse_found[ j ].type ) + cout << endl << "ERROR: " + parse_found[ j ].keyword + " is UNKNOWN type " << endl; + else + cout << parse_found[ j ].keyword + " " + type_str + " " ; + } + cout << endl; +#endif + + // Do other parse stuff here + + + + return parse_error_count; +} + +#endif // #ifdef ENABLE_1_PASS_PARSER diff --git a/src/CCodeCounter.h b/src/CCodeCounter.h new file mode 100644 index 0000000..49d82fd --- /dev/null +++ b/src/CCodeCounter.h @@ -0,0 +1,397 @@ +//! Common code counter class for sub-classing individual languages. +/*! +* \file CCodeCounter.h +* +* This file contains the common code counter class for sub-classing individual languages. +*/ + +#ifndef CCodeCounter_h +#define CCodeCounter_h + +#include "cc_main.h" +#include "CUtil.h" + +// Enable this new approach. Disabled for 2015.12 Release +//#define ENABLE_1_PASS_PARSER + +//! Common code counter class. +/*! +* \class CCodeCounter +* +* Defines the common code counter class. +*/ +class CCodeCounter +{ +public: + CCodeCounter(); + virtual ~CCodeCounter(); + virtual void InitializeCounts(); + virtual int CountSLOC(filemap* fmap, results* result); + bool IsSupportedFileExtension(const string &file_name); + virtual ofstream* GetOutputStream(const string &outputFileNamePrePend = "", + const string &cmd = "", bool csvOutput = false, bool legacyOutput = false); + virtual void CloseOutputStream(); + +//!< public Data declarations + StringVector directive; //!< Directive statement keywords + StringVector data_name_list; //!< Data statement keywords + StringVector exec_name_list; //!< Executable statement keywords + StringVector file_extension; //!< File extension + + StringVector math_func_list; //!< Math functions + StringVector trig_func_list; //!< Trigonometric functions + StringVector log_func_list; //!< Logarithmic functions + StringVector cmplx_calc_list; //!< Calculations (complexity) + StringVector cmplx_cond_list; //!< Conditionals (complexity) + StringVector cmplx_logic_list; //!< Logicals (complexity) + + //Modification: 2018.01 Integration starts + StringVector three_char_operator_list; //!< List of three character operators (for Halsteads volume calculations) + StringVector two_char_operator_list; //!< List of two character operators (for Halsteads volume calculations) + StringVector one_char_operator_list; //!< List of one character operators (for Halsteads volume calculations) + set keyword_operators; //!< List of reserved keyword operators (for Halsteads volume calculations) + //Modification: 2018.01 Integration ends + + // Auxillary meanings keywords/operators. PASCAL ex: IF/ELSE IF, REPEAT-UNTIL, WHILE, FOR, CASE OF, CASE, Database exception clause + StringVector aux_meaning_keys; //!< Cyclomatic Complexity rings keywords/Operators, OO keywords, FP keywords + UIntVector aux_meaning_vals; //!< Cyclomatic Complexity rings values, OO values, FP values + + StringVector cmplx_preproc_list; //!< Preprocessor directives (complexity) + StringVector cmplx_assign_list; //!< Assignments (complexity) + StringVector cmplx_pointer_list; //!< Pointers (complexity) + + StringVector cmplx_cyclomatic_list; //!< Cyclomatic complexity decision keywords (complexity) (C/C++: if, case, while, for, catch, ?) + UIntVector cmplx_cyclomatic_listVals; //!< Cyclomatic complexity decision keywords (complexity) CC ring values, OO vals, fp vals + + StringVector cmplx_cyclomatic_logic_list; //!< Cyclomatic complexity logical keywords (complexity) (C/C++: + StringVector cmplx_cyclomatic_case_list; //!< Cyclomatic complexity case keywords (complexity) + StringVector cmplx_cyclomatic_default_list; //!< Cyclomatic complexity case default keywords (complexity) + StringVector cmplx_cyclomatic_switch_list; //!< Cyclomatic complexity case switch keywords (complexity) + StringVector ignore_cmplx_cyclomatic_list; //!< Cyclomatic complexity decision keywords to ignore (for example End If) + StringVector skip_cmplx_cyclomatic_file_extension_list; //!< Cyclomatic complexity file extensions to skip + + UIntPairVector directive_count; //!< Count of each directive statement keyword + UIntPairVector data_name_count; //!< Count of each data statement keyword + UIntPairVector exec_name_count; //!< Count of each executable statement keyword + + UIntPairVector math_func_count; //!< Count of math functions + UIntPairVector trig_func_count; //!< Count of trigonometric functions + UIntPairVector log_func_count; //!< Count of logarithmic functions + UIntPairVector cmplx_calc_count; //!< Count of calculations + UIntPairVector cmplx_cond_count; //!< Count of conditionals + UIntPairVector cmplx_logic_count; //!< Count of logicals + UIntPairVector cmplx_preproc_count; //!< Count of preprocessor directives + UIntPairVector cmplx_assign_count; //!< Count of assignments + UIntPairVector cmplx_pointer_count; //!< Count of pointers + +#ifdef ENABLE_1_PASS_PARSER + bool use_1pass_parser; //!< use single pass file parse approach Modification: 2015.12 +#endif + + bool print_cmplx; //!< Print complexity and keyword counts + bool cc4enable; //!< Enable Cyclomatic Complexity 4 unique conditional clauses scanning Modification: 2015.12 + size_t lsloc_truncate; //!< # of characters allowed in LSLOC for differencing (0=no truncation) + string language_name; //!< Counter language name + string language_version; //!< Counter language version (do not include the word version) Modification: 2015.12 + //!< For C++ could be "2014", for Scala could be "2.11.5" + + string language_version_authority; //!< language version authority (either the Standards body or version creator) Modification: 2015.12 + //!< For C++ could be "ISO C++ Standards Committee", for Scala could be "scala.org" + + ClassType classtype; //!< Language class type + unsigned int counted_files; //!< Number of files counted + unsigned int counted_dupFiles; //!< Number of duplicate files counted + unsigned int total_filesA; //!< Total number of files in baseline A + unsigned int total_filesB; //!< Total number of duplicate files in baseline B + unsigned int total_dupFilesA; //!< Total number of files in baseline A + unsigned int total_dupFilesB; //!< Total number of duplicate files in baseline B + +// Support for lightweight Stack Dump Modified: 2015.12 +// + // These are set directly from caller before parsing starts Modified: 2015.12 + string parse_file_name; + unsigned int parse_threadIdx; + + // These are set by the Parse Exception Handler Modified: 2015.12 + string parse_physical_line; + string parse_physical_line_comments; + string parse_logical_line; + + // All these are set during Parsing + unsigned long currentPhyLine; //!< Which Physical line in source file is being parsed, starting with 1 + unsigned long currentLSrcLine; //!< Which Logical Source line in source file is being parsed, starting with 1 + // Level ABOVE current Step 1 is always CountSLOC + string currentStep1; //!< What procedure is called from CountSLOC + string currentStep2; //!< What procedure or other unique code place is called from level BELOW CountSLOC + string currentStep3; //!< What step is active from level BELOW the level BELOW CountSLOC + string currentStep4; //!< What step is active from level BELOW the level BELOW/BELOW CountSLOC (if used) + string currentFunc1; //!< What procedure name at level 1 (changes slowest) + string currentFunc2; //!< What procedure name at level 2 (changes faster) + string currentFunc3; //!< What procedure name at level 3 (changes fastest) + string currentFunc4; //!< What procedure name at level 3 (changes fastest, if used) + // Line numbers are only APPROXIMATE. Captured from most recent use of a MACRO as below. + long currentCode1; //!< What line of UCC code is running as called from CountSLOC + long currentCode2; //!< What line of UCC code is running as called from BELOW CountSLOC + long currentCode3; //!< What line of UCC code is running as called from BELOW/BELOW CountSLOC + long currentCode4; //!< What line of UCC code is running as called from BELOW/BELOW/BELOW CountSLOC (if used) + + // Define some helper MACRO to make saving code context easier. Support for later Stack Dump Modified: 2015.12 + #define ENTER_1(msg) { currentStep1 = msg; currentCode1 = __LINE__; currentFunc1 = UCC_FUNC_NAME; } + #define ENTER_2(msg) { currentStep2 = msg; currentCode2 = __LINE__; currentFunc2 = UCC_FUNC_NAME; } + #define ENTER_3(msg) { currentStep3 = msg; currentCode3 = __LINE__; currentFunc3 = UCC_FUNC_NAME; } + #define ENTER_4(msg) { currentStep4 = msg; currentCode4 = __LINE__; currentFunc4 = UCC_FUNC_NAME; } + #define SAVE_TO_1(msg) { currentStep1 = msg; currentCode1 = __LINE__; } + #define SAVE_TO_2(msg) { currentStep2 = msg; currentCode2 = __LINE__; } + #define SAVE_TO_3(msg) { currentStep3 = msg; currentCode3 = __LINE__; } + #define SAVE_TO_4(msg) { currentStep4 = msg; currentCode4 = __LINE__; } + /*Made these public to use them in MainObject.cpp //Ext Team. Modification: 10/16*/ + StringVector BlockCommentStart; //!< Block comment start character(s) (ex. /* in C++) + StringVector BlockCommentEnd; //!< Block comment end character(s) (ex. */ in C++) + StringVector LineCommentStart; //!< Single line or embedded comment character(s) + +protected: + virtual void InitializeResultsCounts(results* result); + static size_t FindQuote(string const &strline, string const &QuoteStart, size_t idx_start, char QuoteEscapeFront); + virtual int ReplaceQuote(string &strline, size_t &idx_start, bool &contd, char &CurrentQuoteEnd); + virtual int PreCountProcess(filemap* /*fmap*/) { return 0; } + int CountBlankSLOC(filemap* fmap, results* result); + virtual int CountCommentsSLOC(filemap* fmap, results* result, filemap* fmapBak = NULL); + virtual int FindHalsteadsVolume(filemap, results* ) { return -1; } //Modification: 2018.05. Integration + +#ifdef ENABLE_1_PASS_PARSER +//!< These support fewer passes file parsing +/*! + * \enum ParseType + * + * Enumeration of types of well known values that parsing of a language could find. + * Or types of parsing found for various reasons. + */ + enum ParseType { + PARSE_UNKNOWN = -1, // Unknown parse type, should NOT be in actual structs after parsing + BLK_COMMENT_START, // from BlockCommentStart, Block comment start character(s) (ex. /* in C++) + BLK_COMMENT_END, // from BlockCommentEnd, Block comment end character(s) (ex. */ in C++) + LINE_COMMENT_START, // from LineCommentStart, Single line or embedded comment character(s) + DIRECTIVE, // from directive, Directive (or preprocessor) statement keywords + DATA_NAME, // from data_name_list, Data statement keywords + EXEC_NAME, // from exec_name_list, Executable statement keywords + MATH_FUNC, // from math_func_list, Math functions (not Trig or Log) + TRIG_FUNC, // from trig_func_list, Trig functions + LOG_FUNC, // from log_func_list, Log functions + CMPLX_CALC, // from cmplx_calc_list, Calculations (complexity) + CMPLX_COND, // from cmplx_cond_list, Conditionals (complexity) + CMPLX_LOGIC, // from cmplx_logic_list, Logicals (complexity) + CMPLX_PREPROC, // from cmplx_preproc_list, Preprocessor directives (complexity) + CMPLX_ASSIGN, // from cmplx_assign_list, Assignments (complexity) + CMPLX_POINTER, // from cmplx_pointer_list, Pointers (complexity) + CMPLX_CYCLOMATIC, // from cmplx_cyclomatic_list, Cyclomatic complexity decision keywords (complexity) + + // These are found when doing first low level parsing + IDENTIFIER, // could be a valid identifier or resolve to a keyword type (above) in later parsing + NUMBER_PART, // could be part or entire number + NUMBER_LONG, // known to be a Long + NUMBER_INT, // known to be an Integer + NUMBER_SHORT, // known to be a Short + NUMBER_FLOAT, // known to be a Float + NUMBER_DOUBLE, // known to be a Double + STRING_LITERAL, // a string that begins and ends with valid Quote symbols within a line + STRING_MULTILINE_START, // begins with valid Quote multiline symbols, end may be on following line(s) + STRING_MULTILINE_END, // ends with valid Quote multiline symbols, start may be from previous line(s) + OPEN_CURLY_BRACE, // { + CLOSE_CURLY_BRACE, // } + OPEN_SQUARE_BRACKET, // [ + CLOSE_SQUARE_BRACKET, // ] + OPEN_PAREN, // ( + CLOSE_PAREN, // ) + LOGICAL_END, // Statement delimiter. C++ uses semicolon ; Scala semicolon is usually optional + WILDCARD, // Match any found; in import statements (or elsewhere _ for Scala) Java uses * + SCOPE_RESOLUTION, // Declare the scope on the left. C++ uses :: or . or -> (pointer later) Scala uses . + DELIM_COMMA, // Comma symbol + DLIM_BLANK, // Blank space delimiter (Scala uses intstead of Comma for example) + TYPE_PREFIX_COLON, // single Colon used to prefix a Type in Scala, maybe used for other purposes? + DOUBLE_COLON, // 2 Colon characters used to declare class members in C++ for instance + REFERENCE, // C++ uses type & Scala implies a reference? + ADDRESS_OF // C++ uses & expression Not used in Scala? + }; +#endif + + // Defines for bit masks for Auxillary meanings of Keywords (must be powers of 2) + // + // Flag for each Cyclomatic Complexity ring to allow parser to avoid some hard coded compares + #define CYCLOMATIC_CC1 1 + #define CYCLOMATIC_CC2 2 + #define CYCLOMATIC_CC3 4 + #define CYCLOMATIC_CC4 8 + #define CYCLOMATIC_CC_ALL (CYCLOMATIC_CC1|CYCLOMATIC_CC2|CYCLOMATIC_CC3|CYCLOMATIC_CC4) + #define CYCLOMATIC_EXCEPT_CC4 (CYCLOMATIC_CC1|CYCLOMATIC_CC2|CYCLOMATIC_CC3) + #define CYCLOMATIC_EXCEPT_CC3 (CYCLOMATIC_CC1|CYCLOMATIC_CC2|CYCLOMATIC_CC4) + // Use these to check in code if needed + #define IsCyclomaticAny(x) (CYCLOMATIC_CC_ALL&x) + #define IsCyclomatic_1(x) (CYCLOMATIC_CC1&x) + #define IsCyclomatic_2(x) (CYCLOMATIC_CC2&x) + #define IsCyclomatic_3(x) (CYCLOMATIC_CC3&x) + #define IsCyclomatic_4(x) (CYCLOMATIC_CC4&x) + +#ifdef ENABLE_1_PASS_PARSER + #define IsComplexCalc(x) (COMPLEX_CALC&x) + #define IsComplexPreProc(x) (COMPLEX_PREPROC&x) + + // TODO: implement OO and FP metrics + #define IS_OBJECT_ORIENTED 64 + + #define IS_FUNCTIONAL 128 + + #define USE_EXTRA_IDX (COMPLEX_CALC|COMPLEX_PREPROC) + #define UseExtraIdx(x) (USE_EXTRA_IDX&x) + + //! Structure to contain well known parse keywords/symbols and types of a language + /*! + * \struct parseElement + * + * Defines a structure to contain info to help parsing a given language + */ + struct parseElement + { + parseElement( const unsigned int original_idx, const unsigned int aux_meanings, const unsigned int ext_idx, + const ParseType parse_type, const string parse_keyword ) + { + idx = original_idx; + extra_idx = ext_idx; + aux = aux_meanings; + type = parse_type; + keyword = parse_keyword; + } + parseElement() + { + idx = 0; + extra_idx = 0; + aux = 0; + type = PARSE_UNKNOWN; + keyword = ""; + } + unsigned int idx; //!< Index of name array that held the keyword + unsigned int extra_idx; //!< Extra Index of to do Keywords with multiple meanings + unsigned int aux; //!< Auxillary meanings such as: is Cyclomatic + ParseType type; //!< Type of element + string keyword; //!< language keyword or special symbol(s) + }; + + //! Vector containing a list of parseElements. + /*! + * \typedef ParseElementVector + * + * Defines a vector containing a list of parseElements. + */ + typedef vector ParseElementVector; + + unsigned int parse_alloc_count; //!< number of elements in pe array + parseElement * pe; //!< pointer to array to search when parsing + bool pe_dup_keywords; //!< true if duplicate keywords of different types + unsigned int pe_keyword_max_size; //!< size of longest keyword string of all entries in pe + unsigned int pe_op_max_size; //!< size of longest operator string; Calc, Logic, Assign + unsigned int pe_max_idx; //!< index of end +1 of parse array + unsigned int pe_mid_idx; //!< index of mid point of parse array + string pe_mid_keyword; //!< keyword value at mid point + +#endif // #ifdef ENABLE_1_PASS_PARSER + + // Modification: 2015.12 + virtual int CountCommentsSLOCperLine( filemap::iterator iter, + results* result, + filemap::iterator itfmBak, + bool & contd, + int & comment_type, + bool & contd_nextline, + size_t & idx_start, + size_t & idx_end, + size_t & quote_idx_start, + size_t & comment_start, + string & curBlckCmtStart, + string & curBlckCmtEnd, + bool & quote_contd, + char & CurrentQuoteEnd ); + int FindCommentStart(string strline, size_t &idx_start, int &comment_type, + string &curBlckCmtStart, string &curBlckCmtEnd); + virtual int CountComplexity(filemap* fmap, results* result); + virtual bool CanProcessCyclomaticComplexity( const results* result ); // Modification: 2015.12 + virtual int CountDirectiveSLOC(filemap* /*fmap*/, results* /*result*/, filemap* /*fmapBak = NULL*/) { return 0; } + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak = NULL); + virtual int ParseFunctionName(const string & /*line*/, string & /*lastline*/, + filemap & /*functionStack*/, string & /*functionName*/, unsigned int & /*functionCount*/) { return 0; } + +#ifdef ENABLE_1_PASS_PARSER + //!< Set up some parse helpers + int PreParseFile( const results* result ); + void SetInPe( StringVector & keys, UIntVector & vals, const bool useExtraIdx ); + void AddToParse( unsigned int & index, + unsigned int & max_str_size, + unsigned int & empty_string_count, + const ParseType type, + StringVector sv ); + #define NO_MATCH (-1) + int FindInPe( const string value, unsigned int & match_count, ParseType & type_found, unsigned int & idx ); + int FindSpecialInPe( const string value, unsigned int & match_count, + ParseType & type_found, unsigned int & original_idx, unsigned int & size_found ); + //!< Do a reduced pass parse + #define NO_PARSE_DONE (-2) + int ParseFile( filemap* fmap, results* result, filemap* fmapBak ); + int ParseLine( const unsigned long cur_line, filemap::iterator iter, results* result, filemap::iterator itfmBak ); + unsigned int StrTok( const string input, const char * separators, StringVector & tokens ); + unsigned int GetTokens( const string input, const string unchanged, ParseElementVector & parse_elements ); + int UpdateKeywordCount( const ParseType type_found, const unsigned int org_idx, results* result, + unsigned int &direct_items, + unsigned int &data_items, + unsigned int &exec_items, + unsigned int &math_items, + unsigned int &trig_items, + unsigned int &logarithm_items, + unsigned int &cmplx_calc_items, + unsigned int &cmplx_cond_items, + unsigned int &cmplx_logic_items, + unsigned int &cmplx_preproc_items, + unsigned int &cmplx_assign_items, + unsigned int &cmplx_pointer_items ); +#endif // #ifdef ENABLE_1_PASS_PARSER + + StringVector exclude_keywords; //!< List of keywords to exclude from counts + + // if language supports multiple quote marks such as javascript, you can put all of them here, ex. "\"'" + string QuoteStart; //!< Starting quotation mark(s) + string QuoteEnd; //!< Ending quotation mark(s) + + bool quote_start_is_end; //!< true if the start and end single line Quote symbols are equal + string QuoteMultiStart; //!< Start of a multiline Quoted string literal + string QuoteMultiEnd; //!< End of a multiline Quoted string literal + bool quote_multi_start_is_end; //!< true if the start and end of multi line Quote symbols are equal + + char QuoteEscapeFront; //!< Escape character for front quote (ex. '\' in C++) + char QuoteEscapeRear; //!< Escape character for rear quote + string ContinueLine; //!< Line continuation character(s) (ex. \\ in C++) + + bool casesensitive; //!< Is language is case sensitive? + + ofstream output_file; //!< Output file stream + ofstream output_file_csv; //!< Output CSV file stream + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CCodeCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CCodeCounter(const CCodeCounter& rhs); + + // Take care of warning C4626: 'CCodeCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CCodeCounter operator=(const CCodeCounter); +}; + + +//! Map containing a logical Language enum and a pointer to Parser class for that language. +/*! +* \typedef CounterForEachLangType +* +* Defines a map containing a list of strings. +*/ +typedef map CounterForEachLangType; + +#endif diff --git a/src/CColdFusionCounter.cpp b/src/CColdFusionCounter.cpp new file mode 100644 index 0000000..de355c7 --- /dev/null +++ b/src/CColdFusionCounter.cpp @@ -0,0 +1,284 @@ +//! Code counter class methods for the ColdFusion language. +/*! +* \file CColdFusionCounter.cpp +* +* This file contains the code counter class methods for the ColdFusion language. +*/ + +#include "CColdFusionCounter.h" + +/*! +* Constructs a CColdFusionCounter object. +*/ +CColdFusionCounter::CColdFusionCounter() +{ + classtype = COLDFUSION; + language_name = "ColdFusion"; + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("ColdFusion", file_extension); + /* + file_extension.push_back(".*cfm");*/ + + BlockCommentStart.push_back(""); + + data_name_list.push_back("cfapplication"); + data_name_list.push_back("cfapplet"); + data_name_list.push_back("cfargument"); + data_name_list.push_back("cfcomponent"); + data_name_list.push_back("cffunction"); + data_name_list.push_back("cfimport"); + data_name_list.push_back("cfinclude"); + data_name_list.push_back("cfinterface"); + data_name_list.push_back("cfproperty"); + data_name_list.push_back("cfset"); + + exec_name_list.push_back("cfabort"); + exec_name_list.push_back("cfassociate"); + exec_name_list.push_back("cfbreak"); + exec_name_list.push_back("cfcache"); + exec_name_list.push_back("cfcase"); + exec_name_list.push_back("cfcatch"); + exec_name_list.push_back("cfcontent"); + exec_name_list.push_back("cfcontinue"); + exec_name_list.push_back("cfcookie"); + exec_name_list.push_back("cfdbinfo"); + exec_name_list.push_back("cfdefaultcase"); + exec_name_list.push_back("cfdirectory"); + exec_name_list.push_back("cfdiv"); + exec_name_list.push_back("cfdocument"); + exec_name_list.push_back("cfdump"); + exec_name_list.push_back("cfelse"); + exec_name_list.push_back("cfelseif"); + exec_name_list.push_back("cferror"); + exec_name_list.push_back("cfexchange"); + exec_name_list.push_back("cfexecute"); + exec_name_list.push_back("cfexit"); + exec_name_list.push_back("cffeed"); + exec_name_list.push_back("cffile"); + exec_name_list.push_back("cffinally"); + exec_name_list.push_back("cfflush"); + exec_name_list.push_back("cfform"); + exec_name_list.push_back("cfftp"); + exec_name_list.push_back("cfgrid"); + exec_name_list.push_back("cfheader"); + exec_name_list.push_back("cfhtmlhead"); + exec_name_list.push_back("cfhttp"); + exec_name_list.push_back("cfif"); + exec_name_list.push_back("cfimage"); + exec_name_list.push_back("cfindex"); + exec_name_list.push_back("cfinput"); + exec_name_list.push_back("cfinsert"); + exec_name_list.push_back("cfinvoke"); + exec_name_list.push_back("cflayout"); + exec_name_list.push_back("cfldap"); + exec_name_list.push_back("cflocation"); + exec_name_list.push_back("cflock"); + exec_name_list.push_back("cflog"); + exec_name_list.push_back("cflogin"); + exec_name_list.push_back("cflogout"); + exec_name_list.push_back("cfloop"); + exec_name_list.push_back("cfmail"); + exec_name_list.push_back("cfobject"); + exec_name_list.push_back("cfoutput"); + exec_name_list.push_back("cfparam"); + exec_name_list.push_back("cfpod"); + exec_name_list.push_back("cfpop"); + exec_name_list.push_back("cfpresentation"); + exec_name_list.push_back("cfprint"); + exec_name_list.push_back("cfprocessdirective"); + exec_name_list.push_back("cfprocparam"); + exec_name_list.push_back("cfprocresult"); + exec_name_list.push_back("cfquery"); + exec_name_list.push_back("cfregistry"); + exec_name_list.push_back("cfrethrow"); + exec_name_list.push_back("cfreturn"); + exec_name_list.push_back("cfsavecontent"); + exec_name_list.push_back("cfschedule"); + exec_name_list.push_back("cfscript"); + exec_name_list.push_back("cfsearch"); + exec_name_list.push_back("cfselect"); + exec_name_list.push_back("cfsetting"); + exec_name_list.push_back("cfsilent"); + exec_name_list.push_back("cfstoredproc"); + exec_name_list.push_back("cfswitch"); + exec_name_list.push_back("cfthread"); + exec_name_list.push_back("cfthrow"); + exec_name_list.push_back("cftimer"); + exec_name_list.push_back("cftrace"); + exec_name_list.push_back("cftransaction"); + exec_name_list.push_back("cftry"); + exec_name_list.push_back("cfupdate"); + + math_func_list.push_back("abs"); + math_func_list.push_back("arrayavg"); + math_func_list.push_back("arraysum"); + math_func_list.push_back("ceiling"); + math_func_list.push_back("decrementvalue"); + math_func_list.push_back("exp"); + math_func_list.push_back("fix"); + math_func_list.push_back("incrementvalue"); + math_func_list.push_back("int"); + math_func_list.push_back("max"); + math_func_list.push_back("min"); + math_func_list.push_back("mod"); + math_func_list.push_back("pi"); + math_func_list.push_back("precisionevaluate"); + math_func_list.push_back("rand"); + math_func_list.push_back("randomize"); + math_func_list.push_back("randrange"); + math_func_list.push_back("round"); + math_func_list.push_back("sgn"); + math_func_list.push_back("sqr"); + + trig_func_list.push_back("acos"); + trig_func_list.push_back("asin"); + trig_func_list.push_back("atn"); + trig_func_list.push_back("cos"); + trig_func_list.push_back("sin"); + trig_func_list.push_back("tan"); + + log_func_list.push_back("log"); + log_func_list.push_back("log10"); + + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("/"); + cmplx_calc_list.push_back("++"); + cmplx_calc_list.push_back("--"); + cmplx_calc_list.push_back("mod"); + cmplx_calc_list.push_back("%"); + cmplx_calc_list.push_back("^"); + + cmplx_cond_list.push_back("cfcase"); + cmplx_cond_list.push_back("cfelse"); + cmplx_cond_list.push_back("cfelseif"); + cmplx_cond_list.push_back("cfif"); + cmplx_cond_list.push_back("cfloop"); + + cmplx_logic_list.push_back("eq"); + cmplx_logic_list.push_back("neq"); + cmplx_logic_list.push_back("gt"); + cmplx_logic_list.push_back("gte"); + cmplx_logic_list.push_back("lt"); + cmplx_logic_list.push_back("lte"); + cmplx_logic_list.push_back("&&"); + cmplx_logic_list.push_back("||"); + cmplx_logic_list.push_back("not"); + cmplx_logic_list.push_back("and"); + cmplx_logic_list.push_back("or"); + cmplx_logic_list.push_back("xor"); + cmplx_logic_list.push_back("equiv"); + cmplx_logic_list.push_back("imp"); + cmplx_logic_list.push_back("is"); + + cmplx_assign_list.push_back("="); + + cmplx_cyclomatic_list.push_back("cfcatch"); + cmplx_cyclomatic_list.push_back("cfif"); + ignore_cmplx_cyclomatic_list.push_back("/cfif"); + cmplx_cyclomatic_list.push_back("cfelseif"); + cmplx_cyclomatic_list.push_back("/cfcase"); + //ignore_cmplx_cyclomatic_list.push_back("/cfcase"); + cmplx_cyclomatic_list.push_back("cfloop"); + ignore_cmplx_cyclomatic_list.push_back("/cfloop"); + + cmplx_cyclomatic_logic_list.push_back("&&"); + cmplx_cyclomatic_logic_list.push_back("||"); + cmplx_cyclomatic_logic_list.push_back("and"); + cmplx_cyclomatic_logic_list.push_back("or"); + //cmplx_cyclomatic_logic_list.push_back("xor"); + + cmplx_cyclomatic_case_list.push_back("/cfcase"); + cmplx_cyclomatic_switch_list.push_back("/cfswitch"); +} + +/*! +* Perform preprocessing of file lines before counting. +* Replace quote stuffing in literals '' or "" to avoid quote matching problems. +* +* \param fmap list of file lines +* +* \return method status +*/ +int CColdFusionCounter::PreCountProcess(filemap* fmap) +{ + size_t i; + filemap::iterator fit; + for (fit = fmap->begin(); fit != fmap->end(); fit++) + { + if (fit->line.empty()) + continue; + //int idx = fit->line.find("<"); + size_t idx = fit->line.find("<"); // warning fix + if (idx != string::npos) + { + for (i = 0; i < idx; i++) + { + fit->line[i] = '$'; + } + } + else { + for (i = 0; i < fit->line.length(); i++) + { + fit->line[i] = '$'; + } + } + } + return 0; +} + +/*! + * Parses lines for function/method names. + * + * \param line line to be processed + * \param lastline last line processed + * \param functionStack stack of functions + * \param functionName function name found + * + * \return 1 if function name is found + */ +int CColdFusionCounter::ParseFunctionName(const string &line, string &/*lastline*/, + filemap &functionStack, string &functionName, unsigned int &functionCount) +{ + + size_t idx; + + idx = line.find("cffunction"); + + if (idx != string::npos) + { + idx=line.find("name"); + if (idx != string::npos) + { + string str; + str = line.substr(idx); + idx = str.find("\""); + str = str.substr(idx + 1); + idx = str.find("\""); + str = str.substr(0, idx); + + lineElement element(++functionCount, str); + functionStack.push_back(element); + + } + } + + + if (functionStack.empty()) + { + // dealing with some code out of any subroutines, it a "main" code + return 2; + } + idx=line.find("/cffunction"); + if (idx != string::npos) + { + functionName = functionStack.back().line; + functionCount = functionStack.back().lineNumber; + functionStack.pop_back(); + return 1; + } + return 0; +} + diff --git a/src/CColdFusionCounter.h b/src/CColdFusionCounter.h new file mode 100644 index 0000000..f53744a --- /dev/null +++ b/src/CColdFusionCounter.h @@ -0,0 +1,41 @@ +//! Code counter class definition for the ColdFusion language. +/*! +* \file CColdFusionCounter.h +* +* This file contains the code counter class definition for the ColdFusion language. +*/ + +#ifndef CColdFusionCounter_h +#define CColdFusionCounter_h + +#include "CTagCounter.h" + +//! ColdFusion code counter class. +/*! +* \class CColdFusionCounter +* +* Defines the ColdFusion code counter class. +*/ +class CColdFusionCounter : public CTagCounter +{ +public: + CColdFusionCounter(); +protected: + virtual int PreCountProcess(filemap* fmap); + int ParseFunctionName(const string &line, string &lastline, filemap &functionStack, string &functionName, + unsigned int &functionCount); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CColdFusionCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CColdFusionCounter(const CColdFusionCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CColdFusionCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CColdFusionCounter operator=(const CColdFusionCounter); // Declare without implementation +}; + +#endif diff --git a/src/CCshCounter.cpp b/src/CCshCounter.cpp new file mode 100644 index 0000000..ab081f7 --- /dev/null +++ b/src/CCshCounter.cpp @@ -0,0 +1,788 @@ +//! Code counter class methods for the C shell script language. +/*! +* \file CCshCounter.cpp +* +* This file contains the code counter class methods for the C shell script language. +* This also includes the Tcsh language.*/ + +#include "CCshCounter.h" + +/*! +* Constructs a CCshCounter object. +*/ +CCshCounter::CCshCounter() +{ + classtype = CSH; + language_name = "C-Shell"; + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("C-Shell", file_extension); + /* + file_extension.push_back(".csh"); + file_extension.push_back(".tcsh")*/ + + QuoteStart = "\"'"; + QuoteEnd = "\"'"; + QuoteEscapeFront = '\\'; + ContinueLine = "\\"; + LineCommentStart.push_back("#"); + + exclude_keywords.push_back("end"); + exclude_keywords.push_back("endif"); + exclude_keywords.push_back("endsw"); + + continue_keywords.push_back("case"); + continue_keywords.push_back("default"); + continue_keywords.push_back("else"); + + exec_name_list.push_back("alias"); + exec_name_list.push_back("break"); + exec_name_list.push_back("breaksw"); + exec_name_list.push_back("builtins"); + exec_name_list.push_back("case"); + exec_name_list.push_back("cd"); + exec_name_list.push_back("chdir"); + exec_name_list.push_back("continue"); + exec_name_list.push_back("dirs"); + exec_name_list.push_back("echo"); + exec_name_list.push_back("eval"); + exec_name_list.push_back("exec"); + exec_name_list.push_back("exit"); + exec_name_list.push_back("foreach"); + exec_name_list.push_back("glob"); + exec_name_list.push_back("goto"); + exec_name_list.push_back("if"); + exec_name_list.push_back("onintr"); + exec_name_list.push_back("popd"); + exec_name_list.push_back("pushd"); + exec_name_list.push_back("rehash"); + exec_name_list.push_back("repeat"); + exec_name_list.push_back("set"); + exec_name_list.push_back("setenv"); + exec_name_list.push_back("shift"); + exec_name_list.push_back("source"); + exec_name_list.push_back("switch"); + exec_name_list.push_back("time"); + exec_name_list.push_back("umask"); + exec_name_list.push_back("unalias"); + exec_name_list.push_back("unhash"); + exec_name_list.push_back("unset"); + exec_name_list.push_back("unsetenv"); + exec_name_list.push_back("while"); + + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("/"); + cmplx_calc_list.push_back("%"); + cmplx_calc_list.push_back("++"); + cmplx_calc_list.push_back("--"); + + cmplx_cond_list.push_back("case"); + cmplx_cond_list.push_back("else"); + cmplx_cond_list.push_back("foreach"); + cmplx_cond_list.push_back("if"); + cmplx_cond_list.push_back("switch"); + cmplx_cond_list.push_back("while"); + + cmplx_logic_list.push_back("&&"); + cmplx_logic_list.push_back("||"); + cmplx_logic_list.push_back("=="); + cmplx_logic_list.push_back("!="); + cmplx_logic_list.push_back("=~"); + cmplx_logic_list.push_back("!~"); + cmplx_logic_list.push_back(">"); + cmplx_logic_list.push_back("<"); + cmplx_logic_list.push_back(">="); + cmplx_logic_list.push_back("=<"); + + cmplx_assign_list.push_back("="); + + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("for"); + cmplx_cyclomatic_list.push_back("case"); + cmplx_cyclomatic_list.push_back("elif"); + cmplx_cyclomatic_list.push_back("while"); + //cmplx_cyclomatic_list.push_back("switch"); + cmplx_cyclomatic_list.push_back("foreach"); + cmplx_cyclomatic_list.push_back("?"); + + cmplx_cyclomatic_logic_list.push_back("||"); + cmplx_cyclomatic_logic_list.push_back("&&"); + + cmplx_cyclomatic_case_list.push_back("case"); + cmplx_cyclomatic_switch_list.push_back("switch"); +} + +/*! +* Perform preprocessing of file lines before counting. +* +* \param fmap list of file lines +* +* \return method status +*/ +int CCshCounter::PreCountProcess(filemap* fmap) +{ + filemap::iterator fit; + for (fit = fmap->begin(); fit != fmap->end(); fit++) + { + if (fit->line.empty()) + continue; + for (size_t i = fit->line.length() - 1; i > 0; i--) + { + // replace $# and ${# with $ to avoid determination of a comment + if (fit->line[i] == '#' && (fit->line[i-1] == '$' || fit->line[i-1] == '{')) + fit->line[i] = '$'; + } + } + return 0; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CCshCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + filemap::iterator fit, fitbak; + string line, lineBak; + + bool data_continue = false; + string strLSLOC = ""; + string strLSLOCBak = ""; + string str; + unsigned int phys_exec_lines = 0; + unsigned int phys_data_lines = 0; + unsigned int temp_lines = 0; + unsigned int cnt = 0; + unsigned int loopLevel = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + line = fit->line; + lineBak = fitbak->line; + + // do not process blank lines (blank_line/comment_line/directive) + if (!CUtil::CheckBlank(line)) + { + // process logical SLOC + LSLOC(result, line, fit->lineNumber, lineBak, strLSLOC, strLSLOCBak, data_continue, + temp_lines, phys_exec_lines, phys_data_lines, loopLevel); + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count); + } + + // update physical SLOC lines + result->exec_lines[PHY] += phys_exec_lines; + phys_exec_lines = 0; + + result->data_lines[PHY] += phys_data_lines; + phys_data_lines = 0; + } + } + return 1; +} + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results +* \param line processed physical line of code +* \param lineBak original physical line of code +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param data_continue continuation of a data declaration line +* \param temp_lines tracks physical line count +* \param phys_exec_lines number of physical executable lines +* \param phys_data_lines number of physical data lines +* \param loopLevel nested loop level +*/ +void CCshCounter::LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + bool &data_continue, unsigned int &temp_lines, unsigned int &phys_exec_lines, + unsigned int &phys_data_lines, unsigned int &loopLevel) +{ + size_t start, end; + size_t i, j, m, strSize; + bool trunc_flag = false, found; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string str, spc; + unsigned int cnt = 0; + + string tmp = CUtil::TrimString(line); + string tmpBak = CUtil::TrimString(lineBak); + start = 0; + + // there may be more than 1 logical SLOC in this line + while (start < tmp.length()) + { + // check for semicolon to denote end of SLOC + end = tmp.find(";", start); + if (end != string::npos) + { + // handle empty statement + if (CUtil::TrimString(tmp.substr(start, end - start + 1)) == ";") + { + start = end + 1; + strLSLOC = strLSLOCBak = ""; + temp_lines = 0; + if (tmp == ";") + phys_exec_lines++; + continue; + } + } + else + end = tmp.length() - 1; + + // check for label + if (tmp[end] == ':' && tmp.substr(start, end - start + 1) != "default:") + { + i = tmp.find_first_of("\t ", start); + if (i == string::npos || i > end) + { + // skip label + start = end + 1; + if (start >= tmp.length()) + { + if (temp_lines == 0 && phys_data_lines == 0 && phys_exec_lines == 0) + phys_exec_lines = 1; + continue; + } + } + } + + // process nested loops + if (print_cmplx) + { + str = CUtil::TrimString(tmp.substr(start, end - start + 1)); + if (CUtil::FindKeyword(str, "foreach") != string::npos + || CUtil::FindKeyword(str, "while") != string::npos) + { + loopLevel++; + + // record nested loop level + if ((unsigned int)result->cmplx_nestloop_count.size() < loopLevel) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopLevel-1]++; + } + else if (CUtil::FindKeyword(str, "end") != string::npos && loopLevel > 0) + loopLevel--; + } + + // check for line containing excluded keywords + for (StringVector::iterator it = exclude_keywords.begin(); it != exclude_keywords.end(); it++) + { + i = CUtil::FindKeyword(tmp, (*it), start, end); + if (i != string::npos) + { + // strip specified keyword and skip if empty + start = i + (*it).length(); + if (CUtil::CheckBlank(CUtil::TrimString(tmp.substr(start, end - start)))) + start = end + 1; + break; + } + } + if (start > end) + { + if (temp_lines == 0 && phys_data_lines == 0 && phys_exec_lines == 0) + phys_exec_lines = 1; + continue; + } + + // check for continuation words + found = false; + if (tmp[end] == ';') + str = CUtil::TrimString(tmp.substr(start, end - start)); + else + str = CUtil::TrimString(tmp.substr(start, end - start + 1)); + for (StringVector::iterator it = continue_keywords.begin(); it != continue_keywords.end(); it++) + { + i = str.find((*it)); + if (i == 0) + { + // process else if + if ((*it) != "else" || str.length() < 7 || str.substr(0, 7) != "else if") + { + found = true; + strLSLOC += str + " "; + if (tmp[end] == ';') + str = CUtil::TrimString(tmpBak.substr(start, end - start)); + else + str = CUtil::TrimString(tmpBak.substr(start, end - start + 1)); + strLSLOCBak += str + " "; + start = end + 1; + if (temp_lines == 0 && phys_data_lines == 0 && phys_exec_lines == 0) + phys_exec_lines = 1; + temp_lines = 0; + } + } + } + if (found) + continue; + + // check for inline if + if (CUtil::FindKeyword(tmp, "if", start, end) == start) + { + i = CUtil::FindKeyword(tmp, "then", start, end); + if (i == string::npos) + { + // get end of if SLOC + found = false; + cnt = 0; + for (j = start + 2; j <= end; j++) + { + if (tmp[j] == '(') + { + found = true; + cnt++; + } + else if (tmp[j] == ')') + cnt--; + if (found && cnt < 1) + break; + } + + // save LSLOC for if statement, then process in-line action + strSize = CUtil::TruncateLine(j - start + 1, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(start, strSize); + strLSLOCBak += tmpBak.substr(start, strSize); + } + start = j + 1; + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + strLSLOC = strLSLOCBak = ""; + phys_exec_lines++; + temp_lines = 0; + continue; + } + } + + // check for line continuation + if (tmp[end] == '\\') + { + // strip off trailing (\) + strSize = CUtil::TruncateLine(end - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + spc = ""; + str = tmp.substr(start, strSize); + for (m = str.length() - 1; m > 0; m--) + { + if (str[m] == ' ') + spc += " "; + else + break; + } + if (m == 0) + { + if (str[0] == ' ') + spc += " "; + } + strLSLOC += CUtil::TrimString(tmp.substr(start, strSize)) + spc; + strLSLOCBak += CUtil::TrimString(tmpBak.substr(start, strSize)) + spc; + } + start = end + 1; + + // make sure that we are not beginning to process a new data line + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", NULL); + + if (cnt > 0) + data_continue = true; + if (data_continue == true) + temp_lines++; + if (temp_lines == 0 && phys_data_lines == 0 && phys_exec_lines == 0) + phys_exec_lines = 1; + } + else + { + // save LSLOC + if (tmp[end] == ';') + strSize = CUtil::TruncateLine(end - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + else + strSize = CUtil::TruncateLine(end - start + 1, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += CUtil::TrimString(tmp.substr(start, strSize)); + strLSLOCBak += CUtil::TrimString(tmpBak.substr(start, strSize)); + } + start = end + 1; + if (strLSLOCBak.length() > 0) + { + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + { + // add a logical SLOC + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", &result->data_name_count); + + temp_lines++; + if (data_continue == true || cnt > 0) + { + result->data_lines[LOG]++; + phys_data_lines = temp_lines; + } + else + { + result->exec_lines[LOG]++; + phys_exec_lines = temp_lines; + } + } + else if (data_continue == true) + phys_data_lines = temp_lines; + else + phys_exec_lines = temp_lines; + } + data_continue = false; + temp_lines = 0; + strLSLOC = strLSLOCBak = ""; + } + } +} + +/*! + * Counts file language complexity based on specified language keywords/characters. + * + * \param fmap list of processed file lines + * \param result counter results + * + * \return method status + */ +int CCshCounter::CountComplexity(filemap* fmap, results* result) +{ + if (classtype == UNKNOWN || classtype == DATAFILE) + return 0; + filemap::iterator fit; + size_t idx; + unsigned int cnt, ret, cyclomatic_cnt = 0, ignore_cyclomatic_cnt = 0, main_cyclomatic_cnt = 0, function_count = 0, cyclomatic_logic_cnt = 0, main_cyclomatic_logic_cnt = 1, cyclomatic_case_cnt = 0, main_cyclomatic_case_cnt = 1, cyclomatic_default_cnt = 0, cyclomatic_switch_cnt = 0; + + string line, lastline, file_ext, function_name = ""; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + filemap function_stack; + stack cyclomatic_stack; + stack cyclomatic_logic_stack; + stack cyclomatic_case_stack; + map function_map; + map logical_map; + map case_map; + bool process_cyclomatic_complexity = false; + + // check whether to process cyclomatic complexity + if (cmplx_cyclomatic_list.size() > 0) + { + process_cyclomatic_complexity = true; + if (skip_cmplx_cyclomatic_file_extension_list.size() > 0) + { + idx = result->file_name.find_last_of("."); + if (idx != string::npos) + { + file_ext = result->file_name.substr(idx); + file_ext = CUtil::ToLower(file_ext); + if (find(skip_cmplx_cyclomatic_file_extension_list.begin(), skip_cmplx_cyclomatic_file_extension_list.end(), file_ext) != skip_cmplx_cyclomatic_file_extension_list.end()) + process_cyclomatic_complexity = false; + } + } + } + + // process each line + for (fit = fmap->begin(); fit != fmap->end(); fit++) + { + line = fit->line; + + if (CUtil::CheckBlank(line)) + continue; + line = echoHelper(line); + + + line = " " + line; + + // mathematical functions + cnt = 0; + CUtil::CountTally(line, math_func_list, cnt, 1, exclude, "", "", &result->math_func_count, casesensitive); + result->cmplx_math_lines += cnt; + + // trigonometric functions + cnt = 0; + CUtil::CountTally(line, trig_func_list, cnt, 1, exclude, "", "", &result->trig_func_count, casesensitive); + result->cmplx_trig_lines += cnt; + + // logarithmic functions + cnt = 0; + CUtil::CountTally(line, log_func_list, cnt, 1, exclude, "", "", &result->log_func_count, casesensitive); + result->cmplx_logarithm_lines += cnt; + + // calculations + cnt = 0; + CUtil::CountTally(line, cmplx_calc_list, cnt, 1, exclude, "", "", &result->cmplx_calc_count, casesensitive); + result->cmplx_calc_lines += cnt; + + // conditionals + cnt = 0; + CUtil::CountTally(line, cmplx_cond_list, cnt, 1, exclude, "", "", &result->cmplx_cond_count, casesensitive); + result->cmplx_cond_lines += cnt; + + // logical operators + cnt = 0; + CUtil::CountTally(line, cmplx_logic_list, cnt, 1, exclude, "", "", &result->cmplx_logic_count, casesensitive); + result->cmplx_logic_lines += cnt; + + // preprocessor directives + cnt = 0; + CUtil::CountTally(line, cmplx_preproc_list, cnt, 1, exclude, "", "", &result->cmplx_preproc_count, casesensitive); + result->cmplx_preproc_lines += cnt; + + // assignments + cnt = 0; + CUtil::CountTally(line, cmplx_assign_list, cnt, 1, exclude, "", "", &result->cmplx_assign_count, casesensitive); + result->cmplx_assign_lines += cnt; + + /* No pointers for shell scripts + // pointers + cnt = 0; + // Pointers are embedded syntax so there is NO exclude string or include strings + CUtil::CountTally(line, cmplx_pointer_list, cnt, 1, "", "", "", &result->cmplx_pointer_count, casesensitive); + result->cmplx_pointer_lines += cnt; + */ + + // cyclomatic complexity + if (process_cyclomatic_complexity) + { + // search for cyclomatic complexity keywords + CUtil::CountTally(line, cmplx_cyclomatic_list, cyclomatic_cnt, 1, exclude, "", "", 0, casesensitive); + + CUtil::CountTally(line, cmplx_cyclomatic_switch_list, cyclomatic_switch_cnt, 1, exclude, "", "", 0, casesensitive); + + // search for keywords to exclude + if (ignore_cmplx_cyclomatic_list.size() > 0) + CUtil::CountTally(line, ignore_cmplx_cyclomatic_list, ignore_cyclomatic_cnt, 1, exclude, "", "", 0, casesensitive); + + // search for cyclomatic complexity logical keywords + if (cmplx_cyclomatic_logic_list.size() > 0) + CUtil::CountTally(line, cmplx_cyclomatic_logic_list, cyclomatic_logic_cnt, 1, exclude, "", "", 0, casesensitive); + + // search for cyclomatic complexity logical keywords + if (cmplx_cyclomatic_case_list.size() > 0) + CUtil::CountTally(line, cmplx_cyclomatic_case_list, cyclomatic_case_cnt, 1, exclude, "", "", 0, casesensitive); + + // search for cyclomatic complexity case default keywords + if (cmplx_cyclomatic_default_list.size() > 0) + CUtil::CountTally(line, cmplx_cyclomatic_default_list, cyclomatic_default_cnt, 1, exclude, "", "", 0, casesensitive); + + if(cyclomatic_default_cnt > 0) + { + cyclomatic_cnt -= cyclomatic_default_cnt; + cyclomatic_case_cnt -= cyclomatic_default_cnt; + cyclomatic_default_cnt = 0; + } + + // parse function name if found + ret = (unsigned)ParseFunctionName(line, lastline, function_stack, function_name); + if (ret != 1 && !cyclomatic_stack.empty() && cyclomatic_stack.size() == function_stack.size()) + { + // remove count stack entry for non-function names + cyclomatic_cnt += cyclomatic_stack.top(); + ignore_cyclomatic_cnt = 0; + cyclomatic_stack.pop(); + } + if (ret != 1 && !cyclomatic_logic_stack.empty() && cyclomatic_logic_stack.size() == function_stack.size()) + { + // remove count stack entry for non-function names + cyclomatic_logic_cnt += cyclomatic_logic_stack.top(); + cyclomatic_logic_stack.pop(); + } + if (ret != 1 && !cyclomatic_case_stack.empty() && cyclomatic_case_stack.size() == function_stack.size()) + { + // remove count stack entry for non-function names + cyclomatic_case_cnt += cyclomatic_case_stack.top(); + cyclomatic_case_stack.pop(); + } + if (ret == 1) + { + // capture count at end of function + lineElement element(cyclomatic_cnt - ignore_cyclomatic_cnt + 1, function_name); + function_map[function_count] = element; + + lineElement n_element(cyclomatic_cnt - ignore_cyclomatic_cnt + cyclomatic_logic_cnt + 1, function_name); + logical_map[function_count] = n_element; + + if (cyclomatic_case_cnt > 0) + { + lineElement c_element(cyclomatic_cnt - ignore_cyclomatic_cnt - cyclomatic_case_cnt + cyclomatic_switch_cnt + 1, function_name); + case_map[function_count] = c_element; + } + else + case_map[function_count] = element; + + if (!function_stack.empty()) + { + // grab previous function from stack to continue + if (!cyclomatic_stack.empty()) + { + cyclomatic_cnt = cyclomatic_stack.top(); + cyclomatic_stack.pop(); + } + if (!cyclomatic_logic_stack.empty()) + { + cyclomatic_logic_cnt = cyclomatic_logic_stack.top(); + cyclomatic_logic_stack.pop(); + } + if (!cyclomatic_case_stack.empty()) + { + cyclomatic_case_cnt = cyclomatic_case_stack.top(); + cyclomatic_case_stack.pop(); + } + } + else { + cyclomatic_cnt = 0; + cyclomatic_logic_cnt = 0; + cyclomatic_case_cnt = 0; + } + function_name = ""; + ignore_cyclomatic_cnt = 0; + cyclomatic_switch_cnt = 0; + } + else if (ret == 2) + { + if (main_cyclomatic_cnt < 1) + main_cyclomatic_cnt = 1; // add 1 for main function here in case no other decision points are found in main + // some code doesn't belong to any function + main_cyclomatic_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt; + main_cyclomatic_logic_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt + cyclomatic_logic_cnt; + main_cyclomatic_case_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt - cyclomatic_case_cnt + cyclomatic_switch_cnt; + cyclomatic_cnt = ignore_cyclomatic_cnt = cyclomatic_logic_cnt = cyclomatic_case_cnt = cyclomatic_switch_cnt = 0; + } + else { + if (!function_stack.empty() && (function_stack.size() > cyclomatic_stack.size() + 1 || (cyclomatic_stack.empty() && function_stack.size() > 1))) + { + // capture previous complexity count from open function + cyclomatic_stack.push(cyclomatic_cnt - ignore_cyclomatic_cnt); + cyclomatic_cnt = ignore_cyclomatic_cnt = 0; + } + if (!function_stack.empty() && (function_stack.size() > cyclomatic_logic_stack.size() + 1 || (cyclomatic_logic_stack.empty() && function_stack.size() > 1))) + { + // capture previous complexity count from open function + cyclomatic_logic_stack.push(cyclomatic_logic_cnt); + cyclomatic_logic_cnt = 0; + } + if (!function_stack.empty() && (function_stack.size() > cyclomatic_case_stack.size() + 1 || (cyclomatic_case_stack.empty() && function_stack.size() > 1))) + { + // capture previous complexity count from open function + cyclomatic_case_stack.push(cyclomatic_case_cnt - cyclomatic_switch_cnt); + cyclomatic_case_cnt = 0; + cyclomatic_switch_cnt = 0; + } + } + } + } + + // done with a file + if (main_cyclomatic_cnt > 0) + { + // add "main" code + lineElement element(main_cyclomatic_cnt, "main"); + lineElement n_element(main_cyclomatic_logic_cnt, "main"); + lineElement c_element(main_cyclomatic_case_cnt, "main"); + function_map[0] = element; + logical_map[0] = n_element; + case_map[0] = c_element; + } + else + { + // finish the first function if not closed + while (!function_stack.empty()) + { + function_name = function_stack.back().line; + function_count = function_stack.back().lineNumber; + function_stack.pop_back(); + + if (!function_stack.empty()) + { + // grab previous function from stack to continue + if (!cyclomatic_stack.empty()) + { + cyclomatic_cnt = cyclomatic_stack.top(); + cyclomatic_stack.pop(); + } + } + else + { + // capture count at end of function + lineElement element(cyclomatic_cnt - ignore_cyclomatic_cnt + 1, function_name); + lineElement n_element(cyclomatic_cnt - ignore_cyclomatic_cnt + cyclomatic_logic_cnt + 1, function_name); + lineElement c_element(cyclomatic_cnt - ignore_cyclomatic_cnt - cyclomatic_case_cnt + cyclomatic_switch_cnt + 1, function_name); + function_map[function_count] = element; + logical_map[function_count] = n_element; + case_map[function_count] = c_element; + } + } + } + + // process ordered functions + for (map::iterator it = function_map.begin(); it != function_map.end(); ++it) + result->cmplx_cycfunct_count.push_back(it->second); + if(cmplx_cyclomatic_logic_list.size() > 0) + { + for (map::iterator it = logical_map.begin(); it != logical_map.end(); ++it) + result->cmplx_cycfunct_CC2_count.push_back(it->second); + } + if(cmplx_cyclomatic_case_list.size() > 0) + { + for (map::iterator it = case_map.begin(); it != case_map.end(); ++it) + result->cmplx_cycfunct_CC3_count.push_back(it->second); + } + return 1; +} + +string CCshCounter::echoHelper(string line){ + string linetmp = ""; + int echoIdx; + bool isEnd = false; + int end = 0; + while (line.length() > 0 && !isEnd) { + end = (int)line.find(";", 0); + + echoIdx = (int)CUtil::FindKeyword(line, "echo"); + + if (end > 0 && (end < echoIdx || echoIdx < 0)) { + linetmp += line.substr( 0, (unsigned)end + 1 ); + int len = (int)line.length(); + line = line.substr( (unsigned)end + 1, (unsigned)len + 1 ); + continue; + } + else if(end < 0){ + isEnd = true; + } + if (echoIdx >= 0) { + linetmp += " " + line.substr( 0, (unsigned)echoIdx + 5 ); + unsigned int len = line.length(); + if (!isEnd) { + line = line.substr( (unsigned)end + 1, len + 1 ); + } + + } + } + if (linetmp != "") { + line = linetmp; + } + return line; + +} diff --git a/src/CCshCounter.h b/src/CCshCounter.h new file mode 100644 index 0000000..181ee7e --- /dev/null +++ b/src/CCshCounter.h @@ -0,0 +1,51 @@ +//! Code counter class definition for the C shell script language. +/*! +* \file CCshCounter.h +* +* This file contains the code counter class definition for the C shell script language. +* This also includes the Tcsh language. +*/ + +#ifndef CCshCounter_h +#define CCshCounter_h + +#include "CCodeCounter.h" + +//! C shell script code counter class. +/*! +* \class CCshCounter +* +* Defines the C shell script code counter class. +*/ +class CCshCounter : public CCodeCounter +{ +public: + CCshCounter(); + +protected: + virtual int PreCountProcess(filemap* fmap); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak = NULL); + void LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + bool &data_continue, unsigned int &temp_lines, unsigned int &phys_exec_lines, unsigned int &phys_data_lines, + unsigned int &loopLevel); + using CCodeCounter::ParseFunctionName; // fix warning + int ParseFunctionName(const string & /*line*/, string & /*lastline*/, filemap & /*functionStack*/, string & /*functionName*/) { return 2; } + int CountComplexity(filemap* fmap, results* result); + string echoHelper(string line); + + StringVector continue_keywords; //!< List of keywords to continue to next line + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CCshCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CCshCounter(const CCshCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CCshCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CCshCounter operator=(const CCshCounter); // Declare without implementation +}; + +#endif diff --git a/src/CCsharpCounter.cpp b/src/CCsharpCounter.cpp new file mode 100644 index 0000000..259996e --- /dev/null +++ b/src/CCsharpCounter.cpp @@ -0,0 +1,419 @@ +//! Code counter class methods for the C# language. +/*! +* \file CCsharpCounter.cpp +* +* This file contains the code counter class methods for the C# language. +*/ + +#include "CCsharpCounter.h" + +/*! +* Constructs a CCsharpCounter object. +*/ +CCsharpCounter::CCsharpCounter( string lang ) : CCJavaCsScalaCounter( lang ) +{ + classtype = CSHARP; + language_name = "C#"; + + isVerbatim = false; + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("C#", file_extension); + +// file_extension.push_back(".cs"); + + directive.push_back("#define"); + directive.push_back("#else"); + directive.push_back("#elif"); + directive.push_back("#endif"); + directive.push_back("#endregion"); + directive.push_back("#error"); + directive.push_back("#if"); + directive.push_back("#line"); + directive.push_back("#region"); + directive.push_back("#undef"); + directive.push_back("#warning"); + + data_name_list.push_back("abstract"); + data_name_list.push_back("bool"); + data_name_list.push_back("byte"); + data_name_list.push_back("char"); + data_name_list.push_back("class"); + data_name_list.push_back("const"); + data_name_list.push_back("decimal"); + data_name_list.push_back("delegate"); + data_name_list.push_back("double"); + data_name_list.push_back("enum"); + data_name_list.push_back("event"); + data_name_list.push_back("explicit"); + data_name_list.push_back("extern"); + data_name_list.push_back("float"); + data_name_list.push_back("implicit"); + data_name_list.push_back("int"); + data_name_list.push_back("interface"); + data_name_list.push_back("internal"); + data_name_list.push_back("long"); + data_name_list.push_back("namespace"); + data_name_list.push_back("object"); + data_name_list.push_back("operator"); + data_name_list.push_back("override"); + data_name_list.push_back("private"); + data_name_list.push_back("protected"); + data_name_list.push_back("public"); + data_name_list.push_back("readonly"); + data_name_list.push_back("sbyte"); + data_name_list.push_back("sealed"); + data_name_list.push_back("short"); + data_name_list.push_back("static"); + data_name_list.push_back("string"); + data_name_list.push_back("struct"); + data_name_list.push_back("uint"); + data_name_list.push_back("ulong"); + data_name_list.push_back("unsafe"); + data_name_list.push_back("ushort"); + data_name_list.push_back("using"); + data_name_list.push_back("virtual"); + data_name_list.push_back("void"); + data_name_list.push_back("volatile"); + + exec_name_list.push_back("as"); + exec_name_list.push_back("base"); + exec_name_list.push_back("break"); + exec_name_list.push_back("case"); + exec_name_list.push_back("catch"); + exec_name_list.push_back("checked"); + exec_name_list.push_back("continue"); + exec_name_list.push_back("default"); + exec_name_list.push_back("do"); + exec_name_list.push_back("else"); + exec_name_list.push_back("finally"); + exec_name_list.push_back("fixed"); + exec_name_list.push_back("for"); + exec_name_list.push_back("foreach"); + exec_name_list.push_back("goto"); + exec_name_list.push_back("if"); + exec_name_list.push_back("lock"); + exec_name_list.push_back("new"); + exec_name_list.push_back("return"); + exec_name_list.push_back("sizeof"); + exec_name_list.push_back("stackalloc"); + exec_name_list.push_back("switch"); + exec_name_list.push_back("this"); + exec_name_list.push_back("throw"); + exec_name_list.push_back("try"); + exec_name_list.push_back("typeof"); + exec_name_list.push_back("unchecked"); + exec_name_list.push_back("while"); + + math_func_list.push_back("abs"); + math_func_list.push_back("cbrt"); + math_func_list.push_back("ceil"); + math_func_list.push_back("copysign"); + math_func_list.push_back("erf"); + math_func_list.push_back("erfc"); + math_func_list.push_back("exp"); + math_func_list.push_back("exp2"); + math_func_list.push_back("expm1"); + math_func_list.push_back("fabs"); + math_func_list.push_back("floor"); + math_func_list.push_back("fdim"); + math_func_list.push_back("fma"); + math_func_list.push_back("fmax"); + math_func_list.push_back("fmin"); + math_func_list.push_back("fmod"); + math_func_list.push_back("frexp"); + math_func_list.push_back("hypot"); + math_func_list.push_back("ilogb"); + math_func_list.push_back("ldexp"); + math_func_list.push_back("lgamma"); + math_func_list.push_back("llrint"); + math_func_list.push_back("lrint"); + math_func_list.push_back("llround"); + math_func_list.push_back("lround"); + math_func_list.push_back("modf"); + math_func_list.push_back("nan"); + math_func_list.push_back("nearbyint"); + math_func_list.push_back("nextafter"); + math_func_list.push_back("nexttoward"); + math_func_list.push_back("pow"); + math_func_list.push_back("remainder"); + math_func_list.push_back("remquo"); + math_func_list.push_back("rint"); + math_func_list.push_back("round"); + math_func_list.push_back("scalbln"); + math_func_list.push_back("scalbn"); + math_func_list.push_back("sqrt"); + math_func_list.push_back("tgamma"); + math_func_list.push_back("trunc"); + + trig_func_list.push_back("cos"); + trig_func_list.push_back("cosh"); + trig_func_list.push_back("sin"); + trig_func_list.push_back("sinh"); + trig_func_list.push_back("tan"); + trig_func_list.push_back("tanh"); + trig_func_list.push_back("acos"); + trig_func_list.push_back("acosh"); + trig_func_list.push_back("asinh"); + trig_func_list.push_back("atanh"); + trig_func_list.push_back("asin"); + trig_func_list.push_back("atan"); + trig_func_list.push_back("atan2"); + + log_func_list.push_back("log"); + log_func_list.push_back("log10"); + log_func_list.push_back("log1p"); + log_func_list.push_back("log2"); + log_func_list.push_back("logb"); + + cmplx_preproc_list.push_back("define"); + cmplx_preproc_list.push_back("elif"); + cmplx_preproc_list.push_back("else"); + cmplx_preproc_list.push_back("endif"); + cmplx_preproc_list.push_back("endregion"); + cmplx_preproc_list.push_back("error"); + cmplx_preproc_list.push_back("if"); + cmplx_preproc_list.push_back("import"); + cmplx_preproc_list.push_back("line"); + cmplx_preproc_list.push_back("region"); + cmplx_preproc_list.push_back("undef"); + cmplx_preproc_list.push_back("warning"); + +/* Below Set in CCJavaCsScalaCounter base class + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("case"); + cmplx_cyclomatic_list.push_back("while"); + cmplx_cyclomatic_list.push_back("for"); + cmplx_cyclomatic_list.push_back("foreach"); + cmplx_cyclomatic_list.push_back("catch"); + cmplx_cyclomatic_list.push_back("?"); + + cmplx_cyclomatic_logic_list.push_back("||"); + cmplx_cyclomatic_logic_list.push_back("&&"); + //cmplx_cyclomatic_logic_list.push_back("^"); + + cmplx_cyclomatic_case_list.push_back("case"); + cmplx_cyclomatic_switch_list.push_back("switch"); +*/ + + //Modification: 2018.01 Integration starts + two_char_operator_list.push_back("??"); + two_char_operator_list.push_back("->"); + two_char_operator_list.push_back("::"); + two_char_operator_list.push_back("=>"); + + one_char_operator_list.push_back("@"); + + for (StringVector::iterator it = cmplx_cyclomatic_list.begin(); it != cmplx_cyclomatic_list.end(); it++) + keyword_operators.insert(*it); + + for (StringVector::iterator it = directive.begin(); it != directive.end(); it++) + keyword_operators.insert(*it); + + for (StringVector::iterator it = data_name_list.begin(); it != data_name_list.end(); it++) + keyword_operators.insert(*it); + + for (StringVector::iterator it = exec_name_list.begin(); it != exec_name_list.end(); it++) + keyword_operators.insert(*it); + //Modification: 2018.01 Integration ends + +} + +/*! +* Perform preprocessing of file lines before counting. +* +* \param fmap list of file lines +* +* \return method status +*/ +int CCsharpCounter::PreCountProcess(filemap* fmap) +{ + size_t i; + bool found; + filemap::iterator fit; + for (fit = fmap->begin(); fit != fmap->end(); fit++) + { + if (fit->line.empty()) + continue; + // check for parenthesis within attribute brackets [...()] + found = false; + for (i = 0; i < fit->line.length(); i++) + { + if (fit->line[i] == '[') + found = true; + else if (found) + { + if (fit->line[i] == ']') + found = false; + else if (fit->line[i] == '(' || fit->line[i] == ')') + fit->line[i] = '$'; + } + } + } + return 0; +} + +/*! +* Replaces up to ONE quoted string inside a string starting at idx_start. +* +* \param strline string to be processed +* \param idx_start index of line character to start search +* \param contd specifies the quote string is continued from the previous line +* \param CurrentQuoteEnd end quote character of the current status +* +* \return method status +*/ +int CCsharpCounter::ReplaceQuote(string &strline, size_t &idx_start, bool &contd, char &CurrentQuoteEnd) +{ + size_t idx_end, idx_quote, idx_verbatim; + char noQuoteEscapeFront = 0x00; + + if (contd) + { + idx_start = 0; + if (strline[0] == CurrentQuoteEnd) + { + if (!isVerbatim || (strline.length() < 2) || (strline[1] != '"')) + { + idx_start = 1; + contd = false; + return 1; + } + } + else + strline[0] = '$'; + } + else + { + // accommodate C# verbatim string (e.g. @"\") + isVerbatim = false; + idx_verbatim = strline.find_first_of("@"); + if (idx_verbatim != string::npos && idx_verbatim + 1 == idx_start) + isVerbatim = true; + + // handle two quote chars in some languages, both " and ' may be accepted + idx_start = FindQuote(strline, QuoteStart, idx_start, QuoteEscapeFront); + if (idx_start != string::npos) + { + idx_quote = QuoteStart.find_first_of(strline[idx_start]); + CurrentQuoteEnd = QuoteEnd[idx_quote]; + } + else + { + idx_start = strline.length(); + return 0; + } + } + + // accommodate C# verbatim string (e.g. @"\") + if (isVerbatim) // verbatim string + idx_end = CUtil::FindCharAvoidEscape(strline, CurrentQuoteEnd, idx_start + 1, noQuoteEscapeFront); + else + idx_end = CUtil::FindCharAvoidEscape(strline, CurrentQuoteEnd, idx_start + 1, QuoteEscapeFront); + if (idx_end == string::npos) + { + idx_end = strline.length() - 1; + strline.replace(idx_start + 1, idx_end - idx_start, idx_end - idx_start, '$'); + contd = true; + idx_start = idx_end + 1; + } + else + { + if ((isVerbatim && (strline.length() > idx_end + 1) && (strline[idx_end+1] == '"')) || + ((QuoteEscapeRear) && (strline.length() > idx_end + 1) && (strline[idx_end+1] == QuoteEscapeRear))) + { + strline[idx_end] = '$'; + strline[idx_end+1] = '$'; + } + else + { + isVerbatim = false; + contd = false; + strline.replace(idx_start + 1, idx_end - idx_start - 1, idx_end - idx_start - 1, '$'); + idx_start = idx_end + 1; + } + } + return 1; +} + +/*! +* Constructs a CCsharpHtmlCounter object. +*/ +CCsharpHtmlCounter::CCsharpHtmlCounter() +{ + classtype = CSHARP_HTML; + language_name = "C#/HTML"; + + file_extension.clear(); + file_extension.push_back(".*cshtm"); +} + +/*! +* Constructs a CCsharpXmlCounter object. +*/ +CCsharpXmlCounter::CCsharpXmlCounter() +{ + classtype = CSHARP_XML; + language_name = "C#/XML"; + + file_extension.clear(); + file_extension.push_back(".*csxml"); +} + +/*! +* Constructs a CCsharpAspCounter object. +*/ +CCsharpAspCounter::CCsharpAspCounter() +{ + classtype = CSHARP_ASP_S; + language_name = "C#/ASPNET"; + + file_extension.clear(); + file_extension.push_back(".*csasps"); +} + +//Modification: 2018.01 Integration starts +bool CCsharpCounter::HasOneDoubleQuote(string line) { + string two_doublequotes = "\"\""; + + //Modification: 2018.01, changed data type to size_t + size_t idx = line.find(two_doublequotes); + while (idx != string::npos) { + line.replace(idx, 2, " "); + idx += 2; + idx = line.find(two_doublequotes, idx); + } + + return line.find("\"") != string::npos; +} + +int CCsharpCounter::GetLineUntilEndOfMultistringIfAny(int current_line_idx, string &line, filemap &fmap, map &nonfunction_operator_counts) { + string beginning_of_raw_string = "@\""; + //Modification: 2018.01, changed data type to size_t + size_t idx = line.find(beginning_of_raw_string); + size_t curr_line_idx = current_line_idx; + (void) nonfunction_operator_counts; //Modification: 2018.01 : Unused argument + + + // If not the beginning of a raw string then don't need to do anything + if (idx == string::npos) + return curr_line_idx; + + if (HasOneDoubleQuote(line.substr(idx + 2))) + return curr_line_idx; + + string line_with_end_of_multistring_if_any = ""; + string curr_line = CUtil::TrimString(fmap[curr_line_idx].line, 0); + + do { + line_with_end_of_multistring_if_any.append(curr_line); + curr_line_idx++; + curr_line = CUtil::TrimString(fmap[curr_line_idx].line, 0); + } while (!HasOneDoubleQuote(curr_line) && curr_line_idx < fmap.size() - 2); + + line = line_with_end_of_multistring_if_any + curr_line; + + return curr_line_idx; +} + +//Modification: 2018.01 Integration ends diff --git a/src/CCsharpCounter.h b/src/CCsharpCounter.h new file mode 100644 index 0000000..80cac00 --- /dev/null +++ b/src/CCsharpCounter.h @@ -0,0 +1,118 @@ +//! Code counter class definition for the C# language. +/*! +* \file CCsharpCounter.h +* +* This file contains the code counter class definition for the C# language. +*/ + +#ifndef CCsharpCounter_h +#define CCsharpCounter_h + +#include "CCJavaCsScalaCounter.h" + +//! C# code counter class. +/*! +* \class CCsharpCounter +* +* Defines the C# code counter class. +*/ +class CCsharpCounter : public CCJavaCsScalaCounter +{ +public: + // Set the language so base class constructors set values as needed + CCsharpCounter( string lang = "C_SHARP" ); + +protected: + virtual int PreCountProcess(filemap* fmap); + virtual int ReplaceQuote(string &strline, size_t &idx_start, bool &contd, char &CurrentQuoteEnd); + bool HasOneDoubleQuote(string line); + int GetLineUntilEndOfMultistringIfAny(int curr_line_idx, string &line, filemap &fmap, map &nonfunction_operator_counts); + +private: + bool isVerbatim; + +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CCsharpCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CCsharpCounter(const CCsharpCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CCsharpCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CCsharpCounter operator=(const CCsharpCounter); // Declare without implementation +}; + +//! C# in HTML code counter class. +/*! +* \class CCsharpHtmlCounter +* +* Defines the C# in HTML code counter class. +*/ +class CCsharpHtmlCounter : public CCsharpCounter +{ +public: + CCsharpHtmlCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CCsharpHtmlCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CCsharpHtmlCounter(const CCsharpHtmlCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CCsharpHtmlCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CCsharpHtmlCounter operator=(const CCsharpHtmlCounter); // Declare without implementation +}; + +//! C# in XML code counter class. +/*! +* \class CCsharpXmlCounter +* +* Defines the C# in XML code counter class. +*/ +class CCsharpXmlCounter : public CCsharpCounter +{ +public: + CCsharpXmlCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CCsharpXmlCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CCsharpXmlCounter(const CCsharpXmlCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CCsharpXmlCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CCsharpXmlCounter operator=(const CCsharpXmlCounter); // Declare without implementation +}; + +//! C# in ASP code counter class. +/*! +* \class CCsharpAspCounter +* +* Defines the C# in ASP code counter class. +*/ +class CCsharpAspCounter : public CCsharpCounter +{ +public: + CCsharpAspCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CCsharpAspCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CCsharpAspCounter(const CCsharpAspCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CCsharpAspCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CCsharpAspCounter operator=(const CCsharpAspCounter); // Declare without implementation +}; + +#endif diff --git a/src/CCssCounter.cpp b/src/CCssCounter.cpp new file mode 100644 index 0000000..321c980 --- /dev/null +++ b/src/CCssCounter.cpp @@ -0,0 +1,264 @@ +//! Code counter class methods for the cascading style sheet (CSS) language. +/*! +* \file CCssCounter.cpp +* +* This file contains the code counter class methods for the cascading style sheet (CSS) language. +*/ + +#include "CCssCounter.h" + +/*! +* Constructs a CCssCounter object. +*/ +CCssCounter::CCssCounter() +{ + classtype = CSS; + language_name = "CSS"; + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("CSS", file_extension); + /* + file_extension.push_back(".css");*/ + + BlockCommentStart.push_back("/*"); + BlockCommentEnd.push_back("*/"); + + exec_name_list.push_back("azimuth"); + exec_name_list.push_back("background"); + exec_name_list.push_back("background-attachment"); + exec_name_list.push_back("background-color"); + exec_name_list.push_back("background-image"); + exec_name_list.push_back("background-repeat"); + exec_name_list.push_back("background-position"); + exec_name_list.push_back("border"); + exec_name_list.push_back("border-bottom"); + exec_name_list.push_back("border-collapse"); + exec_name_list.push_back("border-bottom-color"); + exec_name_list.push_back("border-bottom-style"); + exec_name_list.push_back("border-bottom-width"); + exec_name_list.push_back("border-left"); + exec_name_list.push_back("border-left-color"); + exec_name_list.push_back("border-left-style"); + exec_name_list.push_back("border-left-width"); + exec_name_list.push_back("border-right"); + exec_name_list.push_back("border-right-color"); + exec_name_list.push_back("border-right-style"); + exec_name_list.push_back("border-right-width"); + exec_name_list.push_back("border-spacing"); + exec_name_list.push_back("border-style"); + exec_name_list.push_back("border-top"); + exec_name_list.push_back("border-top-color"); + exec_name_list.push_back("border-top-style"); + exec_name_list.push_back("border-top-width"); + exec_name_list.push_back("border-width"); + exec_name_list.push_back("bottom"); + exec_name_list.push_back("caption-side"); + exec_name_list.push_back("clear"); + exec_name_list.push_back("clip"); + exec_name_list.push_back("content"); + exec_name_list.push_back("counter-decrement"); + exec_name_list.push_back("counter-increment"); + exec_name_list.push_back("counter-reset"); + exec_name_list.push_back("cue"); + exec_name_list.push_back("cue-after"); + exec_name_list.push_back("cue-before"); + exec_name_list.push_back("cursor"); + exec_name_list.push_back("direction"); + exec_name_list.push_back("empty-cells"); + exec_name_list.push_back("float"); + exec_name_list.push_back("font"); + exec_name_list.push_back("font-family"); + exec_name_list.push_back("font-size"); + exec_name_list.push_back("font-style"); + exec_name_list.push_back("font-variant"); + exec_name_list.push_back("font-weight"); + exec_name_list.push_back("height"); + exec_name_list.push_back("left"); + exec_name_list.push_back("letter-spacing"); + exec_name_list.push_back("line-height"); + exec_name_list.push_back("list-style"); + exec_name_list.push_back("list-style-image"); + exec_name_list.push_back("list-style-position"); + exec_name_list.push_back("list-style-type"); + exec_name_list.push_back("line-width"); + exec_name_list.push_back("margin"); + exec_name_list.push_back("margin-bottom"); + exec_name_list.push_back("margin-left"); + exec_name_list.push_back("margin-right"); + exec_name_list.push_back("margin-top"); + exec_name_list.push_back("max-height"); + exec_name_list.push_back("max-width"); + exec_name_list.push_back("min-height"); + exec_name_list.push_back("min-width"); + exec_name_list.push_back("orphans"); + exec_name_list.push_back("outline"); + exec_name_list.push_back("outline-color"); + exec_name_list.push_back("outline-style"); + exec_name_list.push_back("outline-width"); + exec_name_list.push_back("padding"); + exec_name_list.push_back("padding-bottom"); + exec_name_list.push_back("padding-left"); + exec_name_list.push_back("padding-right"); + exec_name_list.push_back("padding-top"); + exec_name_list.push_back("page-break-after"); + exec_name_list.push_back("page-break-before"); + exec_name_list.push_back("pause"); + exec_name_list.push_back("pause-after"); + exec_name_list.push_back("pitch"); + exec_name_list.push_back("play"); + exec_name_list.push_back("play-during"); + exec_name_list.push_back("pitch-range"); + exec_name_list.push_back("position"); + exec_name_list.push_back("quptes"); + exec_name_list.push_back("richness"); + exec_name_list.push_back("right"); + exec_name_list.push_back("speak"); + exec_name_list.push_back("speak-header"); + exec_name_list.push_back("speak-numeral"); + exec_name_list.push_back("speak-punctuation"); + exec_name_list.push_back("speech-rate"); + exec_name_list.push_back("stress"); + exec_name_list.push_back("table-layout"); + exec_name_list.push_back("text"); + exec_name_list.push_back("text-transform"); + exec_name_list.push_back("text-indent"); + exec_name_list.push_back("text-decoration"); + exec_name_list.push_back("text-align"); + exec_name_list.push_back("top"); + exec_name_list.push_back("unicode-bidi"); + exec_name_list.push_back("vertical-align"); + exec_name_list.push_back("visibility"); + exec_name_list.push_back("voice-family"); + exec_name_list.push_back("volume"); + exec_name_list.push_back("white-space"); + exec_name_list.push_back("width"); + exec_name_list.push_back("word-spacing"); + exec_name_list.push_back("z-index"); +} + +/*! +* Processes physical and logical lines according to language specific rules. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CCssCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* /*fmapBak*/) +{ + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + char prev_char = ' '; + string strLSLOC = ""; + string line = ""; + unsigned int phys_exec_lines = 0; + unsigned int phys_data_lines = 0; + unsigned int cnt = 0; + + // The fmap may not contain any lines (e.g. when the file was not found). + // If this check is not performed, when the for loop starts on the second + // line (see +1 below) an exception will occur. Ideally this exception + // will be passed up to CCodeCounter::CountSLOC and then be caught in + // MainObject::ProcessSourceList resulting in the output "Unable to count + // file". However this exception causes a program crash in Microsoft + // Visual Studio 2008. Note that this check causes the exception not to + // occur, and therefore the "Unable to count file" message not be be + // displayed, but the more appropriate "unable to open file" message is + // displayed with or without this check. + if (fmap->size() == 0) + return 1; + + // iterating line-by-line of the file + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++) + { + line = iter->line; + + // checking for a blank line + if (!CUtil::CheckBlank(line)) + { + // LSLOC call for the current line, which is not blank and does not contain non-css keywords + LSLOC(result, line, iter->lineNumber, strLSLOC, prev_char, phys_exec_lines, phys_data_lines); + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(iter->line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count); + } + + // update physical SLOC + result->exec_lines[PHY] += phys_exec_lines; + phys_exec_lines = 0; + + result->data_lines[PHY] += phys_data_lines; + phys_data_lines = 0; + } + } + return 1; +} + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results +* \param line processed physical line of code +* \param strLSLOC processed logical string +* \param lastLinesLastChar last character on previous line +* \param phys_exec_lines number of physical executable lines +* \param phys_data_lines number of physical data lines +*/ +void CCssCounter::LSLOC(results* result, string &line, size_t lineNumber, string &strLSLOC, char &lastLinesLastChar, + unsigned int &phys_exec_lines, unsigned int &phys_data_lines) +{ + size_t index = 0, strSize; + bool trunc_flag = false; + string tmp = CUtil::TrimString(line); + + // iterating character-by-character for the current line + while (index < tmp.length()) + { + // checking current character + switch (line[index]) + { + case ';': case '}': + // adding the current exec strLSLOC to results + if (result->addSLOC(strLSLOC, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + + // reinitialize strLSLOC to null + strLSLOC = ""; + + break; + case '{': + // adding the current data strLSLOC to results + if (result->addSLOC(strLSLOC, lineNumber, trunc_flag)) + result->data_lines[LOG]++; + + // reinitialize strLSLOC to null + strLSLOC = ""; + + break; + default: + // by default for other characters than '{' or ';' or '}' this case is executed + // append character to strLSLOC + strSize = CUtil::TruncateLine(1, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strLSLOC += tmp[index]; + break; + } + + // incrementing index to point to next character + index++; + } + + if (tmp[tmp.length() - 1] == ';' || + (lastLinesLastChar == ';' && tmp.find_first_of('{') == string::npos && tmp.find_first_of('}') == string::npos)) + phys_exec_lines++; + else + phys_data_lines++; + + // extract the last line's last character + lastLinesLastChar = tmp[tmp.length() - 1]; +} diff --git a/src/CCssCounter.h b/src/CCssCounter.h new file mode 100644 index 0000000..f6e4546 --- /dev/null +++ b/src/CCssCounter.h @@ -0,0 +1,42 @@ +//! Code counter class definition for the cascading style sheet (CSS) language. +/*! +* \file CCssCounter.h +* +* This file contains the code counter class definition for the cascading style sheet (CSS) language. +*/ + +#ifndef CCssCounter_h +#define CCssCounter_h + +#include "CCodeCounter.h" + +//! CSS code counter class. +/*! +* \class CCssCounter +* +* Defines the CSS code counter class. +*/ +class CCssCounter : public CCodeCounter +{ +public: + CCssCounter(); + +protected: + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak = NULL); + void LSLOC(results* result, string &line, size_t lineNumber, string &strLSLOC, char &lastLinesLastChar, + unsigned int &phys_exec_lines, unsigned int &phys_data_lines); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CCssCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CCssCounter(const CCssCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CCssCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CCssCounter operator=(const CCssCounter); // Declare without implementation +}; + +#endif diff --git a/src/CDataCounter.cpp b/src/CDataCounter.cpp new file mode 100644 index 0000000..6651e37 --- /dev/null +++ b/src/CDataCounter.cpp @@ -0,0 +1,17 @@ +//! Code counter class methods for data files. +/*! +* \file CDataCounter.cpp +* +* This file contains the code counter class methods for data files. +*/ + +#include "CDataCounter.h" + +/*! +* Constructs a CDataCounter object. +*/ +CDataCounter::CDataCounter() +{ + classtype = DATAFILE; + language_name = "Datafile"; +} diff --git a/src/CDataCounter.h b/src/CDataCounter.h new file mode 100644 index 0000000..2ab23ed --- /dev/null +++ b/src/CDataCounter.h @@ -0,0 +1,37 @@ +//! Code counter class definition for data files. +/*! +* \file CDataCounter.h +* +* This file contains the code counter class definition for data files. +*/ + +#ifndef CDataCounter_h +#define CDataCounter_h + +#include "CCodeCounter.h" + +//! Data file code counter class. +/*! +* \class CDataCounter +* +* Defines the data file code counter class. +*/ +class CDataCounter : public CCodeCounter +{ +public: + CDataCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CDataCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CDataCounter(const CDataCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CDataCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CDataCounter operator=(const CDataCounter); // Declare without implementation +}; + +#endif diff --git a/src/CFortranCounter.cpp b/src/CFortranCounter.cpp new file mode 100644 index 0000000..d373629 --- /dev/null +++ b/src/CFortranCounter.cpp @@ -0,0 +1,1046 @@ +//! Code counter class methods for the Fortran language. +/*! +* \file CFortranCounter.cpp +* +* This file contains the code counter class methods for the Fortran language. +* This includes F77, F90, F95, and F03 including fixed and free formats. +*/ + +#include "CFortranCounter.h" + +/*! +* Constructs a CFortranCounter object. +*/ +CFortranCounter::CFortranCounter() +{ + classtype = FORTRAN; + language_name = "Fortran"; + casesensitive = false; + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("Fortran", file_extension); + + /* + file_extension.push_back(".f"); + file_extension.push_back(".for"); + file_extension.push_back(".f77"); + file_extension.push_back(".f90"); + file_extension.push_back(".f95"); + file_extension.push_back(".f03"); + file_extension.push_back(".hpf");*/ + + QuoteStart = "\"'"; + QuoteEnd = "\"'"; + ContinueLine = "&"; + + LineCommentStart.push_back("!"); + + exclude_keywords.push_back("case default"); + exclude_keywords.push_back("else"); + exclude_keywords.push_back("end"); + exclude_keywords.push_back("endblockdata"); + exclude_keywords.push_back("enddo"); + exclude_keywords.push_back("endfile"); + exclude_keywords.push_back("endfunction"); + exclude_keywords.push_back("endif"); + exclude_keywords.push_back("endinterface"); + exclude_keywords.push_back("endmodule"); + exclude_keywords.push_back("endprogram"); + exclude_keywords.push_back("endselect"); + exclude_keywords.push_back("endsubroutine"); + exclude_keywords.push_back("endtype"); + exclude_keywords.push_back("endwhere"); + exclude_keywords.push_back("endforall"); // f95+ only + exclude_keywords.push_back("endassociate"); // f03+ only + exclude_keywords.push_back("endenum"); // f03+ only + + c_keywords.push_back("call"); + c_keywords.push_back("case default"); + c_keywords.push_back("class default"); // f03+ only + c_keywords.push_back("character"); + c_keywords.push_back("common"); + c_keywords.push_back("complex"); + c_keywords.push_back("contains"); + c_keywords.push_back("continue"); + c_keywords.push_back("cycle"); + + directive.push_back("dictionary"); + directive.push_back("include"); + directive.push_back("options"); + + data_name_list.push_back("allocate"); + data_name_list.push_back("assign"); + data_name_list.push_back("associate"); // f03+ only + data_name_list.push_back("common"); + data_name_list.push_back("complex"); + data_name_list.push_back("character"); + data_name_list.push_back("contains"); + data_name_list.push_back("data"); + data_name_list.push_back("deallocate"); + data_name_list.push_back("dimension"); + data_name_list.push_back("double precision"); + data_name_list.push_back("enum"); // f03+ only + data_name_list.push_back("equivalence"); + data_name_list.push_back("external"); + data_name_list.push_back("final"); // f03+ only + data_name_list.push_back("function"); + data_name_list.push_back("generic"); // f03+ only + data_name_list.push_back("implicit"); + data_name_list.push_back("import"); // f03+ only + data_name_list.push_back("integer"); + data_name_list.push_back("interface"); + data_name_list.push_back("intrinsic"); + data_name_list.push_back("logical"); + data_name_list.push_back("module"); + data_name_list.push_back("namelist"); + data_name_list.push_back("nullify"); + data_name_list.push_back("optional"); + data_name_list.push_back("parameter"); + data_name_list.push_back("program"); + data_name_list.push_back("real"); + data_name_list.push_back("reallocate"); + data_name_list.push_back("recursive"); + data_name_list.push_back("save"); + data_name_list.push_back("select type"); // f03+ only + data_name_list.push_back("subroutine"); + data_name_list.push_back("type"); + data_name_list.push_back("use"); + + exec_name_list.push_back("backspace"); + exec_name_list.push_back("call"); + exec_name_list.push_back("close"); + exec_name_list.push_back("cycle"); + exec_name_list.push_back("do"); + exec_name_list.push_back("elseif"); + exec_name_list.push_back("entry"); + exec_name_list.push_back("exit"); + exec_name_list.push_back("forall"); // f95+ only + exec_name_list.push_back("format"); + exec_name_list.push_back("goto"); + exec_name_list.push_back("if"); + exec_name_list.push_back("inquire"); + exec_name_list.push_back("open"); + exec_name_list.push_back("pause"); + exec_name_list.push_back("print"); + exec_name_list.push_back("read"); + exec_name_list.push_back("return"); + exec_name_list.push_back("rewind"); + exec_name_list.push_back("select case"); + exec_name_list.push_back("stop"); + exec_name_list.push_back("where"); + exec_name_list.push_back("write"); + + math_func_list.push_back("abs"); + math_func_list.push_back("ceiling"); + math_func_list.push_back("dim"); + math_func_list.push_back("dot_product"); + math_func_list.push_back("dprod"); + math_func_list.push_back("exp"); + math_func_list.push_back("floor"); + math_func_list.push_back("matmul"); + math_func_list.push_back("max"); + math_func_list.push_back("min"); + math_func_list.push_back("mod"); + math_func_list.push_back("modulo"); + math_func_list.push_back("sign"); + math_func_list.push_back("sqrt"); + + trig_func_list.push_back("acos"); + trig_func_list.push_back("acosh"); + trig_func_list.push_back("asin"); + trig_func_list.push_back("asinh"); + trig_func_list.push_back("atan"); + trig_func_list.push_back("atan2"); + trig_func_list.push_back("atanh"); + trig_func_list.push_back("cos"); + trig_func_list.push_back("cosh"); + trig_func_list.push_back("sin"); + trig_func_list.push_back("sinh"); + trig_func_list.push_back("tan"); + trig_func_list.push_back("tanh"); + + log_func_list.push_back("log"); + log_func_list.push_back("log10"); + + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("/"); + cmplx_calc_list.push_back("**"); + + cmplx_cond_list.push_back("do"); + cmplx_cond_list.push_back("else"); + cmplx_cond_list.push_back("else if"); + cmplx_cond_list.push_back("elseif"); + cmplx_cond_list.push_back("forall"); // f95+ only + cmplx_cond_list.push_back("if"); + cmplx_cond_list.push_back("select case"); + cmplx_cond_list.push_back("select type"); // f03+ only + + cmplx_logic_list.push_back(".and."); + cmplx_logic_list.push_back(".or."); + cmplx_logic_list.push_back(".not."); + cmplx_logic_list.push_back(".eqv."); + cmplx_logic_list.push_back(".neqv."); + cmplx_logic_list.push_back("=="); + cmplx_logic_list.push_back("/="); + cmplx_logic_list.push_back(".eq."); + cmplx_logic_list.push_back(".ne."); + cmplx_logic_list.push_back(">"); + cmplx_logic_list.push_back("<"); + cmplx_logic_list.push_back(">="); + cmplx_logic_list.push_back("=<"); + cmplx_logic_list.push_back(".gt."); + cmplx_logic_list.push_back(".lt."); + cmplx_logic_list.push_back(".ge."); + cmplx_logic_list.push_back(".le."); + cmplx_logic_list.push_back(".true."); + cmplx_logic_list.push_back(".false."); + + cmplx_preproc_list.push_back("dictionary"); + cmplx_preproc_list.push_back("include"); + cmplx_preproc_list.push_back("options"); + + cmplx_assign_list.push_back("="); + + cmplx_pointer_list.push_back("=>"); + + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("do"); + //cmplx_cyclomatic_list.push_back("IIf"); + //cmplx_cyclomatic_list.push_back("for"); + //cmplx_cyclomatic_list.push_back("while"); + //cmplx_cyclomatic_list.push_back("Until"); + //cmplx_cyclomatic_list.push_back("Catch"); + //cmplx_cyclomatic_list.push_back("WHEN"); + //cmplx_cyclomatic_list.push_back("select"); + cmplx_cyclomatic_list.push_back("case"); + + ignore_cmplx_cyclomatic_list.push_back("end do"); + ignore_cmplx_cyclomatic_list.push_back("end if"); + + cmplx_cyclomatic_logic_list.push_back(".and."); + cmplx_cyclomatic_logic_list.push_back(".or."); + //cmplx_cyclomatic_logic_list.push_back(".eqv."); + //cmplx_cyclomatic_logic_list.push_back(".neqv."); + + cmplx_cyclomatic_case_list.push_back("case"); + cmplx_cyclomatic_switch_list.push_back("end select"); + cmplx_cyclomatic_default_list.push_back("default"); + cmplx_cyclomatic_default_list.push_back("select case"); +} + +/*! +* Perform preprocessing of file lines before counting. +* Replace quote stuffing in literals '' or "" to avoid quote matching problems. +* +* \param fmap list of file lines +* +* \return method status +*/ +int CFortranCounter::PreCountProcess(filemap* fmap) +{ + size_t i; + filemap::iterator fit; + for (fit = fmap->begin(); fit != fmap->end(); fit++) + { + if (fit->line.empty()) + continue; + for (i = fit->line.length() - 1; i > 0; i--) + { + if ((fit->line[i] == '\'' && fit->line[i-1] == '\'') || (fit->line[i] == '"' && fit->line[i-1] == '"')) + { + fit->line[i] = '$'; + fit->line[i-1] = '$'; + } + } + } + return 0; +} + +/*! +* Counts the number of comment lines, removes comments, and +* replaces quoted strings by special chars, e.g., $ +* All arguments are modified by the method. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CFortranCounter::CountCommentsSLOC(filemap* fmap, results* result, filemap *fmapBak) +{ + if (LineCommentStart.empty()) + return 0; + if (classtype == UNKNOWN || classtype == DATAFILE) + return 0; + + bool contd_nextline; + int comment_type = 0; + /* + comment_type: + 0 : not comment + 1 : line comment, whole line + 2 : line comment, embedded + */ + + size_t i, idx_start, comment_start; + size_t quote_idx_start; + string curBlckCmtStart, curBlckCmtEnd, prevLine; + char CurrentQuoteEnd = 0; + bool quote_contd = false, found, foundSpc; + filemap::iterator itfmBak = fmapBak->begin(); + + quote_idx_start = 0; + prevLine = ""; + + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++, itfmBak++) + { + contd_nextline = false; + + quote_idx_start = 0; + idx_start = 0; + + if (CUtil::CheckBlank(iter->line)) + continue; + if (quote_contd) + { + // replace quote until next character + ReplaceQuote(iter->line, quote_idx_start, quote_contd, CurrentQuoteEnd); + prevLine = itfmBak->line; + if (quote_contd) + continue; + } + + while (!contd_nextline && idx_start < iter->line.length()) + { + quote_idx_start = FindQuote(iter->line, QuoteStart, quote_idx_start, QuoteEscapeFront); + comment_start = idx_start; + + // check for comment delimiters 'C', 'c' in col 1 (works for most cases) + found = false; + if ((iter->line[0] == 'C' || iter->line[0] == 'c') && + (prevLine.length() < 1 || prevLine[prevLine.length() - 1] != '&')) + { + // check for reserved 'c' words + for (vector::iterator viter = c_keywords.begin(); viter != c_keywords.end(); viter++) + { + if (CUtil::FindKeyword(iter->line, *viter, 0, TO_END_OF_STRING, false) == 0) + { + found = true; + break; + } + } + if (!found) + { + // check for function or assignment (check for 'c__()' or 'c__ =') + foundSpc = false; + for (i = 1; i < iter->line.length(); i++) + { + if (iter->line[i] == '(') + { + found = true; + break; + } + else if (iter->line[i] == '=') + { + if (i >= iter->line.length() - 1 || iter->line[i + 1] != '=') + found = true; + break; + } + else if (iter->line[i] == ' ') + foundSpc = true; + else if (foundSpc) + break; + } + } + found = !found; + } + + // check for comment delimiters '*', '!' in col 1 + if (found || ((iter->line[0] == '*' || iter->line[0] == '!') && + (prevLine.length() < 1 || prevLine[prevLine.length() - 1] != '&'))) + { + comment_start = 0; + comment_type = 1; + } + // commented out to favor Fortran 90+ (in Fortran 77 any character in column 6 indicates continuation, not comment) + // else if (iter->line.length() > 6 && iter->line[5] == '!' && CUtil::CheckBlank(iter->line.substr(0, 5))) + // comment_start = string::npos; + else + { + FindCommentStart(iter->line, comment_start, comment_type, curBlckCmtStart, curBlckCmtEnd); + if (comment_start != string::npos) + { + // check for characters before comment + for (i = 0; i < comment_start; i++) + { + if (iter->line[i] != ' ') + { + comment_type = 2; + break; + } + } + } + } + + if (comment_start == string::npos && quote_idx_start == string::npos) + { + prevLine = itfmBak->line; + break; + } + + if (comment_start != string::npos) + idx_start = comment_start; + + // if found quote before comment + if (quote_idx_start != string::npos && (comment_start == string::npos || quote_idx_start < comment_start)) + { + ReplaceQuote(iter->line, quote_idx_start, quote_contd, CurrentQuoteEnd); + if (quote_idx_start > idx_start) + { + if (quote_contd) + { + if (itfmBak->line[itfmBak->line.length() - 1] == '&') + { + iter->line[iter->line.length() - 1] = '&'; + if (itfmBak->line.length() > 2 && itfmBak->line[itfmBak->line.length() - 2] == ' ') + iter->line[iter->line.length() - 2] = ' '; + } + } + idx_start = quote_idx_start; + prevLine = itfmBak->line; + continue; // comment delimiter inside quote + } + } + else if (idx_start != string::npos) + { + // comment delimiter starts first + switch(comment_type) + { + case 1: // line comment, definitely whole line + case 3: + prevLine = ""; + iter->line = ""; + itfmBak->line = ""; + result->comment_lines++; + contd_nextline = true; + break; + case 2: // line comment, possibly embedded + case 4: + result->e_comm_lines++; + prevLine = ""; + iter->line = iter->line.substr(0, idx_start); + itfmBak->line = itfmBak->line.substr(0, idx_start); + // trim trailing space + iter->line = CUtil::TrimString(iter->line, 1); + itfmBak->line = CUtil::TrimString(itfmBak->line, 1); + if (iter->line.empty()) + result->comment_lines++; // whole line + else + result->e_comm_lines++; // embedded + contd_nextline = true; + break; + default: + cout << "Error in CountCommentsSLOC()"; + break; + } + } + } + } + return 1; +} + +/*! +* Counts directive lines of code. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CFortranCounter::CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak) +{ + bool contd = false, trunc_flag = false; + size_t idx, strSize; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string strDirLine = "", tmp, str; + filemap::iterator itfmBak = fmapBak->begin(); + + for (filemap::iterator iter = fmap->begin(); iter!=fmap->end(); iter++, itfmBak++) + { + tmp = CUtil::TrimString(iter->line); + if (CUtil::CheckBlank(iter->line)) + continue; + size_t lineNumber = iter->lineNumber; + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(" " + iter->line, directive, cnt, 1, exclude, "", "", &result->directive_count, false); + } + + if (!contd) + { + // if not a continuation of a previous directive + for (vector::iterator viter = directive.begin(); viter != directive.end(); viter++) + { + if (((idx = CUtil::FindKeyword(iter->line, *viter, 0, TO_END_OF_STRING, false)) != string::npos) && idx == 0) + { + contd = true; + break; + } + } + if (contd) + { + strSize = CUtil::TruncateLine(itfmBak->line.length(), 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine = CUtil::TrimString(itfmBak->line.substr(0, strSize)); + if (strDirLine[strDirLine.length() - 1] == '&') + strDirLine = CUtil::TrimString(strDirLine.substr(0, strDirLine.length() - 1)); + result->directive_lines[PHY]++; + } + } + else + { + // continuation of a previous directive + strSize = CUtil::TruncateLine(itfmBak->line.length(), strDirLine.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + str = CUtil::TrimString(itfmBak->line.substr(0, strSize)); + if (str[0] == '&') + strDirLine += CUtil::TrimString(str.substr(1, str.length() - 1)); + else + strDirLine += str; + } + result->directive_lines[PHY]++; + } + + if (contd) + { + // if a directive or continuation of a directive (no continuation symbol found) + if (iter->line[iter->line.length() - 1] != '&') + { + contd = false; + if (result->addSLOC(strDirLine, lineNumber, trunc_flag)) + result->directive_lines[LOG]++; + } + iter->line = ""; + } + } + return 1; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CFortranCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + filemap::iterator fit, fitbak, fitNext = fmap->begin(); + string line, lineBak, lineNext; + + bool data_continue = false, fixed_continue = false; + string strLSLOC = ""; + string strLSLOCBak = ""; + string str; + unsigned int phys_exec_lines = 0; + unsigned int phys_data_lines = 0; + unsigned int temp_lines = 0; + unsigned int cnt = 0; + StringVector loopEnd; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + if (fitNext != fmap->end()) + fitNext++; + line = fit->line; + lineBak = fitbak->line; + + // do not process blank lines (blank_line/comment_line/directive) + if (!CUtil::CheckBlank(line)) + { + // get next line to check for fixed format continuation (non-blank and non-0 in col 6) + lineNext = ""; + fixed_continue = false; + if (fitNext != fmap->end()) + { + lineNext = fitNext->line; + if (!CUtil::CheckBlank(lineNext)) + { + if (lineNext.length() > 6) + { + str = lineNext.substr(0, 5); + if ((CUtil::CheckBlank(str) || CUtil::IsInteger(str)) && + lineNext[5] != ' ' && lineNext[5] != '0') + { + // fixed format continuation + fixed_continue = true; + } + } + } + } + + // process logical SLOC + LSLOC(result, line, fit->lineNumber, lineBak, strLSLOC, strLSLOCBak, fixed_continue, + data_continue, temp_lines, phys_exec_lines, phys_data_lines, loopEnd); + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count, false); + } + + // update physical SLOC lines + result->exec_lines[PHY] += phys_exec_lines; + phys_exec_lines = 0; + + result->data_lines[PHY] += phys_data_lines; + phys_data_lines = 0; + } + } + return 1; +} + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results +* \param line processed physical line of code +* \param lineBak original physical line of code +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param fixed_continue fixed format line continues on next line +* \param data_continue continuation of a data declaration line +* \param temp_lines tracks physical line count +* \param phys_exec_lines number of physical executable lines +* \param phys_data_lines number of physical data lines +* \param loopEnd nested loop end string(s) +*/ +void CFortranCounter::LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + bool &fixed_continue, bool &data_continue, unsigned int &temp_lines, unsigned int &phys_exec_lines, + unsigned int &phys_data_lines, StringVector &loopEnd) +{ + size_t start, end; + size_t i, j, k, m, strSize; + bool found_exclusion = false, trunc_flag = false, found; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string str, spc; + unsigned int cnt = 0; + unsigned int paren_cnt; + + string tmp = CUtil::TrimString(line); + string tmpBak = CUtil::TrimString(lineBak); + start = 0; + + // if continuation, prepend previous line for correct processing + if (strLSLOC.length() > 0) + { + found = false; + if (tmpBak[0] == '&' && tmpBak.length() > 1) + found = true; + else + { + if (line.length() > 6) + { + str = line.substr(0, 5); + if ((CUtil::CheckBlank(str) || CUtil::IsInteger(str)) && + line[5] != ' ' && line[5] != '0') + found = true; + } + } + if (found) + { + tmp = tmp.substr(1, tmp.length() - 1); + tmpBak = tmpBak.substr(1, tmpBak.length() - 1); + } + tmp = strLSLOC + tmp; + tmpBak = strLSLOCBak + tmpBak; + strLSLOC = strLSLOCBak = ""; + } + + // there may be more than 1 logical SLOC in this line + while (start < tmp.length()) + { + // check for semicolon to denote end of SLOC + end = tmp.find(";", start); + if (end == string::npos) + end = tmp.length() - 1; + else + { + // skip empty ";" + str = CUtil::TrimString(tmp.substr(start, end - start + 1)); + if (str == ";") + { + start = end + 1; + continue; + } + } + + // record nested loops + if (print_cmplx) + { + bool new_loop = false; + i = CUtil::FindKeyword(tmp, "end do", start, end, false); + if (i != string::npos) + i = string::npos; + else + i = CUtil::FindKeyword(tmp, "do", start, end, false); + if (i != string::npos) + { + // check for label after do + found = false; + if (i + 2 < end && tmp[i+2] == ' ') + { + for (j = i + 3; j <= end; j++) + { + if (tmp[j] != ' ') + { + for (k = j; k <= end; k++) + { + if (tmp[k] == ' ' || tmp[k] == ',') + break; + } + k--; + str = CUtil::TrimString(tmp.substr(j, k - j + 1)); + if (CUtil::IsInteger(str)) + { + loopEnd.push_back(str); + found = true; + } + break; + } + } + } + if (!found) + loopEnd.push_back("end do"); + new_loop = true; + } + else + { + i = CUtil::FindKeyword(tmp, "end forall", start, end, false); + if (i != string::npos) + i = string::npos; + else + i = CUtil::FindKeyword(tmp, "forall", start, end, false); + if (i != string::npos) + { + loopEnd.push_back("end forall"); + new_loop = true; + } + else if (loopEnd.size() > 0) + { + str = loopEnd.back(); + if (CUtil::FindKeyword(tmp, str, start, end, false) != string::npos) + { + loopEnd.pop_back(); + if (CUtil::IsInteger(str)) + { + // remove additional label terminators + if (loopEnd.size() > 0) + { + for (m = loopEnd.size() - 1; m > 0; m--) + { + if (loopEnd[m] == str) + loopEnd.pop_back(); + else + break; + } + if (m == 0) + { + if (loopEnd[0] == str) + loopEnd.pop_back(); + } + } + } + } + else if (loopEnd.back() == "end do") + { + if (CUtil::FindKeyword(tmp, "enddo", start, end, false) != string::npos) + loopEnd.pop_back(); + } + else if (loopEnd.back() == "end forall") + { + if (CUtil::FindKeyword(tmp, "endforall", start, end, false) != string::npos) + loopEnd.pop_back(); + } + } + } + if (new_loop) + { + if (result->cmplx_nestloop_count.size() < loopEnd.size()) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopEnd.size()-1]++; + } + } + + // check for line containing excluded keywords (don't count as LSLOC) + found_exclusion = false; + for (StringVector::iterator it = exclude_keywords.begin(); it != exclude_keywords.end(); it++) + { + i = CUtil::FindKeyword(tmp, (*it), start, end, false); + if (i != string::npos) + { + found_exclusion = true; + + // exceptions + if ((*it) == "else") + { + // make sure not else if + if (CUtil::FindKeyword(tmp, "else if", i, i + 6, false) == string::npos) + break; + } + if ((*it) == "elsewhere") + { + // make sure elsewhere does not have condition + str = CUtil::TrimString(tmp.substr(i + 9, end - (i + 9) + 1)); + if (str[0] != '(') + break; + } + else + break; + } + } + if (found_exclusion) + { + strLSLOC = strLSLOCBak = ""; + phys_exec_lines++; + temp_lines = 0; + return; + } + + // check for inline if, where, forall + found = false; + i = CUtil::FindKeyword(tmp, "if", start, end, false); + if (i != string::npos) + { + if (CUtil::FindKeyword(tmp, "then", start, end, false) == string::npos) + found = true; + } + if (!found) + { + i = CUtil::FindKeyword(tmp, "where", start, end, false); + if (i != string::npos) + found = true; + } + if (!found) + { + i = CUtil::FindKeyword(tmp, "forall", start, end, false); + if (i != string::npos) + found = true; + } + if (found) + { + // check if in-line action exists after if statement (past ()) + found = false; + paren_cnt = 0; + for (j = i + 2; j <= end; j++) + { + if (tmp[j] == '(') + { + found = true; + paren_cnt++; + } + else if (tmp[j] == ')' && paren_cnt > 0) + paren_cnt--; + if (found && paren_cnt == 0) + { + if (j < end) + { + str = CUtil::TrimString(tmp.substr(j + 1, end - j + 1)); + if (!CUtil::CheckBlank(str) && str != ";" && str != "&" && !fixed_continue) + { + // save LSLOC for if statement, then process in-line action + strSize = CUtil::TruncateLine(j - start + 1, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += CUtil::TrimString(tmp.substr(start, strSize)); + strLSLOCBak += CUtil::TrimString(tmpBak.substr(start, strSize)); + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + strLSLOC = strLSLOCBak = ""; + start = j + 1; + } + } + break; + } + } + } + + // check for fixed continuation or free continuation (&) + if (tmp[end] == '&' || (fixed_continue && end >= tmp.length() - 1)) + { + // strip off trailing (&) + if (tmp[end] == '&') + strSize = CUtil::TruncateLine(end - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + else + strSize = CUtil::TruncateLine(end - start + 1, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + spc = ""; + str = tmp.substr(start, strSize); + for (m = str.length() - 1; m > 0; m--) + { + if (str[m] == ' ') + spc += " "; + else + break; + } + if (m == 0) + { + if (str[0] == ' ') + spc += " "; + } + strLSLOC += CUtil::TrimString(tmp.substr(start, strSize)) + spc; + strLSLOCBak += CUtil::TrimString(tmpBak.substr(start, strSize)) + spc; + } + start = end + 1; + + // make sure that we are not beginning to process a new data line + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", NULL, false); + + if (cnt > 0) + data_continue = true; + if (data_continue == true) + temp_lines++; + if (temp_lines == 0 && phys_data_lines == 0 && phys_exec_lines == 0) + phys_exec_lines = 1; + } + else + { + // save LSLOC + if (tmp[end] == ';') + strSize = CUtil::TruncateLine(end - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + else + strSize = CUtil::TruncateLine(end - start + 1, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += CUtil::TrimString(tmp.substr(start, strSize)); + strLSLOCBak += CUtil::TrimString(tmpBak.substr(start, strSize)); + } + start = end + 1; + if (strLSLOCBak.length() > 0) + { + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + { + // add a logical SLOC + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", &result->data_name_count, false); + + temp_lines++; + if (data_continue == true || cnt > 0) + { + result->data_lines[LOG]++; + phys_data_lines = temp_lines; + } + else + { + result->exec_lines[LOG]++; + phys_exec_lines = temp_lines; + } + } + else if (data_continue == true) + phys_data_lines = temp_lines; + else + phys_exec_lines = temp_lines; + } + data_continue = false; + temp_lines = 0; + strLSLOC = strLSLOCBak = ""; + } + } +} + +/*! +* Parses lines for function/method names. +* +* \param line line to be processed +* \param lastline last line processed +* \param functionStack stack of functions +* \param functionName function name found +* \param functionCount function count found +* +* \return 1 if function name is found +*/ +int CFortranCounter::ParseFunctionName(const string &line, string &/*lastline*/, + filemap &functionStack, string &functionName, unsigned int &functionCount) +{ + string str; + size_t idx; + unsigned int fcnt; + + idx = CUtil::FindKeyword(line, "PROGRAM"); + if (idx != string::npos) + { + if (idx + 8 < line.length()) + { + str = line.substr(idx + 8); + lineElement element(++functionCount, str); + functionStack.push_back(element); + } + } + else + { + idx = CUtil::FindKeyword(line, "SUBROUTINE"); + if (idx != string::npos) + { + if (idx + 11 < line.length()) + { + str = line.substr(idx + 11); + lineElement element(++functionCount, str); + functionStack.push_back(element); + } + } + } + + if (functionStack.empty()) + { + // dealing with some code out of any subroutines, it a "main" code + return 2; + } + + string temp = CUtil::ClearRedundantSpaces(line); + idx = CUtil::FindKeyword(temp, "END"); + if (idx != string::npos && temp == "END") + { + str = functionStack.back().line; + fcnt = functionStack.back().lineNumber; + functionStack.pop_back(); + idx = str.find("("); + if (idx != string::npos) + { + functionName = CUtil::ClearRedundantSpaces(str.substr(0, idx)); + functionCount = fcnt; + return 1; + } + else { + functionName = CUtil::ClearRedundantSpaces(str); + functionCount = fcnt; + return 1; + } + } + return 0; + + +} diff --git a/src/CFortranCounter.h b/src/CFortranCounter.h new file mode 100644 index 0000000..5d068c3 --- /dev/null +++ b/src/CFortranCounter.h @@ -0,0 +1,51 @@ +//! Code counter class definition for the Fortran language. +/*! +* \file CFortranCounter.h +* +* This file contains the code counter class definition for the Fortran language. +* This includes F77, F90, F95, and F03 including fixed and free formats. +*/ + +#ifndef CFortranCounter_h +#define CFortranCounter_h + +#include "CCodeCounter.h" + +//! Fortran code counter class. +/*! +* \class CFortranCounter +* +* Defines the Fortran code counter class. +*/ +class CFortranCounter : public CCodeCounter +{ +public: + CFortranCounter(); + +protected: + StringVector c_keywords; //!< List of keywords starting with 'c' with flexible formats + + virtual int PreCountProcess(filemap* fmap); + virtual int CountCommentsSLOC(filemap* fmap, results* result, filemap* fmapBak = NULL); + virtual int CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak = NULL); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak = NULL); + void LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + bool &fixed_continue, bool &data_continue, unsigned int &temp_lines, unsigned int &phys_exec_lines, + unsigned int &phys_data_lines, StringVector &loopEnd); + int ParseFunctionName(const string &line, string &lastline, filemap &functionStack, string &functionName, + unsigned int &functionCount); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CFortranCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CFortranCounter(const CFortranCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CFortranCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CFortranCounter operator=(const CFortranCounter); // Declare without implementation +}; + +#endif diff --git a/src/CHtmlCounter.cpp b/src/CHtmlCounter.cpp new file mode 100644 index 0000000..c735a04 --- /dev/null +++ b/src/CHtmlCounter.cpp @@ -0,0 +1,143 @@ +//! Code counter class methods for the HTML language. +/*! +* \file CHtmlCounter.cpp +* +* This file contains the code counter class methods for the HTML language. +*/ + +#include "CHtmlCounter.h" + +/*! +* Constructs a CHtmlCounter object. +*/ +CHtmlCounter::CHtmlCounter() +{ + classtype = HTML; + language_name = "HTML"; + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("HTML", file_extension); + + //file_extension.push_back(".*htm"); + + BlockCommentStart.push_back(""); + + exec_name_list.push_back("address"); + exec_name_list.push_back("applet"); + exec_name_list.push_back("area"); + exec_name_list.push_back("a"); + exec_name_list.push_back("base"); + exec_name_list.push_back("basefont"); + exec_name_list.push_back("big"); + exec_name_list.push_back("blockquote"); + exec_name_list.push_back("body"); + exec_name_list.push_back("br"); + exec_name_list.push_back("b"); + exec_name_list.push_back("caption"); + exec_name_list.push_back("center"); + exec_name_list.push_back("cite"); + exec_name_list.push_back("code"); + exec_name_list.push_back("dd"); + exec_name_list.push_back("dfn"); + exec_name_list.push_back("dir"); + exec_name_list.push_back("div"); + exec_name_list.push_back("dl"); + exec_name_list.push_back("dt"); + exec_name_list.push_back("em"); + exec_name_list.push_back("font"); + exec_name_list.push_back("form"); + exec_name_list.push_back("h1"); + exec_name_list.push_back("h2"); + exec_name_list.push_back("h3"); + exec_name_list.push_back("h4"); + exec_name_list.push_back("h5"); + exec_name_list.push_back("h6"); + exec_name_list.push_back("head"); + exec_name_list.push_back("hr"); + exec_name_list.push_back("html"); + exec_name_list.push_back("img"); + exec_name_list.push_back("input"); + exec_name_list.push_back("isindex"); + exec_name_list.push_back("i"); + exec_name_list.push_back("jsp"); + exec_name_list.push_back("kbd"); + exec_name_list.push_back("link"); + exec_name_list.push_back("li"); + exec_name_list.push_back("map"); + exec_name_list.push_back("menu"); + exec_name_list.push_back("meta"); + exec_name_list.push_back("ol"); + exec_name_list.push_back("option"); + exec_name_list.push_back("param"); + exec_name_list.push_back("pre"); + exec_name_list.push_back("p"); + exec_name_list.push_back("samp"); + exec_name_list.push_back("script"); + exec_name_list.push_back("select"); + exec_name_list.push_back("small"); + exec_name_list.push_back("span"); + exec_name_list.push_back("strike"); + exec_name_list.push_back("strong"); + exec_name_list.push_back("style"); + exec_name_list.push_back("sub"); + exec_name_list.push_back("sup"); + exec_name_list.push_back("table"); + exec_name_list.push_back("td"); + exec_name_list.push_back("textarea"); + exec_name_list.push_back("th"); + exec_name_list.push_back("title"); + exec_name_list.push_back("tr"); + exec_name_list.push_back("tt"); + exec_name_list.push_back("ul"); + exec_name_list.push_back("u"); + exec_name_list.push_back("var"); +} + +/*! +* Constructs a CHtmlPhpCounter object. +*/ +CHtmlPhpCounter::CHtmlPhpCounter() +{ + classtype = HTML_PHP; + language_name = "HTML/PHP"; + + file_extension.clear(); + file_extension.push_back(".*htmphp"); +} + +/*! +* Constructs a CHtmlJspCounter object. +*/ +CHtmlJspCounter::CHtmlJspCounter() +{ + classtype = HTML_JSP; + language_name = "HTML/JSP"; + + file_extension.clear(); + file_extension.push_back(".*htmjsp"); +} + +/*! +* Constructs a CHtmlAspCounter object. +*/ +CHtmlAspCounter::CHtmlAspCounter() +{ + classtype = HTML_ASP; + language_name = "HTML/ASP"; + + file_extension.clear(); + file_extension.push_back(".*htmasp"); +} + +/*! +* Constructs a CHtmlColdFusionCounter object. +*/ +CHtmlColdFusionCounter::CHtmlColdFusionCounter() +{ + classtype = HTML_CFM; + language_name = "HTML/ColdFusion"; + + file_extension.clear(); + file_extension.push_back(".*htmcfm"); +} diff --git a/src/CHtmlCounter.h b/src/CHtmlCounter.h new file mode 100644 index 0000000..0d80fec --- /dev/null +++ b/src/CHtmlCounter.h @@ -0,0 +1,133 @@ +//! Code counter class definition for the HTML language. +/*! +* \file CHtmlCounter.h +* +* This file contains the code counter class definition for the HTML language. +*/ + +#ifndef CHtmlCounter_h +#define CHtmlCounter_h + +#include "CTagCounter.h" + +//! HTML code counter class. +/*! +* \class CHtmlCounter +* +* Defines the HTML code counter class. +*/ +class CHtmlCounter : public CTagCounter +{ +public: + CHtmlCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CHtmlCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CHtmlCounter(const CHtmlCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CHtmlCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CHtmlCounter operator=(const CHtmlCounter); // Declare without implementation +}; + +//! HTML in PHP code counter class. +/*! +* \class CHtmlPhpCounter +* +* Defines the HTML in PHP code counter class. +*/ +class CHtmlPhpCounter : public CHtmlCounter +{ +public: + CHtmlPhpCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CHtmlPhpCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CHtmlPhpCounter(const CHtmlPhpCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CHtmlPhpCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CHtmlPhpCounter operator=(const CHtmlPhpCounter); // Declare without implementation +}; + +//! HTML in JSP code counter class. +/*! +* \class CHtmlJspCounter +* +* Defines the HTML in JSP code counter class. +*/ +class CHtmlJspCounter : public CHtmlCounter +{ +public: + CHtmlJspCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CHtmlJspCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CHtmlJspCounter(const CHtmlJspCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CHtmlJspCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CHtmlJspCounter operator=(const CHtmlJspCounter); // Declare without implementation +}; + +//! HTML in ASP code counter class. +/*! +* \class CHtmlAspCounter +* +* Defines the HTML in ASP code counter class. +*/ +class CHtmlAspCounter : public CHtmlCounter +{ +public: + CHtmlAspCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CHtmlAspCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CHtmlAspCounter(const CHtmlAspCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CHtmlAspCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CHtmlAspCounter operator=(const CHtmlAspCounter); // Declare without implementation +}; + +//! HTML in ColdFusion code counter class. +/*! +* \class CHtmlColdFusionCounter +* +* Defines the HTML in ColdFusion code counter class. +*/ +class CHtmlColdFusionCounter : public CHtmlCounter +{ +public: + CHtmlColdFusionCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CHtmlColdFusionCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CHtmlColdFusionCounter(const CHtmlColdFusionCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CHtmlColdFusionCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CHtmlColdFusionCounter operator=(const CHtmlColdFusionCounter); // Declare without implementation +}; + +#endif diff --git a/src/CIdlCounter.cpp b/src/CIdlCounter.cpp new file mode 100644 index 0000000..86a8db3 --- /dev/null +++ b/src/CIdlCounter.cpp @@ -0,0 +1,1018 @@ +//! Code counter class methods for the Interactive Data Language (IDL). +/*! +* \file CIdlCounter.cpp +* +* This file contains the code counter class methods for the Interactive Data Language (IDL). +*/ +#include "CIdlCounter.h" + +/*! +* Constructs a CIdlCounter object. +*/ +CIdlCounter::CIdlCounter() +{ + classtype = IDL; + language_name = "IDL"; + casesensitive = false; + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("IDL", file_extension); + /* + file_extension.push_back(".pro"); + file_extension.push_back(".sav");*/ + + QuoteStart = "\"'"; + QuoteEnd = "\"'"; + ContinueLine = "&"; + + LineCommentStart.push_back(";"); + + directive.push_back("@"); + + exec_name_list.push_back("begin"); + exec_name_list.push_back("break"); + exec_name_list.push_back("case"); + exec_name_list.push_back("common"); + exec_name_list.push_back("compile_opt"); + exec_name_list.push_back("continue"); + exec_name_list.push_back("else"); + exec_name_list.push_back("for"); + exec_name_list.push_back("foreach"); + exec_name_list.push_back("forward_function"); + exec_name_list.push_back("function"); + exec_name_list.push_back("goto"); + exec_name_list.push_back("if"); + exec_name_list.push_back("inherits"); + exec_name_list.push_back("on_ioerror"); + exec_name_list.push_back("pro"); + exec_name_list.push_back("repeat"); + exec_name_list.push_back("return"); + exec_name_list.push_back("switch"); + exec_name_list.push_back("while"); + + math_func_list.push_back("abs"); + math_func_list.push_back("exp"); + math_func_list.push_back("factorial"); + math_func_list.push_back("fft"); + math_func_list.push_back("fix"); + math_func_list.push_back("float"); + math_func_list.push_back("max"); + math_func_list.push_back("mean"); + math_func_list.push_back("min"); + math_func_list.push_back("primes"); + math_func_list.push_back("randomu"); + math_func_list.push_back("round"); + math_func_list.push_back("sqrt"); + math_func_list.push_back("total"); + + trig_func_list.push_back("acos"); + trig_func_list.push_back("asin"); + trig_func_list.push_back("atan"); + trig_func_list.push_back("cos"); + trig_func_list.push_back("cosh"); + trig_func_list.push_back("sin"); + trig_func_list.push_back("sinh"); + trig_func_list.push_back("tan"); + trig_func_list.push_back("tanh"); + + log_func_list.push_back("alog"); + log_func_list.push_back("alog10"); + + cmplx_calc_list.push_back("mod"); + cmplx_calc_list.push_back("^"); + cmplx_calc_list.push_back("++"); + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("--"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("/"); + cmplx_calc_list.push_back("#"); + cmplx_calc_list.push_back("##"); + + cmplx_cond_list.push_back("case"); + cmplx_cond_list.push_back("else"); + cmplx_cond_list.push_back("for"); + cmplx_cond_list.push_back("foreach"); + cmplx_cond_list.push_back("if"); + cmplx_cond_list.push_back("repeat"); + cmplx_cond_list.push_back("switch"); + cmplx_cond_list.push_back("while"); + + cmplx_logic_list.push_back("&&"); + cmplx_logic_list.push_back("||"); + cmplx_logic_list.push_back("~"); + cmplx_logic_list.push_back("and"); + cmplx_logic_list.push_back("not"); + cmplx_logic_list.push_back("or"); + cmplx_logic_list.push_back("xor"); + cmplx_logic_list.push_back(">"); + cmplx_logic_list.push_back("<"); + cmplx_logic_list.push_back("lt"); + cmplx_logic_list.push_back("gt"); + cmplx_logic_list.push_back("ge"); + cmplx_logic_list.push_back("le"); + cmplx_logic_list.push_back("eq"); + cmplx_logic_list.push_back("ne"); + cmplx_logic_list.push_back("##="); + cmplx_logic_list.push_back("#="); + cmplx_logic_list.push_back("*="); + cmplx_logic_list.push_back("+="); + cmplx_logic_list.push_back(".="); + cmplx_logic_list.push_back("/="); + cmplx_logic_list.push_back("<="); + cmplx_logic_list.push_back(">="); + cmplx_logic_list.push_back("^="); + cmplx_logic_list.push_back("and="); + cmplx_logic_list.push_back("or="); + cmplx_logic_list.push_back("xor="); + cmplx_logic_list.push_back(">"); + cmplx_logic_list.push_back("<"); + cmplx_logic_list.push_back("lt"); + cmplx_logic_list.push_back("gt="); + cmplx_logic_list.push_back("ge="); + cmplx_logic_list.push_back("le="); + cmplx_logic_list.push_back("eq="); + cmplx_logic_list.push_back("ne="); + + cmplx_preproc_list.push_back("@"); + + cmplx_pointer_list.push_back("->"); + cmplx_pointer_list.push_back("."); + + /*cmplx_cyclomatic_list.push_back("for"); + cmplx_cyclomatic_list.push_back("foreach"); + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("repeat"); + cmplx_cyclomatic_list.push_back("while"); + cmplx_cyclomatic_list.push_back("?");*/ + + cmplx_assign_list.push_back("="); + + statement_list.push_back("&&"); + statement_list.push_back("||"); + statement_list.push_back("~"); + statement_list.push_back(">"); + statement_list.push_back("<"); + statement_list.push_back("##="); + statement_list.push_back("#="); + statement_list.push_back("*="); + statement_list.push_back("+="); + statement_list.push_back(".="); + statement_list.push_back("/="); + statement_list.push_back("<="); + statement_list.push_back(">="); + statement_list.push_back("^="); + statement_list.push_back(">"); + statement_list.push_back("<"); + statement_list.push_back(" "); + statement_list.push_back("("); + statement_list.push_back(")"); +} + +/*! +* Perform preprocessing of file lines before counting. +* +* \param fmap list of file lines +* +* \return method status +*/ +int CIdlCounter::PreCountProcess(filemap* fmap) +{ + size_t i, j, llen; + filemap::iterator fit; + for (fit = fmap->begin(); fit != fmap->end(); fit++) + { + if (fit->line.empty()) + continue; + + // check for octal and hexadecimal constants (replace any single/double quotes with '$' if found) + llen = fit->line.length(); + for (i = 0; i < llen - 1; i++) + { + if (fit->line[i] == '\"') + { + j = i + 1; + while (isdigit(fit->line[j]) || + fit->line[j] == 'A' || fit->line[j] == 'B' || fit->line[j] == 'C' || fit->line[j] == 'D' || fit->line[j] == 'E' || fit->line[j] == 'F' || + fit->line[j] == 'a' || fit->line[j] == 'b' || fit->line[j] == 'c' || fit->line[j] == 'd' || fit->line[j] == 'e' || fit->line[j] == 'f') + { + j++; + if (j >= llen) { + break; + } + } + if (j + 1 >= llen || fit->line[j] == ' ' || fit->line[j] == 'B') // "n or "nB + { + fit->line[i] = '$'; + } + else if (fit->line[j] == 'L') + { + if (j + 1 >= llen || fit->line[j + 1] == ' ' || fit->line[j + 1] == 'L') // "nL or "nLL + { + fit->line[i] = '$'; + } + } + else if (fit->line[j] == 'U') + { + if (j + 1 >= llen || fit->line[j + 1] == ' ') // "nU + { + fit->line[i] = '$'; + } + else if (fit->line[j + 1] == 'L') + { + if (j + 2 >= llen || fit->line[j + 2] == ' ' || fit->line[j + 2] == 'L') // "nUL or "nULL + { + fit->line[i] = '$'; + } + } + } + } + else if (fit->line[i] == '\'') + { + j = i + 1; + while (isdigit(fit->line[j]) || + fit->line[j] == 'A' || fit->line[j] == 'B' || fit->line[j] == 'C' || fit->line[j] == 'D' || fit->line[j] == 'E' || fit->line[j] == 'F' || + fit->line[j] == 'a' || fit->line[j] == 'b' || fit->line[j] == 'c' || fit->line[j] == 'd' || fit->line[j] == 'e' || fit->line[j] == 'f') + { + j++; + if (j + 1 >= llen) + break; + } + if (fit->line[j] == '\'') + { + + if(j + 1 < llen) + { + + if (fit->line[j + 1] == 'O') + { + if (j + 2 >= llen || fit->line[j + 2] == ' ' || fit->line[j + 2] == 'S') // 'n'O or 'n'OS + { + fit->line[i] = '$'; + fit->line[j] = '$'; + } + else if (fit->line[j + 2] == 'L') + { + if (j + 3 >= llen || fit->line[j + 3] == ' ' || fit->line[j + 3] == 'L') // 'n'OL or 'n'OLL + { + fit->line[i] = '$'; + fit->line[j] = '$'; + } + } + else if (fit->line[j + 2] == 'U') + { + if (j + 3 >= llen || fit->line[j + 3] == ' ' || fit->line[j + 3] == 'S') // 'n'OU or 'n'OUS + { + fit->line[i] = '$'; + fit->line[j] = '$'; + } + else if (fit->line[j + 3] == 'L') + { + if (j + 4 >= llen || fit->line[j + 4] == ' ' || fit->line[j + 4] == 'L') // 'n'OUL or 'n'OULL + { + fit->line[i] = '$'; + fit->line[j] = '$'; + } + } + } + } + else if (fit->line[j + 1] == 'X') + { + if (j + 2 >= llen || fit->line[j + 2] == ' ' || fit->line[j + 2] == 'B' || fit->line[j + 2] == 'S') // 'n'X or 'n'XB or 'n'XS + { + fit->line[i] = '$'; + fit->line[j] = '$'; + } + else if (fit->line[j + 2] == 'L') + { + if (j + 3 >= llen || fit->line[j + 3] == ' ' || fit->line[j + 3] == 'L') // 'n'XL or 'n'XLL + { + fit->line[i] = '$'; + fit->line[j] = '$'; + } + } + else if (fit->line[j + 2] == 'U') + { + if (j + 3 >= llen || fit->line[j + 3] == ' ' || fit->line[j + 3] == 'S') // 'n'XU or 'n'XUS + { + fit->line[i] = '$'; + fit->line[j] = '$'; + } + else if (fit->line[j + 3] == 'L') + { + if (j + 4 >= llen || fit->line[j + 4] == ' ' || fit->line[j + 4] == 'L') // 'n'XUL or 'n'XULL + { + fit->line[i] = '$'; + fit->line[j] = '$'; + } + } + } + } + } + else + fit->line[i] = '$'; // replace trailing ' + } + } + } + } + + return 0; +} + +/*! +* Counts directive lines of code. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CIdlCounter::CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak) +{ + bool trunc_flag = false; + size_t strSize; + unsigned int cnt = 0; + string strDirLine = ""; + + filemap::iterator itfmBak = fmapBak->begin(); + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++, itfmBak++) + { + if (CUtil::CheckBlank(iter->line)) + continue; + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(" " + iter->line, directive, cnt, 1, "", "", "", &result->directive_count, casesensitive); + } + + // process include statement as a directive + if (iter->line[0] == '@') + { + result->directive_lines[PHY]++; + + strSize = CUtil::TruncateLine(itfmBak->line.length(), 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strDirLine = itfmBak->line.substr(0, strSize); + if (result->addSLOC(strDirLine, trunc_flag)) + result->directive_lines[LOG]++; + } + iter->line = ""; + } + } + return 1; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CIdlCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + bool cont_str = false; + unsigned int openBrackets = 0; + string strLSLOC = ""; + string strLSLOCBak = ""; + filemap::iterator fit, fitbak; + string line, lineBak; + StringVector loopLevel; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"; + + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + line = fit->line; + + // insert blank at the beginning (for searching keywords) + line = ' ' + line; + lineBak = ' ' + fitbak->line; + + // do not process blank lines + // blank line means blank_line/comment_line/directive + if (!CUtil::CheckBlank(line)) + { + LSLOC(result, line, lineBak, strLSLOC, strLSLOCBak, cont_str, openBrackets, loopLevel); + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count, casesensitive); + } + result->exec_lines[PHY]++; + } + } + return 1; +} + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results +* \param line processed physical line of code +* \param lineBak original physical line of code +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param cont_str continue string +* \param openBrackets number of open brackets (no matching close bracket) +* \param loopLevel nested loop level +*/ +void CIdlCounter::LSLOC(results* result, string line, string lineBak, string &strLSLOC, string &strLSLOCBak, + bool &cont_str, unsigned int &openBrackets, StringVector &loopLevel) +{ + size_t start = 0, len; + size_t i = 0, j, k, strSize; + bool trunc_flag = false; + string tmp, tmpBak, str; + + int inlineStatement = 0; + + // check exclusions/continuation + tmp = CUtil::TrimString(line); + tmpBak = CUtil::TrimString(lineBak); + if (CUtil::FindKeyword(tmp, "end", 0, TO_END_OF_STRING, casesensitive) == 0 || + CUtil::FindKeyword(tmp, "endforeach", 0, TO_END_OF_STRING, casesensitive) == 0 || + CUtil::FindKeyword(tmp, "endfor", 0, TO_END_OF_STRING, casesensitive) == 0 || + CUtil::FindKeyword(tmp, "endwhile", 0, TO_END_OF_STRING, casesensitive) == 0 || + CUtil::FindKeyword(tmp, "endelse", 0, TO_END_OF_STRING, casesensitive) == 0 || + CUtil::FindKeyword(tmp, "endif", 0, TO_END_OF_STRING, casesensitive) == 0 || + CUtil::FindKeyword(tmp, "endcase", 0, TO_END_OF_STRING, casesensitive) == 0 || + CUtil::FindKeyword(tmp, "endswitch", 0, TO_END_OF_STRING, casesensitive) == 0) + { + if (loopLevel.size() > 0) + loopLevel.pop_back(); + return; + } + else if (CUtil::FindKeyword(tmp, "repeat", 0, TO_END_OF_STRING, casesensitive) != string::npos && + CUtil::FindKeyword(tmp, "begin", 0, TO_END_OF_STRING, casesensitive) != string::npos) + { + if (loopLevel.size() > 0) + loopLevel.pop_back(); + return; + } + else if (CUtil::FindKeyword(tmp, "else", 0, TO_END_OF_STRING, casesensitive) != string::npos && + tmp.find_first_of(":") != std::string::npos && + CUtil::FindKeyword(tmp, "begin", 0, TO_END_OF_STRING, casesensitive) != string::npos) + { + if (loopLevel.size() > 0) + loopLevel.pop_back(); + return; + } + else if (CUtil::FindKeyword(tmp, "of", 0, TO_END_OF_STRING, casesensitive) == 0 ) + { + strLSLOC += tmp + " "; + strLSLOCBak += tmpBak + " "; + return; + } + // process nested loops + if (print_cmplx) + { + if (CUtil::FindKeyword(tmp, "for", 0, TO_END_OF_STRING, casesensitive) != string::npos || + CUtil::FindKeyword(tmp, "while", 0, TO_END_OF_STRING, casesensitive) != string::npos || + CUtil::FindKeyword(tmp, "foreach", 0, TO_END_OF_STRING, casesensitive) != string::npos || + CUtil::FindKeyword(tmp, "goto", 0, TO_END_OF_STRING, casesensitive) != string::npos || + CUtil::FindKeyword(tmp, "break", 0, TO_END_OF_STRING, casesensitive) != string::npos || + CUtil::FindKeyword(tmp, "continue", 0, TO_END_OF_STRING, casesensitive) != string::npos || + CUtil::FindKeyword(tmp, "until", 0, TO_END_OF_STRING, casesensitive) != string::npos) + { + loopLevel.push_back("loop"); + + // record nested loop level + unsigned int loopCnt = 0; + for (StringVector::iterator lit = loopLevel.begin(); lit < loopLevel.end(); lit++) + { + if ((*lit) != "") + loopCnt++; + } + if ((unsigned int)result->cmplx_nestloop_count.size() < loopCnt) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopCnt - 1]++; + } + else if (CUtil::FindKeyword(tmp, "if", 0, TO_END_OF_STRING, casesensitive) != string::npos || + CUtil::FindKeyword(tmp, "case", 0, TO_END_OF_STRING, casesensitive) != string::npos || + CUtil::FindKeyword(tmp, "switch", 0, TO_END_OF_STRING, casesensitive) != string::npos) + loopLevel.push_back(""); + } + + if (CUtil::FindKeyword(tmp, "do", 0, TO_END_OF_STRING, casesensitive) != string::npos && + CUtil::FindKeyword(tmp, "begin", 0, TO_END_OF_STRING, casesensitive) == string::npos) + { + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + strLSLOCBak += tmpBak.substr(0, strSize); + + j = CUtil::ToLower(strLSLOC).find("do"); + if (j != string::npos) + { + result->addSLOC(strLSLOCBak.substr(0, j + 2), trunc_flag); // sloc before and including "do" + result->exec_lines[LOG]++; + + result->addSLOC(strLSLOCBak.substr(j + 2), trunc_flag); // sloc past "do" + result->exec_lines[LOG]++; + + strLSLOC = strLSLOCBak = ""; + cont_str = false; + inlineStatement = 1; + } + } + } + else if (CUtil::FindKeyword(tmp, "do", 0, TO_END_OF_STRING, casesensitive) != string::npos && + CUtil::FindKeyword(tmp, "begin", 0, TO_END_OF_STRING, casesensitive) != string::npos) + { + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + strLSLOCBak += tmpBak.substr(0, strSize); + + j = CUtil::ToLower(strLSLOC).find("do"); + if (j != string::npos) + { + result->addSLOC(strLSLOCBak.substr(0, j + 2), trunc_flag); // sloc before and including "do" + result->exec_lines[LOG]++; + + string next_str = strLSLOCBak.substr(j + 2); // sloc past "do" and ending after "begin" + + if (CUtil::FindKeyword(next_str, "else", 0, TO_END_OF_STRING, casesensitive) != string::npos) + { + size_t found_then = CUtil::ToLower(next_str).find("then"); + if (found_then != string::npos) + { + result->addSLOC(next_str.substr(0, found_then + 4), trunc_flag); // sloc before and including "then" + result->exec_lines[LOG]++; + + size_t found_else = CUtil::ToLower(next_str).find("else"); + result->addSLOC(next_str.substr(found_then + 4, found_else- found_then -4), trunc_flag); // sloc past "then" and before "else" + result->exec_lines[LOG]++; + + strLSLOC = strLSLOCBak = next_str = ""; + cont_str = false; + inlineStatement = 1; + } + } + else + { + string next_str_1 = strLSLOCBak.substr(j + 2); + if (CUtil::FindKeyword(next_str_1, "begin", 0, TO_END_OF_STRING, casesensitive) == string::npos) // no "begin" + { + result->addSLOC(strLSLOCBak.substr(j + 2), trunc_flag); // sloc past "do" with no "else" + result->exec_lines[LOG]++; + } + else // "begin" exists + { + size_t found_begin = CUtil::ToLower(next_str_1).find("begin"); + string next_str_2 = next_str_1.substr(0, found_begin); + if(next_str_2.find_first_not_of(' ') != std::string::npos) + { + // There's a non-space. + result->addSLOC(strLSLOCBak.substr(j + 2), trunc_flag); // sloc past "do" with no "else" + result->exec_lines[LOG]++; + } + } + } + + strLSLOC = strLSLOCBak = ""; + cont_str = false; + inlineStatement = 1; + } + } + } + + if (CUtil::FindKeyword(tmp, "do", 0, TO_END_OF_STRING, casesensitive) != string::npos && + CUtil::FindKeyword(tmp, "begin", 0, TO_END_OF_STRING, casesensitive) == string::npos && + inlineStatement != 1) + { + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + strLSLOCBak += tmpBak.substr(0, strSize); + + j = CUtil::ToLower(strLSLOC).find("do"); + if (j != string::npos) + { + result->addSLOC(strLSLOCBak.substr(0, j + 2), trunc_flag); // sloc before and including "do" + result->exec_lines[LOG]++; + + result->addSLOC(strLSLOCBak.substr(j + 2), trunc_flag); // sloc past "do" + result->exec_lines[LOG]++; + + strLSLOC = strLSLOCBak = ""; + cont_str = false; + inlineStatement = 1; + } + } + } + + if (CUtil::FindKeyword(tmp, "begin", 0, TO_END_OF_STRING, casesensitive) != string::npos && + tmp.find_first_of(":") != std::string::npos && + inlineStatement != 1) + { + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + strLSLOCBak += tmpBak.substr(0, strSize); + + j = strLSLOC.find(":"); + if (j != string::npos) + { + string temp_string = strLSLOCBak.substr(0, j); // sloc before ":" + for (vector::const_iterator i = statement_list.begin(); i != statement_list.end(); ++i) + { + std::size_t found = temp_string.find(*i); + if (found != string::npos) + { + result->addSLOC(temp_string, trunc_flag); + result->exec_lines[LOG]++; + strLSLOC = strLSLOCBak = temp_string = ""; + cont_str = false; + break; + } + } + inlineStatement = 1; + } + } + } + if (CUtil::FindKeyword(tmp, "begin", 0, TO_END_OF_STRING, casesensitive) == string::npos && + CUtil::FindKeyword(tmp, "else", 0, TO_END_OF_STRING, casesensitive) == string::npos && + tmp.find_first_of(":") != std::string::npos && + tmp.find_first_of("?") == std::string::npos && + inlineStatement != 1) + { + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + strLSLOCBak += tmpBak.substr(0, strSize); + + j = strLSLOC.find(":"); + if (j != string::npos) + { + string temp_string_before = strLSLOCBak.substr(0, j); // sloc before ":" + for (vector::const_iterator i = statement_list.begin(); i != statement_list.end(); ++i) + { + std::size_t found = temp_string_before.find(*i); + if (found != std::string::npos) + { + result->addSLOC(temp_string_before, trunc_flag); + result->exec_lines[LOG]++; + break; + } + } + + result->addSLOC(strLSLOCBak.substr(j + 1), trunc_flag); // sloc after ":" to the end + result->exec_lines[LOG]++; + + strLSLOC = strLSLOCBak = temp_string_before = ""; + cont_str = false; + inlineStatement = 1; + } + } + } + if (CUtil::FindKeyword(tmp, "begin", 0, TO_END_OF_STRING, casesensitive) == string::npos && + CUtil::FindKeyword(tmp, "else", 0, TO_END_OF_STRING, casesensitive) == string::npos && + tmp.find_first_of(":") != std::string::npos && + tmp.find_first_of("?") != std::string::npos && + inlineStatement != 1) + { + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + strLSLOCBak += tmpBak.substr(0, strSize); + + j = strLSLOC.find("?"); + if (j != string::npos) + { + string temp_string_before = strLSLOCBak.substr(0, j+1); // sloc before "?" + for (vector::const_iterator i = statement_list.begin(); i != statement_list.end(); ++i) + { + std::size_t found = temp_string_before.find(*i); + if (found != std::string::npos) + { + result->addSLOC(temp_string_before, trunc_flag); + result->exec_lines[LOG]++; + break; + } + } + + string temp_string_after = strLSLOCBak.substr(j + 1); + k = strLSLOC.find(":"); + + result->addSLOC(strLSLOCBak.substr(j + 1, k - j - 1), trunc_flag); // sloc after "?" to ":" + result->exec_lines[LOG]++; + + result->addSLOC(strLSLOCBak.substr(k), trunc_flag); // sloc after ":" to the end + result->exec_lines[LOG]++; + + strLSLOC = strLSLOCBak = temp_string_before = temp_string_after = ""; + cont_str = false; + inlineStatement = 1; + + + } + } + } + + if (CUtil::FindKeyword(tmp, "repeat", 0, TO_END_OF_STRING, casesensitive) != string::npos && + CUtil::FindKeyword(tmp, "until", 0, TO_END_OF_STRING, casesensitive) != string::npos && + CUtil::FindKeyword(tmp, "begin", 0, TO_END_OF_STRING, casesensitive) == string::npos && + inlineStatement != 1) + { + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + strLSLOCBak += tmpBak.substr(0, strSize); + + j = CUtil::ToLower(strLSLOC).find("until"); + if (j != string::npos) + { + result->addSLOC(strLSLOCBak.substr(0, j), trunc_flag); // sloc before "until" + result->exec_lines[LOG]++; + + result->addSLOC(strLSLOCBak.substr(j), trunc_flag); // sloc from "until" to the end + result->exec_lines[LOG]++; + + strLSLOC = strLSLOCBak = ""; + cont_str = false; + inlineStatement = 1; + } + } + } + + if (CUtil::FindKeyword(tmp, "then", 0, TO_END_OF_STRING, casesensitive) != string::npos && + CUtil::FindKeyword(tmp, "begin", 0, TO_END_OF_STRING, casesensitive) == string::npos && + CUtil::FindKeyword(tmp, "else", 0, TO_END_OF_STRING, casesensitive) == string::npos && + inlineStatement != 1 && + tmp.substr(tmp.length() - 1, 1) != "$") + { + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + strLSLOCBak += tmpBak.substr(0, strSize); + + size_t found_then = CUtil::ToLower(strLSLOC).find("then"); + if (found_then != string::npos) + { + result->addSLOC(strLSLOCBak.substr(0, found_then + 4), trunc_flag); // sloc before and including "then" + result->exec_lines[LOG]++; + + result->addSLOC(strLSLOCBak.substr(found_then + 4), trunc_flag); // sloc past "then" + result->exec_lines[LOG]++; + + strLSLOC = strLSLOCBak = ""; + cont_str = false; + inlineStatement = 1; + } + } + } + + if (CUtil::FindKeyword(tmp, "then", 0, TO_END_OF_STRING, casesensitive) != string::npos && + CUtil::FindKeyword(tmp, "else", 0, TO_END_OF_STRING, casesensitive) != string::npos && + CUtil::FindKeyword(tmp, "begin", 0, TO_END_OF_STRING, casesensitive) == string::npos && + inlineStatement != 1) + { + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + strLSLOCBak += tmpBak.substr(0, strSize); + + str = CUtil::ToLower(strLSLOC); + size_t found_then = str.find("then"); + if (found_then != string::npos) + { + result->addSLOC(strLSLOCBak.substr(0, found_then + 4), trunc_flag); // sloc before and including "then" + result->exec_lines[LOG]++; + + size_t found_else = str.find("else"); + result->addSLOC(strLSLOCBak.substr(found_then + 4, found_else - found_then - 4), trunc_flag); // sloc past "then" and before "else" + result->exec_lines[LOG]++; + + result->addSLOC(strLSLOCBak.substr(found_else), trunc_flag); // sloc from "else" to the end + result->exec_lines[LOG]++; + + strLSLOC = strLSLOCBak = ""; + cont_str = false; + inlineStatement = 1; + } + } + } + + if (CUtil::FindKeyword(tmp, "then", 0, TO_END_OF_STRING, casesensitive) != string::npos && + CUtil::FindKeyword(tmp, "else", 0, TO_END_OF_STRING, casesensitive) != string::npos && + CUtil::FindKeyword(tmp, "begin", 0, TO_END_OF_STRING, casesensitive) != string::npos && + inlineStatement != 1) + { + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + strLSLOCBak += tmpBak.substr(0, strSize); + + str = CUtil::ToLower(strLSLOC); + size_t found_then = str.find("then"); + if (found_then != string::npos) + { + result->addSLOC(strLSLOCBak.substr(0, found_then + 4), trunc_flag); // sloc before and including "then" + result->exec_lines[LOG]++; + + size_t found_else = str.find("else"); + result->addSLOC(strLSLOCBak.substr(found_then + 4, found_else- found_then -4), trunc_flag); // sloc past "then" and before "else" + result->exec_lines[LOG]++; + + strLSLOC = strLSLOCBak = ""; + cont_str = false; + inlineStatement = 1; + } + } + } + + // there may be more than 1 logical SLOC in this line + while (i < line.length()) + { + switch (line[i]) + { + case '&': case '\n': // LSLOC terminators + { + if (openBrackets > 0) + { + i++; + continue; + } + tmp = CUtil::TrimString(line.substr(start, i - start + 1)); + tmpBak = CUtil::TrimString(lineBak.substr(start, i - start + 1)); + + if (cont_str && strLSLOC.length() > 0) + { + // check for string continuation + if (tmp[0] == '$') + { + tmp = tmp.substr(1, tmp.length() - 1); + tmpBak = tmpBak.substr(1, tmpBak.length() - 1); + } + } + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + strLSLOCBak += tmpBak.substr(0, strSize); + } + + std::size_t found = strLSLOCBak.find("&"); + + if (found!=std::string::npos) + { + result->addSLOC(strLSLOCBak.substr(0, found), trunc_flag); // sloc before "&" + result->exec_lines[LOG]++; + } + else + { + result->addSLOC(strLSLOCBak, trunc_flag); + result->exec_lines[LOG]++; + } + + strLSLOC = strLSLOCBak = ""; + cont_str = false; + start = i + 1; + break; + } + case '[': case '(': case '{': + openBrackets++; + break; + case ']': case ')': case '}': + openBrackets--; + break; + } + i++; + } + + // check for line continuation + tmp = CUtil::TrimString(line.substr(start, i - start)); + tmpBak = CUtil::TrimString(lineBak.substr(start, i - start)); + if (tmp.length() > 3 && tmp.substr(tmp.length() - 1, 1) == "$") + { + // strip off trailing ($) + tmp = tmp.substr(0, tmp.length() - 1); + tmpBak = tmpBak.substr(0, tmpBak.length() - 1); + len = tmp.length(); + + strSize = CUtil::TruncateLine(len, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + if (cont_str) + { + strLSLOC += CUtil::TrimString(tmp.substr(0, strSize), -1); + strLSLOCBak += CUtil::TrimString(tmpBak.substr(0, strSize), -1); + } + else + { + strLSLOC += CUtil::TrimString(tmp.substr(0, strSize)) + " "; + strLSLOCBak += CUtil::TrimString(tmpBak.substr(0, strSize)) + " "; + } + } + } + else if (inlineStatement == 0) + { + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + strLSLOCBak += tmpBak.substr(0, strSize); + + result->addSLOC(strLSLOCBak, trunc_flag); + result->exec_lines[LOG]++; + strLSLOC = strLSLOCBak = ""; + cont_str = false; + } + } + inlineStatement = 0; + +} + +/*! +* Parses lines for function/method names. +* +* \param line line to be processed +* \param functionStack stack of functions +* \param functionName function name found +* \param functionCount function count found +* +* \return 1 if function name is found +*/ +int CIdlCounter::ParseFunctionName(const string &line, string &/*lastline*/, + filemap &functionStack, string &functionName, unsigned int &functionCount) +{ + string str; + size_t idx; + unsigned int fcnt; + + idx = CUtil::FindKeyword(line, "pro", 0, TO_END_OF_STRING, false); + if (idx != string::npos) + { + if (idx + 4 < line.length()) + { + str = line.substr(idx + 4); + str += ","; // append "," so that a pro/function with no inputs is counted + lineElement element(++functionCount, str); + functionStack.push_back(element); + } + } + else + { + idx = CUtil::FindKeyword(line, "function", 0, TO_END_OF_STRING, false); + if (idx != string::npos) + { + if (idx + 9 < line.length()) + { + str = line.substr(idx + 9); + str += ","; // append "," so that a pro/function with no inputs is counted + lineElement element(++functionCount, str); + functionStack.push_back(element); + } + } + } + + if (functionStack.empty()) + { + // dealing with some code out of any subroutines, it a "main" code + return 2; + } + + idx = CUtil::FindKeyword(line, "end", 0, TO_END_OF_STRING, false); + if (idx != string::npos) + { + str = functionStack.back().line; + fcnt = functionStack.back().lineNumber; + functionStack.pop_back(); + idx = str.find(","); + if (idx != string::npos) + { + functionName = CUtil::ClearRedundantSpaces(str.substr(0, idx)); + functionCount = fcnt; + return 1; + } + } + return 0; +} + diff --git a/src/CIdlCounter.h b/src/CIdlCounter.h new file mode 100644 index 0000000..6ad0ceb --- /dev/null +++ b/src/CIdlCounter.h @@ -0,0 +1,48 @@ +//! Code counter class definition for the Interactive Data Language (IDL). +/*! +* \file CIdlCounter.h +* +* This file contains the code counter class definition for the Interactive Data Language (IDL). +*/ + +#ifndef CIdlCounter_h +#define CIdlCounter_h + +#include "CCodeCounter.h" + +//! IDL code counter class. +/*! +* \class CIdlCounter +* +* Defines the IDL code counter class. +*/ +class CIdlCounter : public CCodeCounter +{ +public: + CIdlCounter(); + +protected: + virtual int PreCountProcess(filemap* fmap); + virtual int CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak = NULL); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak = NULL); + void LSLOC(results* result, string line, string lineBak, string &strLSLOC, string &strLSLOCBak, + bool &cont_str, unsigned int &openBrackets, StringVector &loopLevel); + int ParseFunctionName(const string &line, string &lastline, + filemap &functionStack, string &functionName, unsigned int &functionCount); + + StringVector statement_list; + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CIdlCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CIdlCounter(const CIdlCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CIdlCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CIdlCounter operator=(const CIdlCounter); // Declare without implementation +}; + +#endif diff --git a/src/CJavaCounter.cpp b/src/CJavaCounter.cpp new file mode 100644 index 0000000..53d292d --- /dev/null +++ b/src/CJavaCounter.cpp @@ -0,0 +1,184 @@ +//! Code counter class methods for the Java language. +/*! +* \file CJavaCounter.cpp +* +* This file contains the code counter class methods for the Java language. +*/ + +#include "CJavaCounter.h" + +/*! +* Constructs a CJavaCounter object. +*/ +CJavaCounter::CJavaCounter( string lang ) : CCJavaCsScalaCounter( lang ) +{ + classtype = JAVA; + language_name = "Java"; + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("Java", file_extension); + + //file_extension.push_back(".java"); + + directive.push_back("import"); + directive.push_back("package"); + + data_name_list.push_back("abstract"); + data_name_list.push_back("ArrayList"); + data_name_list.push_back("boolean"); + data_name_list.push_back("byte"); + data_name_list.push_back("char"); + data_name_list.push_back("class"); + data_name_list.push_back("double"); + data_name_list.push_back("extends"); + data_name_list.push_back("float"); + data_name_list.push_back("HashMap"); + data_name_list.push_back("HashSet"); + data_name_list.push_back("implements"); + data_name_list.push_back("int"); + data_name_list.push_back("interface"); + data_name_list.push_back("LinkedHashMap"); + data_name_list.push_back("LinkedList"); + data_name_list.push_back("long"); + data_name_list.push_back("native"); + data_name_list.push_back("private"); + data_name_list.push_back("protected"); + data_name_list.push_back("public"); + data_name_list.push_back("short"); + data_name_list.push_back("static"); + data_name_list.push_back("String"); + data_name_list.push_back("TreeMap"); + data_name_list.push_back("Vector"); + data_name_list.push_back("void"); + data_name_list.push_back("volatile"); + + exec_name_list.push_back("break"); + exec_name_list.push_back("case"); + exec_name_list.push_back("catch"); + exec_name_list.push_back("continue"); + exec_name_list.push_back("default"); + exec_name_list.push_back("do"); + exec_name_list.push_back("else"); + exec_name_list.push_back("finally"); + exec_name_list.push_back("for"); + exec_name_list.push_back("if"); + exec_name_list.push_back("new"); + exec_name_list.push_back("return"); + exec_name_list.push_back("super"); + exec_name_list.push_back("switch"); + exec_name_list.push_back("this"); + exec_name_list.push_back("throw"); + exec_name_list.push_back("throws"); + exec_name_list.push_back("try"); + exec_name_list.push_back("while"); + + math_func_list.push_back("Math.abs"); + math_func_list.push_back("Math.cbrt"); + math_func_list.push_back("Math.ceil"); + math_func_list.push_back("Math.copySign"); + math_func_list.push_back("Math.E"); + math_func_list.push_back("Math.exp"); + math_func_list.push_back("Math.expm1"); + math_func_list.push_back("Math.floor"); + math_func_list.push_back("Math.getExponent"); + math_func_list.push_back("Math.hypot"); + math_func_list.push_back("Math.IEEEremainder"); + math_func_list.push_back("Math.max"); + math_func_list.push_back("Math.min"); + math_func_list.push_back("Math.nextAfter"); + math_func_list.push_back("Math.nextUp"); + math_func_list.push_back("Math.PI"); + math_func_list.push_back("Math.pow"); + math_func_list.push_back("Math.random"); + math_func_list.push_back("Math.rint"); + math_func_list.push_back("Math.round"); + math_func_list.push_back("Math.scalb"); + math_func_list.push_back("Math.signum"); + math_func_list.push_back("Math.sqrt"); + math_func_list.push_back("Math.toRadians"); + math_func_list.push_back("Math.toDegrees"); + math_func_list.push_back("Math.ulp"); + + trig_func_list.push_back("Math.acos"); + trig_func_list.push_back("Math.asin"); + trig_func_list.push_back("Math.atan"); + trig_func_list.push_back("Math.atan2"); + trig_func_list.push_back("Math.cos"); + trig_func_list.push_back("Math.cosh"); + trig_func_list.push_back("Math.sin"); + trig_func_list.push_back("Math.sinh"); + trig_func_list.push_back("Math.tan"); + trig_func_list.push_back("Math.tanh"); + + log_func_list.push_back("Math.log"); + log_func_list.push_back("Math.log10"); + log_func_list.push_back("Math.log1p"); + + //Modification: 2018.01 Integration starts + three_char_operator_list.push_back(">>>"); + + for (StringVector::iterator it = cmplx_cyclomatic_list.begin(); it != cmplx_cyclomatic_list.end(); it++) + keyword_operators.insert(*it); + + for (StringVector::iterator it = directive.begin(); it != directive.end(); it++) + keyword_operators.insert(*it); + + for (StringVector::iterator it = data_name_list.begin(); it != data_name_list.end(); it++) + keyword_operators.insert(*it); + + for (StringVector::iterator it = exec_name_list.begin(); it != exec_name_list.end(); it++) + keyword_operators.insert(*it); + + + //Modification: 2018.01 Integration ends + +/* Below Set in CCJavaCsScalaCounter base class + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("case"); + cmplx_cyclomatic_list.push_back("while"); + cmplx_cyclomatic_list.push_back("for"); + cmplx_cyclomatic_list.push_back("catch"); + cmplx_cyclomatic_list.push_back("?"); + + cmplx_cyclomatic_logic_list.push_back("||"); + cmplx_cyclomatic_logic_list.push_back("&&"); + //cmplx_cyclomatic_logic_list.push_back("^"); + + cmplx_cyclomatic_case_list.push_back("case"); + cmplx_cyclomatic_switch_list.push_back("switch"); +*/ +} + +/*! +* Constructs a CJavaJspCounter object. +*/ +CJavaJspCounter::CJavaJspCounter() +{ + classtype = JAVA_JSP; + language_name = "Java/JSP"; + + file_extension.clear(); + file_extension.push_back(".*java"); +} + +//Modification: 2018.01 Integration starts +/*! +* Returns the current line. Since Java does not support multiline strings, +* a source line will never be part of a multiline string (unlike in C++). +* +* \param curr_line_idx the index of the line in fmap from which to begin +* \param line the concatenation of all source lines beginning from curr_line_idx until there is no multistring +* \param fmap the source code for the file +* \param nonfunction_operator_counts the map for counting the number of times a nonfunction operator (e.g. symbolic operators such as '+' and '[') +* +* \return the index into fmap at which the first physical line, at or after curr_line_idx, does not continue to the next source line as a multiline string +*/ +int CJavaCounter::GetLineUntilEndOfMultistringIfAny(int curr_line_idx, string &line, filemap &fmap, map &nonfunction_operator_counts) { + //Modification: 2018.05: Unused Variables warnings + (void)line; + (void)fmap; + (void)nonfunction_operator_counts; + return curr_line_idx; +} + +//Modification: 2018.01 Integration ends diff --git a/src/CJavaCounter.h b/src/CJavaCounter.h new file mode 100644 index 0000000..26d7eb6 --- /dev/null +++ b/src/CJavaCounter.h @@ -0,0 +1,63 @@ +//! Code counter class definition for the Java language. +/*! +* \file CJavaCounter.h +* +* This file contains the code counter class definition for the Java language. +*/ + +#ifndef CJavaCounter_h +#define CJavaCounter_h + +#include "CCJavaCsScalaCounter.h" + +//! Java code counter class. +/*! +* \class CJavaCounter +* +* Defines the Java code counter class. +*/ +class CJavaCounter : public CCJavaCsScalaCounter +{ +public: + // Set the language so base class constructors set values as needed + CJavaCounter( string lang = "JAVA" ); + int GetLineUntilEndOfMultistringIfAny(int curr_line_idx, string &line, filemap &fmap, map &nonfunction_operator_counts); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CJavaCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CJavaCounter(const CJavaCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CJavaCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CJavaCounter operator=(const CJavaCounter); // Declare without implementation +}; + +//! Java in JSP code counter class. +/*! +* \class CJavaJspCounter +* +* Defines the Java in JSP code counter class. +*/ +class CJavaJspCounter : public CJavaCounter +{ +public: + CJavaJspCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CJavaJspCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CJavaJspCounter(const CJavaJspCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CJavaJspCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CJavaJspCounter operator=(const CJavaJspCounter); // Declare without implementation +}; + +#endif diff --git a/src/CJavascriptCounter.cpp b/src/CJavascriptCounter.cpp new file mode 100644 index 0000000..360c2a4 --- /dev/null +++ b/src/CJavascriptCounter.cpp @@ -0,0 +1,1045 @@ +//! Code counter class methods for the JavaScript language. +/*! +* \file CJavascriptCounter.cpp +* +* This file contains the code counter class methods for the JavaScript language. +*/ + +#include "CJavascriptCounter.h" + +/*! +* Constructs a CJavascriptCounter object. +*/ +CJavascriptCounter::CJavascriptCounter() +{ + classtype = JAVASCRIPT; + language_name = "JavaScript"; + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("JavaScript", file_extension); + + //file_extension.push_back(".js"); + + QuoteStart = "\"'/"; + QuoteEnd = "\"'/"; + QuoteEscapeFront = '\\'; + LineCommentStart.push_back("//"); + BlockCommentStart.push_back("/*"); + BlockCommentEnd.push_back("*/"); + + data_name_list.push_back("abstract"); + data_name_list.push_back("boolean"); + data_name_list.push_back("byte"); + data_name_list.push_back("char"); + data_name_list.push_back("class"); + data_name_list.push_back("double"); + data_name_list.push_back("enum"); + data_name_list.push_back("float"); + data_name_list.push_back("implements"); + data_name_list.push_back("instanceOf"); + data_name_list.push_back("int"); + data_name_list.push_back("interface"); + data_name_list.push_back("long"); + data_name_list.push_back("private"); + data_name_list.push_back("protected"); + data_name_list.push_back("public"); + data_name_list.push_back("short"); + data_name_list.push_back("static"); + data_name_list.push_back("void"); + + exec_name_list.push_back("alert"); + exec_name_list.push_back("arguments"); + exec_name_list.push_back("assign"); + exec_name_list.push_back("break"); + exec_name_list.push_back("case"); + exec_name_list.push_back("catch"); + exec_name_list.push_back("close"); + exec_name_list.push_back("comment"); + exec_name_list.push_back("constructor"); + exec_name_list.push_back("continue"); + exec_name_list.push_back("default"); + exec_name_list.push_back("debugger"); + exec_name_list.push_back("delete"); + exec_name_list.push_back("do"); + exec_name_list.push_back("else"); + exec_name_list.push_back("escape"); + exec_name_list.push_back("eval"); + exec_name_list.push_back("export"); + exec_name_list.push_back("extends"); + exec_name_list.push_back("false"); + exec_name_list.push_back("find"); + exec_name_list.push_back("final"); + exec_name_list.push_back("finally"); + exec_name_list.push_back("focus"); + exec_name_list.push_back("for"); + exec_name_list.push_back("function"); + exec_name_list.push_back("if"); + exec_name_list.push_back("import"); + exec_name_list.push_back("label"); + exec_name_list.push_back("length"); + exec_name_list.push_back("location"); + exec_name_list.push_back("native"); + exec_name_list.push_back("new"); + exec_name_list.push_back("null"); + exec_name_list.push_back("open"); + exec_name_list.push_back("package"); + exec_name_list.push_back("print"); + exec_name_list.push_back("prompt"); + exec_name_list.push_back("prototype"); + exec_name_list.push_back("ref"); + exec_name_list.push_back("return"); + exec_name_list.push_back("self"); + exec_name_list.push_back("status"); + exec_name_list.push_back("stop"); + exec_name_list.push_back("super"); + exec_name_list.push_back("switch"); + exec_name_list.push_back("synchronized"); + exec_name_list.push_back("taint"); + exec_name_list.push_back("this"); + exec_name_list.push_back("throw"); + exec_name_list.push_back("throws"); + exec_name_list.push_back("transient"); + exec_name_list.push_back("true"); + exec_name_list.push_back("try"); + exec_name_list.push_back("typeof"); + exec_name_list.push_back("untaint"); + exec_name_list.push_back("var"); + exec_name_list.push_back("watch"); + exec_name_list.push_back("while"); + exec_name_list.push_back("with"); + + math_func_list.push_back("abs"); + math_func_list.push_back("ceil"); + math_func_list.push_back("exp"); + math_func_list.push_back("floor"); + math_func_list.push_back("max"); + math_func_list.push_back("min"); + math_func_list.push_back("pow"); + math_func_list.push_back("random"); + math_func_list.push_back("round"); + math_func_list.push_back("sqrt"); + + trig_func_list.push_back("acos"); + trig_func_list.push_back("asin"); + trig_func_list.push_back("atan"); + trig_func_list.push_back("atan2"); + trig_func_list.push_back("cos"); + trig_func_list.push_back("sin"); + trig_func_list.push_back("tan"); + + log_func_list.push_back("log"); + + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("/"); + cmplx_calc_list.push_back("%"); + cmplx_calc_list.push_back("++"); + cmplx_calc_list.push_back("--"); + + cmplx_cond_list.push_back("case"); + cmplx_cond_list.push_back("else"); + cmplx_cond_list.push_back("else if"); + cmplx_cond_list.push_back("for"); + cmplx_cond_list.push_back("if"); + cmplx_cond_list.push_back("switch"); + cmplx_cond_list.push_back("while"); + cmplx_cond_list.push_back("?"); + + cmplx_logic_list.push_back("=="); + cmplx_logic_list.push_back("==="); + cmplx_logic_list.push_back("!="); + cmplx_logic_list.push_back("!=="); + cmplx_logic_list.push_back(">"); + cmplx_logic_list.push_back("<"); + cmplx_logic_list.push_back(">="); + cmplx_logic_list.push_back("=<"); + + cmplx_assign_list.push_back("="); + + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("case"); + cmplx_cyclomatic_list.push_back("while"); + cmplx_cyclomatic_list.push_back("for"); + cmplx_cyclomatic_list.push_back("catch"); + cmplx_cyclomatic_list.push_back("?"); + + cmplx_cyclomatic_logic_list.push_back("&&"); + cmplx_cyclomatic_logic_list.push_back("||"); + + cmplx_cyclomatic_case_list.push_back("case"); + cmplx_cyclomatic_switch_list.push_back("switch"); + +} + +/*! +* Handles special case for quote characters within regexp operators. +* +* \param strline string to be processed +* \param idx_start index of line character to start search +* \param contd specifies the quote string is continued from the previous line +* \param CurrentQuoteEnd end quote character of the current status +* +* \return method status +*/ +int CJavascriptCounter::ReplaceQuote(string &strline, size_t &idx_start, bool &contd, char &CurrentQuoteEnd) +{ + static bool inRegexp = false; + size_t idx = idx_start; + size_t i = idx; + + if (inRegexp || strline[idx] == '/') + { + if (!inRegexp) + { + // check for open parenthesis to indicate a regexp (match(//), replace(//, ""), split(//)) + while (i > 0) + { + if (strline[i-1] == '(') + break; + else if (strline[i-1] != ' ' && strline[i-1] != '\t') + { + idx_start++; + return 1; + } + i--; + } + if (i <= 0) + { + idx_start++; + return 1; + } + i = idx; + } + + // replace all "\\" by "$$" + size_t start = idx_start; + while ((start = strline.find("\\\\", start)) != string::npos) + { + strline.replace(start, 2, "$$"); + start += 2; + } + + while (i < strline.length()) + { + if (inRegexp) + { + if ((strline[i] == '/' && (i == 0 || (i > 0 && strline[i - 1] != '\\'))) + || (contd && strline[i] == ';')) + { + // replace everything in the regexp + strline.replace(idx, i - idx + 1, i - idx + 1, '$'); + inRegexp = false; + contd = false; + idx = i + 1; + idx_start = idx; + return 1; + } + } + else if (strline[i] == '/') + { + idx = i; + inRegexp = true; + } + i++; + } + + if (inRegexp) + { + strline.replace(idx, i - idx, i - idx, '$'); + contd = true; + } + } + idx_start = idx; + + if (!inRegexp) + return CCodeCounter::ReplaceQuote(strline, idx_start, contd, CurrentQuoteEnd); + + return 1; +} + +/*! +* Counts directive lines of code. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CJavascriptCounter::CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak) +{ + bool contd = false, trunc_flag = false, foundSeperatingTag = false; + size_t idx, strSize; + size_t lineNumber = 0; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string strDirLine = ""; + filemap::iterator itfmBak = fmapBak->begin(); + + for (filemap::iterator iter = fmap->begin(); iter!=fmap->end(); iter++, itfmBak++) + { + if (CUtil::CheckBlank(iter->line)) + continue; + + if(iter->lineNumber == 0) + { + if(!foundSeperatingTag) + { + lineNumber++; + foundSeperatingTag = true; + } + else + { + if((iter+1)!=fmap->end()) + { + lineNumber = (iter+1)->lineNumber - 1; + } + foundSeperatingTag = false; + } + } + else + { + lineNumber = iter->lineNumber; + } + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(" " + iter->line, directive, cnt, 1, exclude, "", "", &result->directive_count); + } + + if (!contd) + { + // if not a continuation of a previous directive + for (vector::iterator viter = directive.begin(); viter != directive.end(); viter++) + { + // ensures the keyword stands alone, avoid, e.g., #ifabc + if (((idx = CUtil::FindKeyword(iter->line, *viter)) != string::npos) && idx == 0) + { + contd = true; + break; + } + } + if (contd) + { + strSize = CUtil::TruncateLine(itfmBak->line.length(), 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine = itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + } + else + { + // continuation of a previous directive + strSize = CUtil::TruncateLine(itfmBak->line.length(), strDirLine.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine += "\n" + itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + + if (contd) + { + // drop continuation symbol + if (strDirLine[strDirLine.length()-1] == '\\') + strDirLine = strDirLine.substr(0, strDirLine.length()-1); + + // if a directive or continuation of a directive (no continuation symbol found) + if (iter->line[iter->line.length()-1] != ',' && iter->line[iter->line.length()-1] != '\\') + { + contd = false; + if (result->addSLOC(strDirLine, lineNumber, trunc_flag)) + result->directive_lines[LOG]++; + } + iter->line = ""; + } + } + return 1; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CJavascriptCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + unsigned int paren_count = 0; + bool for_flag = false; + bool found_forifwhile = false; + bool found_while = false; + char prev_char = 0; + bool data_continue = false; + bool inArrayDec = false; + string strLSLOC = ""; + string strLSLOCBak = ""; + unsigned int openBrackets = 0; + + filemap::iterator fit, fitbak; + string line, lineBak; + StringVector loopLevel; + bool foundSeperatingTag = false; + + unsigned int phys_exec_lines = 0; + unsigned int phys_data_lines = 0; + unsigned int temp_lines = 0; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + size_t lineNumber = 0; + + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + line = fit->line; + + // insert blank at the beginning (for searching keywords + line = ' ' + line; + lineBak = ' ' + fitbak->line; + + if(fit->lineNumber == 0) + { + if(!foundSeperatingTag) + { + lineNumber++; + foundSeperatingTag = true; + } + else + { + if((fit+1)!=fmap->end()) + { + lineNumber = (fit+1)->lineNumber - 1; + } + foundSeperatingTag = false; + } + } + else + { + lineNumber = fit->lineNumber; + } + + // do not process blank lines + // blank line means blank_line/comment_line/directive + if (!CUtil::CheckBlank(line)) + { + LSLOC(result, line, lineNumber, lineBak, strLSLOC, strLSLOCBak, paren_count, for_flag, found_forifwhile, found_while, + prev_char, data_continue, temp_lines, phys_exec_lines, phys_data_lines, inArrayDec, + openBrackets, loopLevel); + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count); + } + + result->exec_lines[PHY] += phys_exec_lines; + phys_exec_lines = 0; + + result->data_lines[PHY] += phys_data_lines; + phys_data_lines = 0; + } + } + return 1; +} + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results +* \param line processed physical line of code +* \param lineBak original physical line of code +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param paren_cnt count of parenthesis +* \param forflag found for flag +* \param found_forifwhile found for, if, or while flag +* \param found_while found while flag +* \param prev_char previous character +* \param data_continue continuation of a data declaration line +* \param temp_lines tracks physical line count +* \param phys_exec_lines number of physical executable lines +* \param phys_data_lines number of physical data lines +* \param inArrayDec marks an array declaration +* \param openBrackets number of open brackets (no matching close bracket) +* \param loopLevel nested loop level +*/ +void CJavascriptCounter::LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, unsigned int &paren_cnt, + bool &forflag, bool &found_forifwhile, bool &found_while, char &prev_char, bool &data_continue, + unsigned int &temp_lines, unsigned int &phys_exec_lines, unsigned int &phys_data_lines, bool &inArrayDec, + unsigned int &openBrackets, StringVector &loopLevel) +{ + // paren_cnt is used with 'for' statement only + size_t start = 0; //starting index of the working string + size_t i = 0, strSize; + bool found_do, found_try, found_else, trunc_flag = false; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$:"; + unsigned int cnt = 0; + unsigned int loopCnt = 0; + StringVector::iterator lit; + + string tmp = CUtil::TrimString(strLSLOC); + + // do, try + found_do = (CUtil::FindKeyword(tmp, "do") != string::npos); + found_try = (CUtil::FindKeyword(tmp, "try") != string::npos); + // else is treated differently, else is included in SLOC, do and try are not + found_else = (CUtil::FindKeyword(tmp, "else") != string::npos); + + // there may be more than 1 logical SLOC in this line + while (i < line.length()) + { + switch (line[i]) + { + case ';': case '{': // LSLOC terminators + // ';' for normal executable or declaration statement + // '{' for starting a function or 'do' stmt or a block (which is counted) + + // get the previous logical mark until i-1 index is the new LSLOC + // except 'do' precedes '{' + // except '}' precedes ';' ?? + // do nothing inside 'for' statement + if (paren_cnt > 0 && line[i] == ';') + break; + + // record open bracket for nested loop processing + if (print_cmplx) + { + if (line[i] == '{') + { + openBrackets++; + if ((unsigned int)loopLevel.size() < openBrackets) + loopLevel.push_back(""); + } + else + { + if ((unsigned int)loopLevel.size() > openBrackets && openBrackets > 0) + loopLevel.pop_back(); + } + } + + // case 'while(...);', 'while(...) {', and '} while(...);' + // this case is handled in case ')' + if (found_while && found_forifwhile) + { + found_while = false; + found_forifwhile = false; + start = i + 1; + break; + } + + if (line[i] == '{') + { + if (prev_char == '=') + inArrayDec = true; + + // continue until seeing ';' + if (inArrayDec) + break; + + // case for(...); and if (...) { + // these specials are handled + if (found_forifwhile) + { + found_forifwhile = false; + start = i + 1; + break; + } + + // check if 'do' precedes '{' + if (!found_do && !found_try && !found_else) + { + // find for 'do' in string before tmp string + tmp = CUtil::TrimString(line.substr(start, i - start)); + found_do = (tmp == "do"); // found 'do' statement + found_try = (tmp == "try"); // found 'try' statement + // same as else + found_else = (tmp == "else"); // found 'else' statement + } + if (found_do || found_try || found_else) + { + if (found_do && print_cmplx) + { + if (loopLevel.size() > 0) + loopLevel.pop_back(); + loopLevel.push_back("do"); + } + found_do = false; + found_try = false; + if (!found_else) + { + // everything before 'do', 'try' are cleared + strLSLOC = ""; + strLSLOCBak = ""; + start = i + 1; + } + break; // do not store '{' following 'do' + } + } + + // wrong, e.g., a[]={1,2,3}; + if (line[i] == ';' && prev_char == '}') + { + // check if in array declaration or not + // if no, skip, otherwise, complete the SLOC containing array declaration + if (!inArrayDec) + { + start = i + 1; + break; + } + } + + inArrayDec = false; + + // check for empty statement (=1 LSLOC) + if (CUtil::TrimString(line.substr(start, i + 1 - start)) == ";" && strLSLOC.length() < 1) + { + strLSLOC = ";"; + strLSLOCBak = ";"; + } + else + { + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + { + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", &result->data_name_count); + + temp_lines++; + if (data_continue == true && line[i] == ';') + { + result->data_lines[LOG]++; + phys_data_lines = temp_lines; + } + else + { + if (cnt > 0 && line[i] == ';') + { + result->data_lines[LOG]++; + phys_data_lines = temp_lines; + } + else + { + result->exec_lines[LOG]++; + phys_exec_lines = temp_lines; + } + } + } + else if (data_continue == true && line[i] == ';') + phys_data_lines = temp_lines; + else + phys_exec_lines = temp_lines; + data_continue = false; + temp_lines = 0; + strLSLOC = strLSLOCBak = ""; + start = i + 1; + + break; + case '(': + if (forflag) + paren_cnt++; + else + { + // handle 'for', 'foreach', 'while', 'if' the same way + tmp = CUtil::TrimString(line.substr(start,i)); + if (CUtil::FindKeyword(tmp, "for") != string::npos + || CUtil::FindKeyword(tmp, "foreach") != string::npos + || CUtil::FindKeyword(tmp, "while")!= string::npos + || CUtil::FindKeyword(tmp, "if") != string::npos) + { + forflag = true; + paren_cnt++; + + if (print_cmplx && (unsigned int)loopLevel.size() > openBrackets && openBrackets > 0) + loopLevel.pop_back(); + + if (CUtil::FindKeyword(tmp, "while")!= string::npos) + { + if (print_cmplx) + loopLevel.push_back("while"); + found_while = true; + } + else if (print_cmplx) + { + if (CUtil::FindKeyword(tmp, "for") != string::npos) + loopLevel.push_back("for"); + else if (CUtil::FindKeyword(tmp, "foreach") != string::npos) + loopLevel.push_back("foreach"); + + // record nested loop level + if (CUtil::FindKeyword(tmp, "if") == string::npos) + { + loopCnt = 0; + for (lit = loopLevel.begin(); lit < loopLevel.end(); lit++) + { + if ((*lit) != "") + loopCnt++; + } + if ((unsigned int)result->cmplx_nestloop_count.size() < loopCnt) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopCnt-1]++; + } + } + } + } + break; + case ')': + if (forflag) + { + if (paren_cnt > 0) + paren_cnt--; + if (paren_cnt == 0) + { + // handle 'for', 'foreach', 'while', 'if' + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + strLSLOCBak = strLSLOC = ""; + phys_exec_lines = temp_lines; + temp_lines = 0; + start = i + 1; + found_forifwhile = true; + forflag = false; + } + } + break; + case '}': + // skip '}' when found ';' and then '}' because '{' is counted already + // also, {} is also skipped, counted + if (prev_char == ';' || prev_char == '{' || prev_char == '}') + if (!inArrayDec) start = i + 1; + + // record close bracket for nested loop processing + if (print_cmplx) + { + if (openBrackets > 0) + openBrackets--; + if (loopLevel.size() > 0) + loopLevel.pop_back(); + } + break; + } + + if (line[i] != ' ' && line[i] != '\t') + { + // if ;}}} --> don't count }}} at all + // also, if {}}} --> don't count }}} at all + // if ( !(line[i] == '}' && (prev_char == ';' || prev_char == '{'))) // see case '}' above + prev_char = line[i]; + + // change to not found if a char appears before + if (line[i] != ')' && found_forifwhile) + found_forifwhile = false; + } + + i++; + } + + tmp = CUtil::TrimString(line.substr(start, i - start)); + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + tmp = CUtil::TrimString(lineBak.substr(start, i - start)); + strLSLOCBak += tmp.substr(0, strSize); + + // drop continuation symbol + if (strLSLOC[strLSLOC.length()-1] == '\\') + { + strLSLOC = strLSLOC.substr(0, strLSLOC.length()-1); + strLSLOCBak = strLSLOCBak.substr(0, strLSLOCBak.length()-1); + } + } + + // make sure that we are not beginning to process a new data line + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", NULL); + + if (cnt > 0) + data_continue = true; + if (data_continue) + temp_lines++; + if (temp_lines == 0 && phys_data_lines == 0 && phys_exec_lines == 0) + phys_exec_lines = 1; +} + +/*! +* Constructs a CJavascriptPhpCounter object. +*/ +CJavascriptPhpCounter::CJavascriptPhpCounter() +{ + classtype = JAVASCRIPT_PHP; + language_name = "JavaScript/PHP"; + + file_extension.clear(); + file_extension.push_back(".*jsphp"); +} + +/*! +* Constructs a CJavascriptHtmlCounter object. +*/ +CJavascriptHtmlCounter::CJavascriptHtmlCounter() +{ + classtype = JAVASCRIPT_HTML; + language_name = "JavaScript/HTML"; + + file_extension.clear(); + file_extension.push_back(".*jshtm"); +} + +/*! +* Constructs a CJavascriptXmlCounter object. +*/ +CJavascriptXmlCounter::CJavascriptXmlCounter() +{ + classtype = JAVASCRIPT_XML; + language_name = "JavaScript/XML"; + + file_extension.clear(); + file_extension.push_back(".*jsxml"); +} + +/*! +* Constructs a CJavascriptJspCounter object. +*/ +CJavascriptJspCounter::CJavascriptJspCounter() +{ + classtype = JAVASCRIPT_JSP; + language_name = "JavaScript/JSP"; + + file_extension.clear(); + file_extension.push_back(".*jsjsp"); +} + +/*! +* Constructs a CJavascriptAspServerCounter object. +*/ +CJavascriptAspServerCounter::CJavascriptAspServerCounter() +{ + classtype = JAVASCRIPT_ASP_S; + language_name = "JavaScript/ASP Server"; + + file_extension.clear(); + file_extension.push_back(".*jsasps"); +} + +/*! +* Constructs a CJavascriptAspClientCounter object. +*/ +CJavascriptAspClientCounter::CJavascriptAspClientCounter() +{ + classtype = JAVASCRIPT_ASP_C; + language_name = "JavaScript/ASP Client"; + + file_extension.clear(); + file_extension.push_back(".*jsaspc"); +} + +/*! +* Constructs a CJavascriptColdFusionCounter object. +*/ +CJavascriptColdFusionCounter::CJavascriptColdFusionCounter() +{ + classtype = JAVASCRIPT_CFM; + language_name = "JavaScript/ColdFusion"; + + file_extension.clear(); + file_extension.push_back(".*jscfm"); +} + +/*! +* Parses lines for function/method names. +* +* \param line line to be processed +* \param lastline last line processed +* \param functionStack stack of functions +* \param functionName function name found +* \param functionCount function count found +* +* \return 1 if function name is found +*/ +int CJavascriptCounter::ParseFunctionName(const string &line, string &lastline, + filemap &functionStack, string &functionName, unsigned int &functionCount) +{ + string tline, str, front, back; + size_t idx, tidx, cnt, cnt2; + unsigned int fcnt; + + tline = CUtil::TrimString(line); + idx = tline.find('{'); + if (idx != string::npos) + { + // check whether it is at first index, if yes then function name is at above line + if (idx == 0) + { + idx = lastline.find("function"); + if (idx != string::npos) + { + front = lastline.substr(0, idx); + back = lastline.substr(idx + 8); + + idx = front.find('='); + if (idx != string::npos) + { + front = front.substr(0, idx); + lineElement element(++functionCount, CUtil::TrimString(front)); + functionStack.push_back(element); + lastline.erase(); + } + else { + idx = back.find('('); + if (idx != string::npos) + { + back = back.substr(0, idx); + lineElement element(++functionCount, CUtil::TrimString(back)); + functionStack.push_back(element); + lastline.erase(); + } + } + } + } + else + { + str = tline.substr(0, idx); + tidx = cnt = cnt2 = 0; + if (str[0] != '(' && str[0] != ':' && (lastline.length() < 1 || lastline[lastline.length() - 1] != ':')) + { + while (tidx != string::npos) + { + tidx = str.find('(', tidx); + if (tidx != string::npos) + { + cnt++; + tidx++; + } + } + if (cnt > 0) + { + tidx = 0; + while (tidx != string::npos) + { + tidx = str.find(')', tidx); + if (tidx != string::npos) + { + cnt2++; + tidx++; + } + } + } + } + // make sure parentheses are closed and no parent class listed + if ((cnt > 0 && cnt == cnt2) || (lastline.length() > 0 && lastline[lastline.length() - 1] == ';')) + lastline = str; + else + lastline += " " + str; + idx = lastline.find("function"); + if (idx != string::npos) + { + front = lastline.substr(0, idx); + back = lastline.substr(idx + 8); + idx = front.find('='); + if (idx != string::npos) + { + front = front.substr(0, idx); + lineElement element(++functionCount, CUtil::TrimString(front)); + functionStack.push_back(element); + lastline.erase(); + } + else { + idx = back.find('('); + if (idx != string::npos) + { + back = back.substr(0, idx); + lineElement element(++functionCount, CUtil::TrimString(back)); + functionStack.push_back(element); + lastline.erase(); + } + } + } + } + } + else if (tline.length() > 0 && tline[tline.length() - 1] != ';' && + lastline.length() > 0 && lastline[lastline.length() - 1] != ';') + { + // append until all parentheses are closed + tidx = lastline.find('('); + if (tidx != string::npos) + { + cnt = 1; + while (tidx != string::npos) + { + tidx = lastline.find('(', tidx + 1); + if (tidx != string::npos) + cnt++; + } + tidx = lastline.find(')'); + while (tidx != string::npos) + { + cnt++; + tidx = lastline.find(')', tidx + 1); + } + if (cnt % 2 != 0) + lastline += " " + tline; + else + lastline = tline; + } + else + lastline = tline; + } + else + lastline = tline; + + if (functionStack.empty()) + { + // dealing with some code out of any subroutines, it a "main" code + return 2; + } + + idx = CUtil::FindKeyword(line, "{"); + if (idx != string::npos) + { + lineElement element(functionCount, "{"); + functionStack.push_back(element); + } + idx = CUtil::FindKeyword(line, "}"); + if (idx != string::npos) + { + functionStack.pop_back(); + if (functionStack.size() == 0) + return 0; + str = functionStack.back().line; + fcnt = functionStack.back().lineNumber; + if (str != "{") + { + functionStack.pop_back(); + functionName = CUtil::ClearRedundantSpaces(str); + functionCount = fcnt; + return 1; + } + } + return 0; + + +} diff --git a/src/CJavascriptCounter.h b/src/CJavascriptCounter.h new file mode 100644 index 0000000..c7884ef --- /dev/null +++ b/src/CJavascriptCounter.h @@ -0,0 +1,216 @@ +//! Code counter class definition for the JavaScript language. +/*! +* \file CJavascriptCounter.h +* +* This file contains the code counter class definition for the JavaScript language. +*/ + +#ifndef CJavascriptCounter_h +#define CJavascriptCounter_h + +#include "CCodeCounter.h" + +//! JavaScript code counter class. +/*! +* \class CJavascriptCounter +* +* Defines the JavaScript code counter class. +*/ +class CJavascriptCounter : public CCodeCounter +{ +public: + CJavascriptCounter(); + +protected: + virtual int ReplaceQuote(string &strline, size_t &idx_start, bool &contd, char &CurrentQuoteEnd); + virtual int CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak = NULL); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak = NULL); + void LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + unsigned int &paren_cnt, bool &forflag, bool &found_forifwhile, bool &found_while, char &prev_char, + bool &data_continue, unsigned int &temp_lines, unsigned int &phys_exec_lines, unsigned int &phys_data_lines, + bool &inArrayDec, unsigned int &openBrackets, StringVector &loopLevel); + int ParseFunctionName(const string &line, string &/*lastline*/, filemap &functionStack, string &functionName, + unsigned int &functionCount); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CJavascriptCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CJavascriptCounter(const CJavascriptCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CJavascriptCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CJavascriptCounter operator=(const CJavascriptCounter); // Declare without implementation +}; + +//! JavaScript in PHP code counter class. +/*! +* \class CJavascriptPhpCounter +* +* Defines the JavaScript in PHP code counter class. +*/ +class CJavascriptPhpCounter : public CJavascriptCounter +{ +public: + CJavascriptPhpCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CJavascriptPhpCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CJavascriptPhpCounter(const CJavascriptPhpCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CJavascriptPhpCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CJavascriptPhpCounter operator=(const CJavascriptPhpCounter); // Declare without implementation +}; + +//! JavaScript in HTML code counter class. +/*! +* \class CJavascriptHtmlCounter +* +* Defines the JavaScript in HTML code counter class. +*/ +class CJavascriptHtmlCounter : public CJavascriptCounter +{ +public: + CJavascriptHtmlCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CJavascriptHtmlCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CJavascriptHtmlCounter(const CJavascriptHtmlCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CJavascriptHtmlCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CJavascriptHtmlCounter operator=(const CJavascriptHtmlCounter); // Declare without implementation +}; + +//! JavaScript in XML code counter class. +/*! +* \class CJavascriptXmlCounter +* +* Defines the JavaScript in XML code counter class. +*/ +class CJavascriptXmlCounter : public CJavascriptCounter +{ +public: + CJavascriptXmlCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CJavascriptXmlCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CJavascriptXmlCounter(const CJavascriptXmlCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CJavascriptXmlCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CJavascriptXmlCounter operator=(const CJavascriptXmlCounter); // Declare without implementation +}; + +//! JavaScript in JSP code counter class. +/*! +* \class CJavascriptJspCounter +* +* Defines the JavaScript in JSP code counter class. +*/ +class CJavascriptJspCounter : public CJavascriptCounter +{ +public: + CJavascriptJspCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CJavascriptJspCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CJavascriptJspCounter(const CJavascriptJspCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CJavascriptJspCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CJavascriptJspCounter operator=(const CJavascriptJspCounter); // Declare without implementation +}; + +//! JavaScript in ASP server code counter class. +/*! +* \class CJavascriptAspServerCounter +* +* Defines the JavaScript in ASP server code counter class. +*/ +class CJavascriptAspServerCounter : public CJavascriptCounter +{ +public: + CJavascriptAspServerCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CJavascriptAspServerCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CJavascriptAspServerCounter(const CJavascriptAspServerCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CJavascriptAspServerCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CJavascriptAspServerCounter operator=(const CJavascriptAspServerCounter); // Declare without implementation +}; + +//! JavaScript in ASP client code counter class. +/*! +* \class CJavascriptAspClientCounter +* +* Defines the JavaScript in ASP client code counter class. +*/ +class CJavascriptAspClientCounter : public CJavascriptCounter +{ +public: + CJavascriptAspClientCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CJavascriptAspClientCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CJavascriptAspClientCounter(const CJavascriptAspClientCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CJavascriptAspClientCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CJavascriptAspClientCounter operator=(const CJavascriptAspClientCounter); // Declare without implementation +}; + +//! JavaScript in ColdFusion code counter class. +/*! +* \class CJavascriptColdFusionCounter +* +* Defines the JavaScript in ColdFusion code counter class. +*/ +class CJavascriptColdFusionCounter : public CJavascriptCounter +{ +public: + CJavascriptColdFusionCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CJavascriptColdFusionCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CJavascriptColdFusionCounter(const CJavascriptColdFusionCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CJavascriptColdFusionCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CJavascriptColdFusionCounter operator=(const CJavascriptColdFusionCounter); // Declare without implementation +}; + +#endif diff --git a/src/CMakefileCounter.cpp b/src/CMakefileCounter.cpp new file mode 100644 index 0000000..410db03 --- /dev/null +++ b/src/CMakefileCounter.cpp @@ -0,0 +1,213 @@ +//! Code counter class methods for Makefiles. +/*! +* \file CMakefileCounter.cpp +* +* This file contains the code counter class methods for Makefiles. +*/ + +#include "CMakefileCounter.h" + +/*! +* Constructs a CMakefileCounter object. +*/ +CMakefileCounter::CMakefileCounter() +{ + classtype = MAKEFILE; + language_name = "Makefile"; + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("Makefile", file_extension); + + //file_extension.push_back(".make"); + //file_extension.push_back(".makefile"); + + QuoteStart = "\"'"; + QuoteEnd = "\"'"; + LineCommentStart.push_back("#"); + + directive.push_back("include"); + directive.push_back("-include"); + directive.push_back("sinclude"); + + cmplx_assign_list.push_back("="); + cmplx_assign_list.push_back("?="); + cmplx_assign_list.push_back(":="); + cmplx_assign_list.push_back("+="); + + cmplx_preproc_list.push_back("include"); + cmplx_preproc_list.push_back("-include"); + cmplx_preproc_list.push_back("sinclude"); +} + +/*! +* Counts directive lines of code. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CMakefileCounter::CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak) +{ + bool contd = false, trunc_flag = false; + size_t idx, strSize; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string strDirLine = ""; + + filemap::iterator itfmBak = fmapBak->begin(); + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++, itfmBak++) + { + if (CUtil::CheckBlank(iter->line)) + continue; + size_t lineNumber = iter->lineNumber; + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(" " + iter->line, directive, cnt, 1, exclude, "", "", &result->directive_count); + } + + if (!contd) + { + // if not a continuation of a previous directive + for (vector::iterator viter = directive.begin(); viter != directive.end(); viter++) + { + // ensures the keyword stands alone + if (((idx = CUtil::FindKeyword(iter->line, *viter)) != string::npos) && idx == 0) + { + contd = true; + break; + } + } + if (contd) + { + strSize = CUtil::TruncateLine(itfmBak->line.length(), 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine = itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + } + else + { + // continuation of a previous directive + strSize = CUtil::TruncateLine(itfmBak->line.length(), strDirLine.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine += "\n" + itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + + if (contd) + { + // drop continuation symbol + if (strDirLine[strDirLine.length()-1] == '\\') + strDirLine = strDirLine.substr(0, strDirLine.length()-1); + + // if a directive or continuation of a directive (no continuation symbol found) + if (iter->line[iter->line.length()-1] != '\\') + { + contd = false; + if (result->addSLOC(strDirLine, lineNumber, trunc_flag)) + result->directive_lines[LOG]++; + } + iter->line = ""; + } + } + return 1; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CMakefileCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + filemap::iterator fit, fitbak; + string line, lineBak; + string strLSLOC = ""; + string strLSLOCBak = ""; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + line = fit->line; + + // insert blank at the beginning (for searching keywords) + line = ' ' + line; + lineBak = ' ' + fitbak->line; + + // do not process blank lines + // blank line means blank_line/comment_line/directive + if (!CUtil::CheckBlank(line)) + { + LSLOC(result, line, fit->lineNumber, lineBak, strLSLOC, strLSLOCBak); + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count); + } + result->exec_lines[PHY]++; + } + } + return 1; +} + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results +* \param line processed physical line of code +* \param lineBak original physical line of code +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +*/ +void CMakefileCounter::LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak) +{ + size_t strSize; + bool trunc_flag = false; + string tmp = CUtil::TrimString(line); + //string tmpBak = CUtil::TrimString(line); + string tmpBak = CUtil::TrimString(lineBak); //warning fix + string tmpLower = CUtil::ToLower(tmp); + + // check for continuation and skip ending condition strings + if (tmp[tmp.length()-1] == '\\') + { + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 1) + { + strLSLOC += CUtil::TrimString(tmp.substr(0, strSize - 1)) + " "; + strLSLOCBak += CUtil::TrimString(tmpBak.substr(0, strSize - 1)) + " "; + return; + } + } + if (tmpLower == "endef" || tmpLower == "else" || tmpLower == "endif" || tmpLower == "done") + return; + + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + strLSLOCBak += tmpBak.substr(0, strSize); + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + strLSLOC = strLSLOCBak = ""; +} diff --git a/src/CMakefileCounter.h b/src/CMakefileCounter.h new file mode 100644 index 0000000..ed4bc70 --- /dev/null +++ b/src/CMakefileCounter.h @@ -0,0 +1,42 @@ +//! Code counter class definition for Makefiles. +/*! +* \file CMakefileCounter.h +* +* This file contains the code counter class definition for Makefiles. +*/ + +#ifndef CMakefileCounter_h +#define CMakefileCounter_h + +#include "CCodeCounter.h" + +//! Makefile code counter class. +/*! +* \class CMakefileCounter +* +* Defines the Makefile code counter class. +*/ +class CMakefileCounter : public CCodeCounter +{ +public: + CMakefileCounter(); + +protected: + virtual int CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak = NULL); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak = NULL); + void LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CMakefileCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CMakefileCounter(const CMakefileCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CMakefileCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CMakefileCounter operator=(const CMakefileCounter); // Declare without implementation +}; + +#endif diff --git a/src/CMatlabCounter.cpp b/src/CMatlabCounter.cpp new file mode 100644 index 0000000..dbe5ecd --- /dev/null +++ b/src/CMatlabCounter.cpp @@ -0,0 +1,591 @@ +//! Code counter class methods for the Matlab language. +/*! +* \file CMatlabCounter.cpp +* +* This file contains the code counter class methods for the Matlab language. +*/ + +#include "CMatlabCounter.h" +#include + +/*! +* Constructs a CMatlab object. +*/ +CMatlabCounter::CMatlabCounter() +{ + classtype = MATLAB; + language_name = "MATLAB"; + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("MATLAB", file_extension); + //file_extension.push_back(".m"); + + QuoteStart = "'"; + QuoteEnd = "'"; + QuoteEscapeRear = '\''; + ContinueLine = "..."; + + BlockCommentStart.push_back("%{"); + BlockCommentEnd.push_back("%}"); + LineCommentStart.push_back("%"); + + directive.push_back("import"); + + exec_name_list.push_back("all"); + exec_name_list.push_back("break"); + exec_name_list.push_back("case"); + exec_name_list.push_back("catch"); + exec_name_list.push_back("continue"); + exec_name_list.push_back("for"); + exec_name_list.push_back("if"); + exec_name_list.push_back("else"); + exec_name_list.push_back("elseif"); + exec_name_list.push_back("end"); + exec_name_list.push_back("otherwise"); + exec_name_list.push_back("parfor"); + exec_name_list.push_back("return"); + exec_name_list.push_back("switch"); + exec_name_list.push_back("try"); + exec_name_list.push_back("while"); + + math_func_list.push_back("ceil"); + math_func_list.push_back("eps"); + math_func_list.push_back("exp"); + math_func_list.push_back("factor"); + math_func_list.push_back("factorial"); + math_func_list.push_back("fix"); + math_func_list.push_back("floor"); + math_func_list.push_back("idivide"); + math_func_list.push_back("Inf"); + math_func_list.push_back("intmax"); + math_func_list.push_back("intmin"); + math_func_list.push_back("max"); + math_func_list.push_back("mod"); + math_func_list.push_back("NaN"); + math_func_list.push_back("pi"); + math_func_list.push_back("pow2"); + math_func_list.push_back("power"); + math_func_list.push_back("realmax"); + math_func_list.push_back("realmin"); + math_func_list.push_back("rem"); + math_func_list.push_back("round"); + math_func_list.push_back("sqrt"); + + trig_func_list.push_back("acos"); + trig_func_list.push_back("acot"); + trig_func_list.push_back("acsc"); + trig_func_list.push_back("asec"); + trig_func_list.push_back("asin"); + trig_func_list.push_back("atan"); + trig_func_list.push_back("atan2"); + trig_func_list.push_back("cos"); + trig_func_list.push_back("cot"); + trig_func_list.push_back("csc"); + trig_func_list.push_back("sec"); + trig_func_list.push_back("sin"); + trig_func_list.push_back("tan"); + + log_func_list.push_back("log"); + log_func_list.push_back("log10"); + log_func_list.push_back("log1p"); + log_func_list.push_back("log2"); + + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("^"); + cmplx_calc_list.push_back("\\"); + cmplx_calc_list.push_back("/"); + cmplx_calc_list.push_back("\'"); + cmplx_calc_list.push_back(".'"); + cmplx_calc_list.push_back(".*"); + cmplx_calc_list.push_back(".^"); + cmplx_calc_list.push_back(".\\"); + cmplx_calc_list.push_back("./"); + + cmplx_cond_list.push_back("case"); + cmplx_cond_list.push_back("else"); + cmplx_cond_list.push_back("elseif"); + cmplx_cond_list.push_back("for"); + cmplx_cond_list.push_back("if"); + cmplx_cond_list.push_back("switch"); + cmplx_cond_list.push_back("while"); + + cmplx_logic_list.push_back("<"); + cmplx_logic_list.push_back(">"); + cmplx_logic_list.push_back(">="); + cmplx_logic_list.push_back("<="); + cmplx_logic_list.push_back("=="); + cmplx_logic_list.push_back("~="); + + cmplx_preproc_list.push_back("import"); + + cmplx_assign_list.push_back("="); + + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("elseif"); + cmplx_cyclomatic_list.push_back("for"); + cmplx_cyclomatic_list.push_back("while"); + cmplx_cyclomatic_list.push_back("catch"); + cmplx_cyclomatic_list.push_back("parfor"); + cmplx_cyclomatic_list.push_back("case"); + + //there seems to be no logical operators in matlab, so no CC2 results. + cmplx_cyclomatic_case_list.push_back("case"); + cmplx_cyclomatic_switch_list.push_back("switch"); +} + +/*! +* Counts directive lines of code. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CMatlabCounter::CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak) +{ + bool contd = false, trunc_flag = false; + size_t idx, strSize; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string strDirLine = ""; + + filemap::iterator itfmBak = fmapBak->begin(); + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++, itfmBak++) + { + if (CUtil::CheckBlank(iter->line)) + continue; + size_t lineNumber = iter->lineNumber; + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(" " + iter->line, directive, cnt, 1, exclude, "", "", &result->directive_count); + } + + if (!contd) + { + // if not a continuation of a previous directive + for (vector::iterator viter = directive.begin(); viter != directive.end(); viter++) + { + // ensures the keyword stands alone, avoid, e.g., #ifabc + if (((idx = CUtil::FindKeyword(iter->line, *viter)) != string::npos) && idx == 0) + { + contd = true; + break; + } + } + if (contd) + { + strSize = CUtil::TruncateLine(itfmBak->line.length(), 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine = itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + } + else + { + // continuation of a previous directive + strSize = CUtil::TruncateLine(itfmBak->line.length(), strDirLine.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine += "\n" + itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + + if (contd) + { + // drop continuation symbol + if (strDirLine.length() > 3 && strDirLine.substr(strDirLine.length()-3, 3) == "...") + strDirLine = strDirLine.substr(0, strDirLine.length()-3); + + // if a directive or continuation of a directive (no continuation symbol found) + if (iter->line.length() < 3 || iter->line.substr(iter->line.length()-4, 3) != "...") + { + contd = false; + if (result->addSLOC(strDirLine, lineNumber, trunc_flag)) + result->directive_lines[LOG]++; + } + iter->line = ""; + } + } + return 1; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CMatlabCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + bool cont_str = false; + unsigned int openBrackets = 0; + string strLSLOC = ""; + string strLSLOCBak = ""; + filemap::iterator fit, fitbak; + string line, lineBak; + StringVector loopLevel; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + line = fit->line; + + // insert blank at the beginning (for searching keywords) + line = ' ' + line; + lineBak = ' ' + fitbak->line; + + // do not process blank lines + // blank line means blank_line/comment_line/directive + if (!CUtil::CheckBlank(line)) + { + LSLOC(result, line, fit->lineNumber, lineBak, strLSLOC, strLSLOCBak, cont_str, openBrackets, loopLevel); + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count); + } + result->exec_lines[PHY]++; + } + } + return 1; +} + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results +* \param line processed physical line of code +* \param lineBak original physical line of code +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param cont_str continue string +* \param openBrackets number of open brackets (no matching close bracket) +* \param loopLevel nested loop level +*/ +void CMatlabCounter::LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + bool &cont_str, unsigned int &openBrackets, StringVector &loopLevel) +{ + size_t start = 0, len; + size_t i = 0, strSize; + bool trunc_flag = false; + string tmp, tmpBak, str; + + // check exclusions/continuation + tmp = CUtil::TrimString(line); + tmpBak = CUtil::TrimString(lineBak); + if (CUtil::FindKeyword(tmp, "end") == 0) + { + if (loopLevel.size() > 0) + loopLevel.pop_back(); + return; + } + else if (CUtil::FindKeyword(tmp, "case") == 0 || CUtil::FindKeyword(tmp, "else") == 0 || CUtil::FindKeyword(tmp, "otherwise") == 0) + { + strLSLOC += tmp + " "; + strLSLOCBak += tmpBak + " "; + return; + } + + // process nested loops + if (print_cmplx) + { + if (CUtil::FindKeyword(tmp, "for") != string::npos || + CUtil::FindKeyword(tmp, "while") != string::npos || + CUtil::FindKeyword(tmp, "parfor")!= string::npos) + { + loopLevel.push_back("loop"); + + // record nested loop level + unsigned int loopCnt = 0; + for (StringVector::iterator lit = loopLevel.begin(); lit < loopLevel.end(); lit++) + { + if ((*lit) != "") + loopCnt++; + } + if ((unsigned int)result->cmplx_nestloop_count.size() < loopCnt) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopCnt-1]++; + } + else if (CUtil::FindKeyword(tmp, "if") != string::npos || + CUtil::FindKeyword(tmp, "switch") != string::npos || + CUtil::FindKeyword(tmp, "try") != string::npos) + loopLevel.push_back(""); + } + + // there may be more than 1 logical SLOC in this line + while (i < line.length()) + { + switch (line[i]) + { + case ';': case ',': // LSLOC terminators + + if (openBrackets > 0) + { + i++; + continue; + } + + tmp = CUtil::TrimString(line.substr(start, i - start + 1)); + tmpBak = CUtil::TrimString(lineBak.substr(start, i - start + 1)); + + if (cont_str && strLSLOC.length() > 0) + { + // check for string continuation + if (tmp[0] == '\'') + { + tmp = tmp.substr(1, tmp.length() - 1); + tmpBak = tmpBak.substr(1, tmpBak.length() - 1); + } + } + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + strLSLOCBak += tmpBak.substr(0, strSize); + } + result->addSLOC(strLSLOCBak, lineNumber, trunc_flag); + result->exec_lines[LOG]++; + strLSLOC = strLSLOCBak = ""; + cont_str = false; + start = i + 1; + + break; + case '[': case '(': case '{': + openBrackets++; + break; + case ']': case ')': case '}': + openBrackets--; + break; + } + i++; + } + + // check for line continuation + tmp = CUtil::TrimString(line.substr(start, i - start)); + tmpBak = CUtil::TrimString(lineBak.substr(start, i - start)); + if (tmp.length() > 3 && tmp.substr(tmp.length()-3, 3) == "...") + { + // strip off trailing (...) + tmp = tmp.substr(0, tmp.length()-3); + tmpBak = tmpBak.substr(0, tmpBak.length()-3); + + // strip off trailing (') to continue string + str = CUtil::TrimString(tmp, 1); + if (str[str.length()-1] == '\'') + { + len = str.length() - 1; + cont_str = true; + } + else + len = tmp.length(); + + strSize = CUtil::TruncateLine(len, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + if (cont_str) + { + strLSLOC += CUtil::TrimString(tmp.substr(0, strSize), -1); + strLSLOCBak += CUtil::TrimString(tmpBak.substr(0, strSize), -1); + } + else + { + strLSLOC += CUtil::TrimString(tmp.substr(0, strSize)) + " "; + strLSLOCBak += CUtil::TrimString(tmpBak.substr(0, strSize)) + " "; + } + } + } + else + { + // save LSLOC + if (cont_str && strLSLOC.length() > 0) + { + // check for string continuation + if (tmp[0] == '\'') + { + tmp = tmp.substr(1, tmp.length() - 1); + tmpBak = tmpBak.substr(1, tmpBak.length() - 1); + } + } + + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + strLSLOCBak += tmpBak.substr(0, strSize); + + result->addSLOC(strLSLOCBak, lineNumber, trunc_flag); + result->exec_lines[LOG]++; + strLSLOC = strLSLOCBak = ""; + cont_str = false; + } + } +} + +/*! +* Parses lines for function/method names. +* +* \param line line to be processed +* \param functionStack stack of functions +* \param functionName function name found +* \param functionCount function count found +* +* \return 1 if function name is found +*/ + +/* Modification: 2016.10 +* Fixed crash problem when multiple 'end' in one line crashed */ + +int CMatlabCounter::ParseFunctionName(const string &line, string &/*lastline*/, + filemap &functionStack, string &functionName, unsigned int &functionCount) +{ + string str; + size_t idx; + unsigned int fcnt; + + bool finishFlag = false; + string tempLine= line; + + while(!finishFlag) { + finishFlag = true; + // case 1: see "function" with function name + idx = CUtil::FindKeyword(tempLine, "function"); + if (idx != string::npos) + { + str = line.substr(idx + 9); + + idx = tempLine.find("="); + if (idx != string::npos) + { + str =tempLine.substr(idx+1); + } + idx = str.find("("); + if (idx != string::npos) + { + str =str.substr(0,idx); + } + lineElement element(++functionCount, str); + functionStack.push_back(element); // save the name of the function to stack + //--------------------------------------------- + // handle on multiple keywords in one line + string str1 = "function"; + tempLine.replace(CUtil::FindKeyword(tempLine, str1), str1.length(),""); // Modification: 2016_10 + finishFlag = false; + } + + // case 2: if, for, while, switch + // give a counter to count how many end should be left + if ( CUtil::FindKeyword(tempLine, "if") != string::npos) { + lineElement element(++functionCount, "end"); + functionStack.push_back(element); // put a end to the stack + //--------------------------------------------- + // handle on multiple keywords in one line + string str1 = "if"; + tempLine.replace(CUtil::FindKeyword(tempLine, str1), str1.length(),""); // Modification: 2016_10 + finishFlag = false; + } + if ( CUtil::FindKeyword(tempLine, "for") != string::npos) { + lineElement element(++functionCount, "end"); + functionStack.push_back(element); // put a end to the stack + //--------------------------------------------- + // handle on multiple keywords in one line + string str1 = "for"; + tempLine.replace(CUtil::FindKeyword(tempLine, str1), str1.length(),""); // Modification: 2016_10 + finishFlag = false; + } + if ( CUtil::FindKeyword(tempLine, "switch") != string::npos) { + lineElement element(++functionCount, "end"); + functionStack.push_back(element); // put a end to the stack + //--------------------------------------------- + // handle on multiple keywords in one line + string str1 = "switch"; + tempLine.replace(CUtil::FindKeyword(tempLine, str1), str1.length(),""); // Modification: 2016_10 + finishFlag = false; + } + if ( CUtil::FindKeyword(tempLine, "try") != string::npos) { + lineElement element(++functionCount, "end"); + functionStack.push_back(element); // put a end to the stack + //--------------------------------------------- + // handle on multiple keywords in one line + string str1 = "try"; + tempLine.replace(CUtil::FindKeyword(tempLine, str1), str1.length(),""); // Modification: 2016_10 + finishFlag = false; + } + if ( CUtil::FindKeyword(tempLine, "while") != string::npos) { + lineElement element(++functionCount, "end"); + functionStack.push_back(element); // put a end to the stack + //--------------------------------------------- + // handle on multiple keywords in one line + string str1 = "while"; + tempLine.replace(CUtil::FindKeyword(tempLine, str1), str1.length(),""); // Modification: 2016_10 + finishFlag = false; + } + if ( CUtil::FindKeyword(tempLine, "parfor") != string::npos) { + lineElement element(++functionCount, "end"); + functionStack.push_back(element); // put a end to the stack + //--------------------------------------------- + // handle on multiple keywords in one line + string str1 = "parfor"; + tempLine.replace(CUtil::FindKeyword(tempLine, str1), str1.length(),""); // Modification: 2016_10 + + finishFlag = false; + } + + + // case 3: see a "end" in the line + if (CUtil::FindKeyword(tempLine, "end") != string::npos) { + + //--------------------------------------------- + // handle on multiple keywords in one line + string str1 = "end"; + tempLine.replace(CUtil::FindKeyword(tempLine, str1),str1.length(),""); // Modification: 2016_10 + finishFlag = false; + + // the end is for array indexing Arr3(5:end) will get [5 6 7 8] + if (CUtil::FindKeyword(tempLine, "(") != string::npos && CUtil::FindKeyword(tempLine, ")") != string::npos) { + // do thing + } + else { + str = functionStack.back().line; // get the latest element added to the str + fcnt = functionStack.back().lineNumber; + idx = str.find("end"); + if (idx != string::npos){ + functionStack.pop_back(); // take that end(not end of function) out + } + else { + functionName = CUtil::ClearRedundantSpaces(str); + functionCount = fcnt; + functionStack.pop_back(); + return 1; + } + } + } + + + if (functionStack.empty()) + { + finishFlag=true; + // dealing with some code out of any subroutines, it a "main" code + return 2; + } + } + return 0; +} diff --git a/src/CMatlabCounter.h b/src/CMatlabCounter.h new file mode 100644 index 0000000..f021ac0 --- /dev/null +++ b/src/CMatlabCounter.h @@ -0,0 +1,45 @@ +//! Code counter class definition for the Matlab language. +/*! +* \file CMatlabCounter.h +* +* This file contains the code counter class definition for the Matlab Language. +*/ + +#ifndef CMatlabCounter_h +#define CMatlabCounter_h + +#include "CCodeCounter.h" + +//! Matlab code counter class. +/*! +* \class CMatlabCounter +* +* Defines the Matlab code counter class. +*/ +class CMatlabCounter : public CCodeCounter +{ +public: + CMatlabCounter(); + +protected: + virtual int CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak = NULL); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak = NULL); + void LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + bool &cont_str, unsigned int &openBrackets, StringVector &loopLevel); + int ParseFunctionName(const string &line, string &lastline, + filemap &functionStack, string &functionName, unsigned int &functionCount); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CMatlabCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CMatlabCounter(const CMatlabCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CMatlabCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CMatlabCounter operator=(const CMatlabCounter); // Declare without implementation +}; + +#endif diff --git a/src/CMidasCounter.cpp b/src/CMidasCounter.cpp new file mode 100644 index 0000000..3a49d67 --- /dev/null +++ b/src/CMidasCounter.cpp @@ -0,0 +1,628 @@ +//! Code counter class methods for the Midas macro languages. +/*! +* \file CMidasCounter.cpp +* +* This file contains the code counter class methods for the Midas macro languages. +*/ + +#include "CMidasCounter.h" + +/*! +* Constructs a CMidasCounter object. +*/ +CMidasCounter::CMidasCounter() +{ + casesensitive = false; + + QuoteStart = "\""; + QuoteEnd = "\""; + QuoteEscapeFront = '\"'; + ContinueLine = "&"; + + LineCommentStart.push_back("!"); + + exclude_keywords.push_back("endcontrols"); + exclude_keywords.push_back("endif"); + exclude_keywords.push_back("endloop"); + exclude_keywords.push_back("endl"); // abbreviation for endloop + exclude_keywords.push_back("endmacro"); + exclude_keywords.push_back("endsubroutine"); + exclude_keywords.push_back("endwhile"); + exclude_keywords.push_back("endw"); // abbreviation for endwhile + exclude_keywords.push_back("else"); + exclude_keywords.push_back("label"); + + directive.push_back("include"); + + exec_name_list.push_back("break"); + exec_name_list.push_back("call"); + exec_name_list.push_back("continue"); + exec_name_list.push_back("else"); + exec_name_list.push_back("elseif"); + exec_name_list.push_back("forall"); + exec_name_list.push_back("goto"); + exec_name_list.push_back("if"); + exec_name_list.push_back("loop"); + exec_name_list.push_back("pipe"); + exec_name_list.push_back("procedure"); + exec_name_list.push_back("return"); + exec_name_list.push_back("subroutine"); + exec_name_list.push_back("trap"); + exec_name_list.push_back("while"); + + math_func_list.push_back("calc"); + math_func_list.push_back("fcalc"); + math_func_list.push_back("fft"); + math_func_list.push_back("firwind"); + math_func_list.push_back("histogram"); + math_func_list.push_back("maxmin"); + math_func_list.push_back("peakpick"); + math_func_list.push_back("ramp"); + + trig_func_list.push_back("waveform"); + + cmplx_calc_list.push_back("**"); + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("/"); + + cmplx_cond_list.push_back("else"); + cmplx_cond_list.push_back("elseif"); + cmplx_cond_list.push_back("forall"); + cmplx_cond_list.push_back("if"); + cmplx_cond_list.push_back("loop"); + cmplx_cond_list.push_back("trap"); + cmplx_cond_list.push_back("while"); + + cmplx_logic_list.push_back("and"); + cmplx_logic_list.push_back("or"); + cmplx_logic_list.push_back("gt"); + cmplx_logic_list.push_back("lt"); + cmplx_logic_list.push_back("ge"); + cmplx_logic_list.push_back("le"); + cmplx_logic_list.push_back("eq"); + cmplx_logic_list.push_back("eqs"); + cmplx_logic_list.push_back("eqss"); + cmplx_logic_list.push_back("ngt"); + cmplx_logic_list.push_back("nlt"); + cmplx_logic_list.push_back("nge"); + cmplx_logic_list.push_back("nle"); + cmplx_logic_list.push_back("neq"); + cmplx_logic_list.push_back("neqs"); + cmplx_logic_list.push_back("neqss"); + + cmplx_preproc_list.push_back("include"); + + cmplx_assign_list.push_back("results"); + + //Modification 2016.10; USC + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("elseif"); + cmplx_cyclomatic_list.push_back("forall"); + cmplx_cyclomatic_list.push_back("loop"); + cmplx_cyclomatic_list.push_back("trap"); + cmplx_cyclomatic_list.push_back("while"); + + cmplx_cyclomatic_logic_list.push_back("and"); + cmplx_cyclomatic_logic_list.push_back("or"); + +} + +/*! +* Counts directive lines of code. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CMidasCounter::CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak) +{ + bool contd = false, trunc_flag = false; + size_t idx, strSize; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string strDirLine = "", tmp, tmpBak; + filemap::iterator itfmBak = fmapBak->begin(); + + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++, itfmBak++) + { + if (CUtil::CheckBlank(iter->line)) + continue; + size_t lineNumber = iter->lineNumber; + + tmp = CUtil::TrimString(iter->line); + tmpBak = CUtil::TrimString(itfmBak->line); + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(" " + tmp, directive, cnt, 1, exclude, "", "", &result->directive_count, false); + } + + if (!contd) + { + // if not a continuation of a previous directive + for (vector::iterator viter = directive.begin(); viter != directive.end(); viter++) + { + if (((idx = CUtil::FindKeyword(tmp, *viter, 0, TO_END_OF_STRING, false)) != string::npos) && idx == 0) + { + contd = true; + break; + } + } + if (contd) + { + // strip off trailing (&) + if (tmpBak[tmpBak.length()-1] == '&') + tmpBak = tmpBak.substr(0, tmpBak.length() - 1); + strSize = CUtil::TruncateLine(tmpBak.length(), 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine = tmpBak.substr(0, strSize); + result->directive_lines[PHY]++; + } + } + else + { + // continuation of a previous directive + // strip off trailing (&) + if (tmpBak[tmpBak.length()-1] == '&') + tmpBak = tmpBak.substr(0, tmpBak.length() - 1); + strSize = CUtil::TruncateLine(tmpBak.length(), strDirLine.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine += tmpBak.substr(0, strSize); + result->directive_lines[PHY]++; + } + + if (contd) + { + // if a directive or continuation of a directive + if (tmp[tmp.length()-1] != '&') + { + // add another logical directive line, should also have type + // if no continuation symbol found + contd = false; + if (result->addSLOC(strDirLine, lineNumber, trunc_flag)) + result->directive_lines[LOG]++; + } + iter->line = ""; + } + } + return 1; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CMidasCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + filemap::iterator fit, fitbak; + string line, lineBak; + + bool data_continue = false; + string strLSLOC = ""; + string strLSLOCBak = ""; + unsigned int phys_exec_lines = 0; + unsigned int phys_data_lines = 0; + unsigned int temp_lines = 0; + unsigned int cnt = 0; + StringVector loopEnd; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + line = fit->line; + + // insert blank at the beginning (for searching keywords) + line = ' ' + line; + lineBak = ' ' + fitbak->line; + + // do not process blank lines (blank_line/comment_line/directive) + if (!CUtil::CheckBlank(line)) + { + // process logical SLOC + LSLOC(result, line, fit->lineNumber, lineBak, strLSLOC, strLSLOCBak, + data_continue, temp_lines, phys_exec_lines, phys_data_lines, loopEnd); + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count, false); + } + + // update physical SLOC lines + result->exec_lines[PHY] += phys_exec_lines; + phys_exec_lines = 0; + + result->data_lines[PHY] += phys_data_lines; + phys_data_lines = 0; + } + } + return 1; +} + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results +* \param line processed physical line of code +* \param lineBak original physical line of code +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param data_continue continuation of a data declaration line +* \param temp_lines tracks physical line count +* \param phys_exec_lines number of physical executable lines +* \param phys_data_lines number of physical data lines +* \param loopEnd nested loop end string(s) +*/ +void CMidasCounter::LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + bool &data_continue, unsigned int &temp_lines, unsigned int &phys_exec_lines, + unsigned int &phys_data_lines, StringVector &loopEnd) +{ + size_t start = 0; //starting index of the working string + size_t i = 0, strSize; + bool found_exclusion = false, trunc_flag = false; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$:"; + unsigned int cnt = 0; + + string tmp = CUtil::TrimString(line); + string tmpBak = CUtil::TrimString(lineBak); + + // record nested loops + if (print_cmplx) + { + bool new_loop = false; + if (CUtil::FindKeyword(tmp, "do", 0, TO_END_OF_STRING, false) == 0) + { + loopEnd.push_back("enddo"); + new_loop = true; + } + else if (CUtil::FindKeyword(tmp, "forall", 0, TO_END_OF_STRING, false) == 0 || + CUtil::FindKeyword(tmp, "foreach", 0, TO_END_OF_STRING, false) == 0 || + CUtil::FindKeyword(tmp, "for", 0, TO_END_OF_STRING, false) == 0) + { + loopEnd.push_back("endfor"); + new_loop = true; + } + else if (CUtil::FindKeyword(tmp, "loop", 0, TO_END_OF_STRING, false) == 0) + { + loopEnd.push_back("endloop"); + new_loop = true; + } + else if (CUtil::FindKeyword(tmp, "while", 0, TO_END_OF_STRING, false) == 0) + { + loopEnd.push_back("endwhile"); + new_loop = true; + } + else if (loopEnd.size() > 0) + { + if (CUtil::FindKeyword(tmp, loopEnd.back(), 0, TO_END_OF_STRING, false) == 0) + loopEnd.pop_back(); + } + if (new_loop) + { + if (result->cmplx_nestloop_count.size() < loopEnd.size()) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopEnd.size()-1]++; + + // drop end keyword on forall since it is a single line loop + if (CUtil::FindKeyword(tmp, "forall", 0, TO_END_OF_STRING, false) == 0) + loopEnd.pop_back(); + } + } + + // check for line starting with excluded keywords (don't count as LSLOC) + for (StringVector::iterator it = exclude_keywords.begin(); it != exclude_keywords.end(); it++) + { + found_exclusion = (CUtil::FindKeyword(tmp, (*it), 0, TO_END_OF_STRING, false) == 0); + if (found_exclusion) + { + // process else + if ((*it) == "else") + { + if (CUtil::FindKeyword(tmp, "elseif", 0, TO_END_OF_STRING, false) != 0) + break; + } + else + break; + } + } + if (found_exclusion) + { + strLSLOC = strLSLOCBak = ""; + phys_exec_lines++; + temp_lines = 0; + return; + } + + // check for inline if + if (CUtil::FindKeyword(tmp, "if", 0, TO_END_OF_STRING, false) == 0) + { + i = CUtil::FindKeyword(tmp, "then", 0, TO_END_OF_STRING, false); + if (i != string::npos) + { + // check if anything exists past the "then" + start = i + 4; + if (start < tmp.length()) + { + // save LSLOC for if statement, then process in-line action + strSize = CUtil::TruncateLine(start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + strLSLOCBak += tmpBak.substr(0, strSize); + tmp = CUtil::TrimString(tmp.substr(start, tmp.length() - start)); + tmpBak = CUtil::TrimString(tmpBak.substr(start, tmpBak.length() - start)); + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + strLSLOC = strLSLOCBak = ""; + phys_exec_lines++; + temp_lines = 0; + } + } + } + // check for "forall" or "for" + else if (CUtil::FindKeyword(tmp, "forall", 0, TO_END_OF_STRING, false) == 0 || + CUtil::FindKeyword(tmp, "for", 0, TO_END_OF_STRING, false) == 0) + { + // split after forall/for + if (CUtil::FindKeyword(tmp, "forall", 0, TO_END_OF_STRING, false) == 0) + start = 6; + else + start = 3; + if (tmp.length() > start) + { + // save LSLOC for if statement, then process loop action + strSize = CUtil::TruncateLine(start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + strLSLOCBak += tmpBak.substr(0, strSize); + tmp = CUtil::TrimString(tmp.substr(start, tmp.length() - start)); + tmpBak = CUtil::TrimString(tmpBak.substr(start, tmpBak.length() - start)); + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + strLSLOC = strLSLOCBak = ""; + phys_exec_lines++; + temp_lines = 0; + } + } + + // check for continuation (&); exception is (&&) to use literal (&) + if (tmpBak.substr(tmpBak.length() - 1, 1) == "&" && + (tmpBak.length() < 2 || tmpBak.substr(tmpBak.length() - 2, 1) != "&")) + { + // strip off trailing (&) + if (tmp.length() > 1) + { + tmp = tmp.substr(start, tmp.length() - 1); + tmpBak = tmpBak.substr(start, tmpBak.length() - 1); + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + strLSLOCBak += tmpBak.substr(0, strSize); + } + } + else + tmp = tmpBak = ""; + + // make sure that we are not beginning to process a new data line + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", NULL, false); + + if (cnt > 0) + data_continue = true; + if (data_continue) + temp_lines++; + if (temp_lines == 0 && phys_data_lines == 0 && phys_exec_lines == 0) + phys_exec_lines = 1; + } + else + { + // save LSLOC + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + strLSLOCBak += tmpBak.substr(0, strSize); + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + { + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", &result->data_name_count, false); + + temp_lines++; + if (data_continue == true || cnt > 0) + { + result->data_lines[LOG]++; + phys_data_lines = temp_lines; + } + else + { + result->exec_lines[LOG]++; + phys_exec_lines = temp_lines; + } + } + else if (data_continue == true) + phys_data_lines = temp_lines; + else + phys_exec_lines = temp_lines; + data_continue = false; + temp_lines = 0; + strLSLOC = strLSLOCBak = ""; + } +} + +/** + * Counts file language complexity based on specified language keywords/characters. + * + * @param fmap list of processed file lines + * @param result counter results + * + * @return method status + * + * Creation Time and Owner : 2016.10; USC + */ +int CMidasCounter::CountComplexity(filemap* fmap, results* result) +{ + if (classtype == UNKNOWN || classtype == DATAFILE) + return 0; + filemap::iterator fit; + size_t idx; + unsigned int cnt, proc_cnt=0, cyclomatic_cnt = 0, ignore_cyclomatic_cnt = 0, cyclomatic_logic_cnt = 0, procedure_count = 0; + string line, file_ext, procedure_name =""; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + map procedure_map; + map logical_map; + + filemap procedureStack; + + bool process_cyclomatic_complexity = false; + + // check whether to process cyclomatic complexity + if (cmplx_cyclomatic_list.size() > 0) + { + process_cyclomatic_complexity = true; + if (skip_cmplx_cyclomatic_file_extension_list.size() > 0) + { + idx = result->file_name.find_last_of("."); + if (idx != string::npos) + { + file_ext = result->file_name.substr(idx); + file_ext = CUtil::ToLower(file_ext); + if (find(skip_cmplx_cyclomatic_file_extension_list.begin(), skip_cmplx_cyclomatic_file_extension_list.end(), file_ext) != skip_cmplx_cyclomatic_file_extension_list.end()) + process_cyclomatic_complexity = false; + } + } + } + + // process each line + for (fit = fmap->begin(); fit != fmap->end(); fit++) + { + line = fit->line; + + if (CUtil::CheckBlank(line)) + continue; + + line = " " + line; + + // mathematical functions + cnt = 0; + CUtil::CountTally(line, math_func_list, cnt, 1, exclude, "", "", &result->math_func_count, casesensitive); + result->cmplx_math_lines += cnt; + + // trigonometric functions + cnt = 0; + CUtil::CountTally(line, trig_func_list, cnt, 1, exclude, "", "", &result->trig_func_count, casesensitive); + result->cmplx_trig_lines += cnt; + + // logarithmic functions + cnt = 0; + CUtil::CountTally(line, log_func_list, cnt, 1, exclude, "", "", &result->log_func_count, casesensitive); + result->cmplx_logarithm_lines += cnt; + + // calculations + cnt = 0; + CUtil::CountTally(line, cmplx_calc_list, cnt, 1, exclude, "", "", &result->cmplx_calc_count, casesensitive); + result->cmplx_calc_lines += cnt; + + // conditionals + cnt = 0; + CUtil::CountTally(line, cmplx_cond_list, cnt, 1, exclude, "", "", &result->cmplx_cond_count, casesensitive); + result->cmplx_cond_lines += cnt; + + // logical operators + cnt = 0; + CUtil::CountTally(line, cmplx_logic_list, cnt, 1, exclude, "", "", &result->cmplx_logic_count, casesensitive); + result->cmplx_logic_lines += cnt; + + // preprocessor directives + cnt = 0; + CUtil::CountTally(line, cmplx_preproc_list, cnt, 1, exclude, "", "", &result->cmplx_preproc_count, + casesensitive); + result->cmplx_preproc_lines += cnt; + + // assignments + cnt = 0; + CUtil::CountTally(line, cmplx_assign_list, cnt, 1, exclude, "", "", &result->cmplx_assign_count, casesensitive); + result->cmplx_assign_lines += cnt; + + /* + // pointers + cnt = 0; + // Pointers are embedded syntax so there is NO exclude string or include strings + CUtil::CountTally(line, cmplx_pointer_list, cnt, 1, "", "", "", &result->cmplx_pointer_count, casesensitive); + result->cmplx_pointer_lines += cnt; + */ + + if(process_cyclomatic_complexity) { + unsigned int temp = 0; + CUtil::CountTally(line, cmplx_cyclomatic_list, temp, 1, exclude, "", "", 0, casesensitive); + cyclomatic_cnt += temp; + + if (ignore_cmplx_cyclomatic_list.size() > 0) + CUtil::CountTally(line, ignore_cmplx_cyclomatic_list, ignore_cyclomatic_cnt, 1, exclude, "", "", 0, + casesensitive); + + // search for cyclomatic complexity logical keywords + if (cmplx_cyclomatic_logic_list.size() > 0) + CUtil::CountTally(line, cmplx_cyclomatic_logic_list, cyclomatic_logic_cnt, 1, exclude, "", "", 0, + casesensitive); + + //search for startmacro + size_t idx_macro = CUtil::FindKeyword(line, "startmacro"); + if (idx_macro != string::npos) { + lineElement element(++proc_cnt, "startmacro"); + procedureStack.push_back(element); + } + } + } + + while(!procedureStack.empty()) + { + procedure_name = procedureStack.back().line; + procedure_count = procedureStack.back().lineNumber; + procedureStack.pop_back(); + + if(procedureStack.empty()) + { + lineElement element(cyclomatic_cnt - ignore_cyclomatic_cnt + 1, procedure_name); + lineElement n_element(cyclomatic_cnt + cyclomatic_logic_cnt + 1, procedure_name); + procedure_map[procedure_count] = element; + logical_map[procedure_count] = n_element; + } + + } + + for (map::iterator it = procedure_map.begin(); it != procedure_map.end(); ++it) + result->cmplx_cycfunct_count.push_back(it->second); + if(cmplx_cyclomatic_logic_list.size() > 0) + { + for (map::iterator it = logical_map.begin(); it != logical_map.end(); ++it) + result->cmplx_cycfunct_CC2_count.push_back(it->second); + } + return 1; + +} diff --git a/src/CMidasCounter.h b/src/CMidasCounter.h new file mode 100644 index 0000000..5af9b85 --- /dev/null +++ b/src/CMidasCounter.h @@ -0,0 +1,46 @@ +//! Code counter class definition for the Midas macro languages. +/*! +* \file CMidasCounter.h +* +* This file contains the code counter class definition for the Midas macro languages. +*/ + +#ifndef CMidasCounter_h +#define CMidasCounter_h + +#include "CCodeCounter.h" + +//! Midas code counter class. +/*! +* \class CMidasCounter +* +* Defines the Midas code counter class. +*/ +class CMidasCounter : public CCodeCounter +{ +public: + CMidasCounter(); + +protected: + virtual int CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak = NULL); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak = NULL); + void LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + bool &data_continue, unsigned int &temp_lines, unsigned int &phys_exec_lines, + unsigned int &phys_data_lines, StringVector &loopEnd); + //Modification 2016.10; USC + int CountComplexity(filemap* fmap, results* result); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CMidasCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CMidasCounter(const CMidasCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CMidasCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CMidasCounter operator=(const CMidasCounter); // Declare without implementation +}; + +#endif diff --git a/src/CNeXtMidasCounter.cpp b/src/CNeXtMidasCounter.cpp new file mode 100644 index 0000000..5ecc178 --- /dev/null +++ b/src/CNeXtMidasCounter.cpp @@ -0,0 +1,47 @@ +//! Code counter class methods for the NeXtMidas macro language. +/*! +* \file CNeXtMidasCounter.cpp +* +* This file contains the code counter class methods for the NeXtMidas macro language. +*/ + +#include "CNeXtMidasCounter.h" + +/*! +* Constructs a CNeXtMidasCounter object. +*/ +CNeXtMidasCounter::CNeXtMidasCounter() +{ + classtype = NEXTMIDAS; + language_name = "NeXtMidas"; + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("NeXtMidas", file_extension); + + //file_extension.push_back(".mm"); + + exclude_keywords.push_back("enddo"); + exclude_keywords.push_back("endfor"); + + data_name_list.push_back("global"); + + exec_name_list.push_back("do"); + exec_name_list.push_back("foreach"); + + math_func_list.push_back("filter"); + math_func_list.push_back("ifft"); + + cmplx_cond_list.push_back("do"); + cmplx_cond_list.push_back("foreach"); + + cmplx_logic_list.push_back("isTrue"); + cmplx_logic_list.push_back("isFalse"); + cmplx_logic_list.push_back("oeq"); + cmplx_logic_list.push_back("feq"); + cmplx_logic_list.push_back("noeq"); + cmplx_logic_list.push_back("nfeq"); + + //Modification 2016.11; USC + cmplx_cyclomatic_list.push_back("do"); + cmplx_cyclomatic_list.push_back("foreach"); +} diff --git a/src/CNeXtMidasCounter.h b/src/CNeXtMidasCounter.h new file mode 100644 index 0000000..f8c8f10 --- /dev/null +++ b/src/CNeXtMidasCounter.h @@ -0,0 +1,37 @@ +//! Code counter class definition for the NeXtMidas macro language. +/*! +* \file CNeXtMidasCounter.h +* +* This file contains the code counter class definition for the NeXtMidas macro language. +*/ + +#ifndef CNeXtMidasCounter_h +#define CNeXtMidasCounter_h + +#include "CMidasCounter.h" + +//! NeXtMidas code counter class. +/*! +* \class CNeXtMidasCounter +* +* Defines the NeXtMidas code counter class. +*/ +class CNeXtMidasCounter : public CMidasCounter +{ +public: + CNeXtMidasCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CNeXtMidasCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CNeXtMidasCounter(const CNeXtMidasCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CNeXtMidasCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CNeXtMidasCounter operator=(const CNeXtMidasCounter); // Declare without implementation +}; + +#endif diff --git a/src/CObjCCounter.cpp b/src/CObjCCounter.cpp new file mode 100644 index 0000000..92ae1fc --- /dev/null +++ b/src/CObjCCounter.cpp @@ -0,0 +1,240 @@ +//! Code counter class methods for the Objective C languages. +/*! +* \file CObjCCounter.cpp +* +* This file contains the code counter class methods for the Objective C languages. +*/ + +#include "CObjCCounter.h" + +/*! +* Constructs a CObjCCounter object. +*/ + +CObjCCounter::CObjCCounter(string lang) : CCJavaCsScalaCounter(lang) +{ + classtype = OBJC; + + language_name = "OBJC"; + //file_extension.push_back(".m"); share extension with Matlab, need to specify in extfile + //file_extension.push_back(".mm"); share extension with NeXtMidas, need to specify in extfile + //file_extension.push_back(".h"); share extension with C_CPP, need to specify in extfile + + directive.push_back("#define"); + directive.push_back("#dictionary"); + directive.push_back("#error"); + directive.push_back("#if"); + directive.push_back("#ifdef"); + directive.push_back("#ifndef"); + directive.push_back("#else"); + directive.push_back("#elif"); + directive.push_back("#endif"); + directive.push_back("#import"); + directive.push_back("#include"); + directive.push_back("#line"); + directive.push_back("#module"); + directive.push_back("#pragma"); + directive.push_back("#undef"); + directive.push_back("#using"); + + directive.push_back("# define"); + directive.push_back("# dictionary"); + directive.push_back("# error"); + directive.push_back("# if"); + directive.push_back("# ifdef"); + directive.push_back("# ifndef"); + directive.push_back("# else"); + directive.push_back("# elif"); + directive.push_back("# endif"); + directive.push_back("# import"); + directive.push_back("# include"); + directive.push_back("# line"); + directive.push_back("# module"); + directive.push_back("# pragma"); + directive.push_back("# undef"); + directive.push_back("# using"); + + // directive.push_back("@interface"); // in Objective C, althought "@interface" should be compile direction, the interface definition should be excutable line + // directive.push_back("@autoreleasepool"); count as excutable line now, will double count as compile direction and excutable line + // directive.push_back("@synchronized"); count as excutable line now, will double count as compile direction and excutable line + // directive.push_back("@selector"); // not very sure how to use it now + directive.push_back("@implementation"); + directive.push_back("@protocol"); + directive.push_back("@optional"); + directive.push_back("@required"); + directive.push_back("@end"); + directive.push_back("@encode"); + directive.push_back("@throw"); + directive.push_back("@property"); + directive.push_back("@synthesize"); + + + + data_name_list.push_back("asm"); + data_name_list.push_back("auto"); + data_name_list.push_back("bool"); + data_name_list.push_back("char"); + data_name_list.push_back("class"); + data_name_list.push_back("const"); + data_name_list.push_back("double"); + data_name_list.push_back("enum"); + data_name_list.push_back("explicit"); + data_name_list.push_back("extern"); + data_name_list.push_back("FILE"); + data_name_list.push_back("float"); + data_name_list.push_back("friend"); + data_name_list.push_back("inline"); + data_name_list.push_back("int"); + data_name_list.push_back("long"); + data_name_list.push_back("mutable"); + data_name_list.push_back("namespace"); + data_name_list.push_back("operator"); + data_name_list.push_back("register"); + data_name_list.push_back("short"); + data_name_list.push_back("static"); + data_name_list.push_back("string"); + data_name_list.push_back("struct"); + data_name_list.push_back("template"); + data_name_list.push_back("typedef"); + data_name_list.push_back("union"); + data_name_list.push_back("unsigned"); + data_name_list.push_back("using"); + data_name_list.push_back("virtual"); + data_name_list.push_back("void"); + data_name_list.push_back("volatile"); + data_name_list.push_back("wchar_t"); + data_name_list.push_back("id"); + data_name_list.push_back("BOOL"); + + exec_name_list.push_back("break"); + exec_name_list.push_back("case"); + exec_name_list.push_back("catch"); + exec_name_list.push_back("cerr"); + exec_name_list.push_back("cin"); + exec_name_list.push_back("clog"); + exec_name_list.push_back("const_cast"); + exec_name_list.push_back("continue"); + exec_name_list.push_back("cout"); + exec_name_list.push_back("default"); + exec_name_list.push_back("delete"); + exec_name_list.push_back("do"); + exec_name_list.push_back("dynamic_cast"); + exec_name_list.push_back("else"); + exec_name_list.push_back("entry"); + exec_name_list.push_back("for"); + exec_name_list.push_back("goto"); + exec_name_list.push_back("if"); + exec_name_list.push_back("new"); + exec_name_list.push_back("reinterpret_cast"); + exec_name_list.push_back("return"); + exec_name_list.push_back("sizeof"); + exec_name_list.push_back("stderr"); + exec_name_list.push_back("stdin"); + exec_name_list.push_back("stdout"); + exec_name_list.push_back("switch"); + exec_name_list.push_back("static_cast"); + exec_name_list.push_back("throw"); + exec_name_list.push_back("try"); + exec_name_list.push_back("typeid"); + exec_name_list.push_back("while"); + exec_name_list.push_back("print"); + exec_name_list.push_back("finally"); + + math_func_list.push_back("abs"); + math_func_list.push_back("cbrt"); + math_func_list.push_back("ceil"); + math_func_list.push_back("copysign"); + math_func_list.push_back("erf"); + math_func_list.push_back("erfc"); + math_func_list.push_back("exp"); + math_func_list.push_back("exp2"); + math_func_list.push_back("expm1"); + math_func_list.push_back("fabs"); + math_func_list.push_back("fdim"); + math_func_list.push_back("floor"); + math_func_list.push_back("fma"); + math_func_list.push_back("fmax"); + math_func_list.push_back("fmin"); + math_func_list.push_back("fmod"); + math_func_list.push_back("frexp"); + math_func_list.push_back("hypot"); + math_func_list.push_back("ilogb"); + math_func_list.push_back("ldexp"); + math_func_list.push_back("lgamma"); + math_func_list.push_back("llrint"); + math_func_list.push_back("lrint"); + math_func_list.push_back("llround"); + math_func_list.push_back("lround"); + math_func_list.push_back("modf"); + math_func_list.push_back("nan"); + math_func_list.push_back("nearbyint"); + math_func_list.push_back("nextafter"); + math_func_list.push_back("nexttoward"); + math_func_list.push_back("pow"); + math_func_list.push_back("remainder"); + math_func_list.push_back("remquo"); + math_func_list.push_back("rint"); + math_func_list.push_back("round"); + math_func_list.push_back("scalbln"); + math_func_list.push_back("scalbn"); + math_func_list.push_back("sqrt"); + math_func_list.push_back("tgamma"); + math_func_list.push_back("trunc"); + + trig_func_list.push_back("cos"); + trig_func_list.push_back("cosh"); + trig_func_list.push_back("sin"); + trig_func_list.push_back("sinh"); + trig_func_list.push_back("tan"); + trig_func_list.push_back("tanh"); + trig_func_list.push_back("acos"); + trig_func_list.push_back("acosh"); + trig_func_list.push_back("asinh"); + trig_func_list.push_back("atanh"); + trig_func_list.push_back("asin"); + trig_func_list.push_back("atan"); + trig_func_list.push_back("atan2"); + + log_func_list.push_back("log"); + log_func_list.push_back("log10"); + log_func_list.push_back("log1p"); + log_func_list.push_back("log2"); + log_func_list.push_back("logb"); + + cmplx_preproc_list.push_back("#define"); + cmplx_preproc_list.push_back("#dictionary"); + cmplx_preproc_list.push_back("#elif"); + cmplx_preproc_list.push_back("#else"); + cmplx_preproc_list.push_back("#endif"); + cmplx_preproc_list.push_back("#error"); + cmplx_preproc_list.push_back("#if"); + cmplx_preproc_list.push_back("#ifdef"); + cmplx_preproc_list.push_back("#ifndef"); + cmplx_preproc_list.push_back("#import"); + cmplx_preproc_list.push_back("#include"); + cmplx_preproc_list.push_back("#line"); + cmplx_preproc_list.push_back("#module"); + cmplx_preproc_list.push_back("#pragma"); + cmplx_preproc_list.push_back("#undef"); + cmplx_preproc_list.push_back("#using"); + + cmplx_pointer_list.push_back("->"); + +/* Below Set in CCJavaCsScalaCounter base class + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("case"); + cmplx_cyclomatic_list.push_back("while"); + cmplx_cyclomatic_list.push_back("for"); + cmplx_cyclomatic_list.push_back("catch"); + cmplx_cyclomatic_list.push_back("?"); + + cmplx_cyclomatic_logic_list.push_back("||"); + cmplx_cyclomatic_logic_list.push_back("&&"); + //cmplx_cyclomatic_logic_list.push_back("^"); + + cmplx_cyclomatic_case_list.push_back("case"); + cmplx_cyclomatic_switch_list.push_back("switch"); +*/ + skip_cmplx_cyclomatic_file_extension_list.push_back(".h"); +} + diff --git a/src/CObjCCounter.h b/src/CObjCCounter.h new file mode 100644 index 0000000..191ffe5 --- /dev/null +++ b/src/CObjCCounter.h @@ -0,0 +1,38 @@ +//! Code counter class definition for the C/C++ languages. +/*! +* \file CCCounter.h +* +* This file contains the code counter class definition for the C/C++ languages. +*/ + +#ifndef CObjCCounter_h +#define CObjCCounter_h + +#include "CCJavaCsScalaCounter.h" + +//! Objective C code counter class. +/*! +* \class CObjCCounter +* +* Defines the Objective C code counter class. +*/ + +class CObjCCounter : public CCJavaCsScalaCounter +{ +public: + CObjCCounter( string lang = "OBJC" ); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CObjCCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CObjCCounter(const CObjCCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CObjCCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CObjCCounter operator=(const CObjCCounter); // Declare without implementation +}; + +#endif \ No newline at end of file diff --git a/src/CPascalCounter.cpp b/src/CPascalCounter.cpp new file mode 100644 index 0000000..d4d155d --- /dev/null +++ b/src/CPascalCounter.cpp @@ -0,0 +1,1240 @@ +//! Code counter class methods for the Pascal language. +/*! +* \file CPascalCounter.cpp +* +* This file contains the code counter class methods for the Pascal language. +*/ + +#include "CPascalCounter.h" + +/*! +* Constructs a CPascalCounter object. +*/ +CPascalCounter::CPascalCounter() +{ + classtype = PASCAL; + language_name = "Pascal"; + casesensitive = false; + + QuoteStart = "'"; + QuoteEnd = "'"; + QuoteEscapeFront = '\''; + BlockCommentStart.push_back("(*"); + BlockCommentEnd.push_back("*)"); + BlockCommentStart.push_back("{"); + BlockCommentEnd.push_back("}"); + LineCommentStart.push_back("//"); + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("Pascal", file_extension); + /* + file_extension.push_back(".pas"); + file_extension.push_back(".p"); + file_extension.push_back(".pp"); + file_extension.push_back(".pa3"); + file_extension.push_back(".pa4"); + file_extension.push_back(".pa5");*/ + + data_name_list.push_back("ansistring"); + data_name_list.push_back("array"); + data_name_list.push_back("boolean"); + data_name_list.push_back("byte"); + data_name_list.push_back("bytebool"); + data_name_list.push_back("cardinal"); + data_name_list.push_back("char"); + data_name_list.push_back("class"); + data_name_list.push_back("comp"); + data_name_list.push_back("complex"); + data_name_list.push_back("const"); + data_name_list.push_back("double"); + data_name_list.push_back("extended"); + data_name_list.push_back("file"); + data_name_list.push_back("integer"); + data_name_list.push_back("interface"); + data_name_list.push_back("int64"); + data_name_list.push_back("longbool"); + data_name_list.push_back("longint"); + data_name_list.push_back("longword"); + data_name_list.push_back("object"); + data_name_list.push_back("pchar"); + data_name_list.push_back("qword"); + data_name_list.push_back("real"); + data_name_list.push_back("record"); + data_name_list.push_back("set"); + data_name_list.push_back("shortint"); + data_name_list.push_back("shortstring"); + data_name_list.push_back("single"); + data_name_list.push_back("smallint"); + data_name_list.push_back("string"); + data_name_list.push_back("type"); + data_name_list.push_back("widestring"); + data_name_list.push_back("word"); + data_name_list.push_back("wordbool"); + + exec_name_list.push_back("absolute"); + exec_name_list.push_back("assembler"); + exec_name_list.push_back("case"); + exec_name_list.push_back("const"); + exec_name_list.push_back("constructor"); + exec_name_list.push_back("destructor"); + exec_name_list.push_back("dispose"); + exec_name_list.push_back("downto"); + exec_name_list.push_back("else"); + exec_name_list.push_back("exit"); + exec_name_list.push_back("far"); + exec_name_list.push_back("for"); + exec_name_list.push_back("forward"); + exec_name_list.push_back("freemem"); + exec_name_list.push_back("function"); + exec_name_list.push_back("getmem"); + exec_name_list.push_back("goto"); + exec_name_list.push_back("if"); + exec_name_list.push_back("implementation"); + exec_name_list.push_back("inline"); + exec_name_list.push_back("interrupt"); + exec_name_list.push_back("label"); + exec_name_list.push_back("mark"); + exec_name_list.push_back("near"); + exec_name_list.push_back("new"); + exec_name_list.push_back("nil"); + exec_name_list.push_back("packed"); + exec_name_list.push_back("private"); + exec_name_list.push_back("procedure"); + exec_name_list.push_back("program"); + exec_name_list.push_back("protected"); + exec_name_list.push_back("public"); + exec_name_list.push_back("repeat"); + exec_name_list.push_back("unit"); + exec_name_list.push_back("uses"); + exec_name_list.push_back("var"); + exec_name_list.push_back("virtual"); + exec_name_list.push_back("while"); + exec_name_list.push_back("with"); + + math_func_list.push_back("abs"); + math_func_list.push_back("arg"); + math_func_list.push_back("cmplx"); + math_func_list.push_back("dec"); + math_func_list.push_back("exp"); + math_func_list.push_back("im"); + math_func_list.push_back("inc"); + math_func_list.push_back("min"); + math_func_list.push_back("max"); + math_func_list.push_back("polar"); + math_func_list.push_back("pow"); + math_func_list.push_back("re"); + math_func_list.push_back("round"); + math_func_list.push_back("sqr"); + math_func_list.push_back("sqrt"); + + trig_func_list.push_back("cos"); + trig_func_list.push_back("sin"); + trig_func_list.push_back("arccos"); + trig_func_list.push_back("arcsin"); + trig_func_list.push_back("arctan"); + + log_func_list.push_back("ln"); + + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("/"); + cmplx_calc_list.push_back("**"); + cmplx_calc_list.push_back("div"); + cmplx_calc_list.push_back("mod"); + + cmplx_cond_list.push_back("case"); + cmplx_cond_list.push_back("else"); + cmplx_cond_list.push_back("for"); + cmplx_cond_list.push_back("if"); + cmplx_cond_list.push_back("repeat"); + cmplx_cond_list.push_back("while"); + cmplx_cond_list.push_back("with"); + + cmplx_logic_list.push_back("="); + cmplx_logic_list.push_back("<>"); + cmplx_logic_list.push_back(">"); + cmplx_logic_list.push_back("<"); + cmplx_logic_list.push_back(">="); + cmplx_logic_list.push_back("=<"); + cmplx_logic_list.push_back("not"); + cmplx_logic_list.push_back("and"); + cmplx_logic_list.push_back("or"); + cmplx_logic_list.push_back("xor"); + cmplx_logic_list.push_back("shl"); + cmplx_logic_list.push_back("shr"); + + cmplx_assign_list.push_back(":="); + + cmplx_pointer_list.push_back("^"); + + //author: Anthony Zhu + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("for"); + cmplx_cyclomatic_list.push_back("while"); + cmplx_cyclomatic_list.push_back("until"); + cmplx_cyclomatic_list.push_back("except"); + + cmplx_cyclomatic_logic_list.push_back("and"); + cmplx_cyclomatic_logic_list.push_back("or"); + //cmplx_cyclomatic_logic_list.push_back("xor"); + + cmplx_cyclomatic_case_list.push_back("case"); + //cmplx_cyclomatic_list.push_back("case"); special case, need special handler +} + +/*! +* Counts the number of comment lines, removes comments, and +* replaces quoted strings by special chars, e.g., $ +* All arguments are modified by the method. +* Since Pascal compiler directives are block comments starting with '$' +* this method also captures directive SLOC. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CPascalCounter::CountCommentsSLOC(filemap* fmap, results* result, filemap *fmapBak) +{ + if (BlockCommentStart.empty() && LineCommentStart.empty()) + return 0; + if (classtype == DATAFILE) + return 0; + + bool contd = false; + bool contd_nextline; + int comment_type = 0; + /* + comment_type: + 0 : not a comment + 1 : line comment, whole line + 2 : line comment, embedded + 3 : block comment, undecided + 4 : block comment, embedded + */ + + size_t idx_start, idx_end, comment_start; + size_t quote_idx_start; + string curBlckCmtStart, curBlckCmtEnd; + char CurrentQuoteEnd = 0; + bool quote_contd = false; + filemap::iterator itfmBak = fmapBak->begin(); + + string strDirLine; + size_t strSize; + bool isDirective = false, trunc_flag = false; + + quote_idx_start = 0; + + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++, itfmBak++) + { + contd_nextline = false; + + quote_idx_start = 0; + idx_start = 0; + + if (CUtil::CheckBlank(iter->line)) + continue; + size_t lineNumber = iter->lineNumber; + + if (quote_contd) + { + // Replace quote until next character + ReplaceQuote(iter->line, quote_idx_start, quote_contd, CurrentQuoteEnd); + if (quote_contd) + continue; + } + if (contd) + comment_type = 3; + + while (!contd_nextline && idx_start < iter->line.length()) + { + // need to handle multiple quote chars in some languages, both " and ' may be accepted + quote_idx_start = FindQuote(iter->line, QuoteStart, quote_idx_start, QuoteEscapeFront); + comment_start = idx_start; + if (!contd) + FindCommentStart(iter->line, comment_start, comment_type, curBlckCmtStart, curBlckCmtEnd); + + if (comment_start == string::npos && quote_idx_start == string::npos) + break; + + if (comment_start != string::npos) + idx_start = comment_start; + + // if found quote before comment, e.g., "this is quote");//comment + if (quote_idx_start != string::npos && (comment_start == string::npos || quote_idx_start < comment_start)) + { + ReplaceQuote(iter->line, quote_idx_start, quote_contd, CurrentQuoteEnd); + if (quote_idx_start > idx_start) + { + // comment delimiter inside quote + idx_start = quote_idx_start; + continue; + } + } + else if (comment_start != string::npos) + { + // comment delimiter starts first + switch (comment_type) + { + case 1: // line comment, definitely whole line + iter->line = ""; + itfmBak->line = ""; + result->comment_lines++; + contd_nextline = true; + break; + case 2: // line comment, possibly embedded + iter->line = iter->line.substr(0, idx_start); + itfmBak->line = itfmBak->line.substr(0, idx_start); + // trim trailing space + iter->line = CUtil::TrimString(iter->line, 1); + itfmBak->line = CUtil::TrimString(itfmBak->line, 1); + if (iter->line.empty()) + result->comment_lines++; // whole line + else + result->e_comm_lines++; // embedded + contd_nextline = true; + break; + case 3: // block comment + case 4: + if (contd) + idx_end = iter->line.find(curBlckCmtEnd); + else + { + idx_end = iter->line.find(curBlckCmtEnd, idx_start + curBlckCmtStart.length()); + + // check whether comment is a directive (starts with '$') + isDirective = false; + for (size_t i = 0; i < BlockCommentStart.size(); i++) + { + if (iter->line.substr(idx_start, BlockCommentStart[i].length() + 1) == BlockCommentStart[i] + "$") + { + strDirLine = ""; + isDirective = true; + break; + } + } + } + + if (idx_end == string::npos) + { + if (comment_type == 3) + { + if (isDirective) + { + strSize = CUtil::TruncateLine(itfmBak->line.length(), strDirLine.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + if (contd) + strDirLine += "\n" + itfmBak->line.substr(0, strSize); + else + strDirLine = itfmBak->line.substr(0, strSize); + } + result->directive_lines[PHY]++; + } + else + result->comment_lines++; + iter->line = ""; + itfmBak->line = ""; + } + else if (comment_type == 4) + { + if (isDirective) + { + strSize = CUtil::TruncateLine(itfmBak->line.length() - idx_start, 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine = itfmBak->line.substr(idx_start, strSize); + result->directive_lines[PHY]++; + } + iter->line = iter->line.substr(0, idx_start); + itfmBak->line = itfmBak->line.substr(0, idx_start); + // trim trailing space + iter->line = CUtil::TrimString(iter->line, 1); + itfmBak->line = CUtil::TrimString(itfmBak->line, 1); + if (!isDirective) + { + if (iter->line.empty()) + result->comment_lines++; // whole line + else + result->e_comm_lines++; // embedded + } + } + contd = true; + contd_nextline = true; + break; + } + else + { + if (isDirective) + { + strSize = CUtil::TruncateLine(idx_end - idx_start + curBlckCmtEnd.length(), strDirLine.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + if (contd) + strDirLine += "\n" + itfmBak->line.substr(idx_start, strSize); + else + strDirLine = itfmBak->line.substr(idx_start, strSize); + } + result->directive_lines[PHY]++; + } + contd = false; + iter->line.erase(idx_start, idx_end - idx_start + curBlckCmtEnd.length()); + itfmBak->line.erase(idx_start, idx_end - idx_start + curBlckCmtEnd.length()); + if (isDirective) + { + if (result->addSLOC(strDirLine, lineNumber, trunc_flag)) + result->directive_lines[LOG]++; + strDirLine = ""; + } + else + { + if (iter->line.empty()) + result->comment_lines++; + else + { + // trim trailing space + iter->line = CUtil::TrimString(iter->line, 1); + itfmBak->line = CUtil::TrimString(itfmBak->line, 1); + if (iter->line.empty()) + result->comment_lines++; // whole line + else + result->e_comm_lines++; // embedded + } + } + + // quote chars found may be erased as it is inside comment + quote_idx_start = idx_start; + } + break; + default: + cout << "Error in CountCommentsSLOC()" << endl; + break; + } + } + } + } + return 1; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CPascalCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + bool found_block = false; + bool found_forifwhile = false; + bool found_end = false; + bool found_loop = false; + string strLSLOC = ""; + string strLSLOCBak = ""; + + filemap::iterator fit, fitbak; + string line, lineBak; + StringVector loopLevel; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + // insert blank at the beginning (for searching keywords) + line = ' ' + fit->line; + lineBak = ' ' + fitbak->line; + + // do not process blank lines + // blank line means blank_line/comment_line/directive + if (!CUtil::CheckBlank(line)) + { + // blank line means blank_line/comment_line/directive + // call SLOC function to detect logical SLOC and add to result + LSLOC(result, line, fit->lineNumber, lineBak, strLSLOC, strLSLOCBak, found_block, + found_forifwhile, found_end, found_loop, loopLevel); + + cnt = 0; + CUtil::CountTally(line, data_name_list, cnt, 1, exclude, "", "", NULL, false); + + // need to check also if the data line continues + if (cnt > 0) + result->data_lines[PHY] += 1; + else + result->exec_lines[PHY] += 1; + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count, false); + } + } + } + return 1; +} + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results +* \param line processed physical line of code +* \param lineBak original physical line of code +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param found_block found block flag +* \param found_forifwhile found for, if, or while flag +* \param found_end found end flag +* \param found_loop found loop flag +* \param loopLevel nested loop level +*/ +void CPascalCounter::LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, bool &found_block, + bool &found_forifwhile, bool &found_end, bool &found_loop, StringVector &loopLevel) +{ + size_t start = 0; // starting index of the working string + size_t i, tempi, strSize; + string templine = CUtil::TrimString(line); + string tmp; + bool trunc_flag = false; + unsigned int loopCnt; + StringVector::iterator lit; + string keywordchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + // there may be more than 1 logical SLOC in a line + found_end = false; + for (i = 0; i < line.length(); i++) + { + if (line[i] == ';') + { + if (!found_end) + { + strSize = CUtil::TruncateLine(i - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + tmp = strLSLOC; + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, + found_block, found_forifwhile, found_end, trunc_flag); + + // record end loop for nested loop processing + if (print_cmplx) + { + if (found_loop) + { + found_loop = false; + loopLevel.push_back("do"); + + loopCnt = 0; + for (lit = loopLevel.begin(); lit < loopLevel.end(); lit++) + { + if ((*lit) != "") + loopCnt++; + } + if ((unsigned int)result->cmplx_nestloop_count.size() < loopCnt) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopCnt-1]++; + + loopLevel.pop_back(); + } + else if (CUtil::FindKeyword(tmp, "end", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "until", 0, TO_END_OF_STRING, false) != string::npos) + { + if (loopLevel.size() > 0) + loopLevel.pop_back(); + } + } + } + else + { + if (strLSLOC.size() > 0) + { + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, + found_block, found_forifwhile, found_end, trunc_flag); + } + found_end = false; // end xxx + found_block = false; + found_forifwhile = false; + strLSLOC = ""; + strLSLOCBak = ""; + } + found_loop = false; + start = i + 1; + } + + // if it ends in xxx, then it has already been counted, so ignore it + tmp = "xxx " + CUtil::TrimString(line.substr(start, i + 1 - start)); + if (CUtil::FindKeyword(tmp, "end", 0, TO_END_OF_STRING, false) != string::npos) + { + // check for 'end,' and skip + if ((line.length() > i + 1 && line[i + 1] == ',') || line[i] == ',') + continue; + + found_end = true; + found_block = false; + found_loop = false; + + // capture SLOC + strSize = CUtil::TruncateLine(i - 2 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, + found_block, found_forifwhile, found_end, trunc_flag); + + // if end is followed by a period 'end.' record SLOC if any + if (line.length() > i + 1 && line[i + 1] == '.') + { + // record end loop for nested loop processing + if (print_cmplx) + { + while (loopLevel.size() > 0) + loopLevel.pop_back(); + } + start = i + 2; + continue; + } + else + { + // record end loop for nested loop processing + if (print_cmplx) + { + if (loopLevel.size() > 0) + loopLevel.pop_back(); + } + start = i + 1; + } + } + + // continue the following processing only if line[i] is not in a middle of a word + if (keywordchars.find(line[i]) != string::npos && i < line.length() - 1) + continue; + + if (!found_end) + { + if (!found_forifwhile) + { + if (CUtil::FindKeyword(tmp, "for", 0 , TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "while", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "repeat", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "with", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "if", 0, TO_END_OF_STRING, false) != string::npos) + { + found_forifwhile = true; + } + + if (CUtil::FindKeyword(tmp, "do", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "repeat", 0, TO_END_OF_STRING, false) != string::npos) + { + if (CUtil::FindKeyword(tmp, "do", 0, TO_END_OF_STRING, false) != string::npos) + { + // found a SLOC + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, + found_block, found_forifwhile, found_end, trunc_flag); + start = i + 1; + + found_loop = true; + } + else + { + // record nested loop level + if (print_cmplx) + { + loopLevel.push_back("repeat"); + + loopCnt = 0; + for (lit = loopLevel.begin(); lit < loopLevel.end(); lit++) + { + if ((*lit) != "") + loopCnt++; + } + if ((unsigned int)result->cmplx_nestloop_count.size() < loopCnt) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopCnt-1]++; + } + } + continue; + } + } + else if (CUtil::FindKeyword(tmp, "do", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "then", 0, TO_END_OF_STRING, false) != string::npos) + { + // found a SLOC + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, + found_block, found_forifwhile, found_end, trunc_flag); + start = i + 1; + + if (CUtil::FindKeyword(tmp, "do", 0, TO_END_OF_STRING, false) != string::npos) + found_loop = true; + + continue; + } + + // process else since no ';' is allowed before else + if (CUtil::FindKeyword(tmp, "else", 0, TO_END_OF_STRING, false) != string::npos) + { + strSize = CUtil::TruncateLine(i - 4 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, + found_block, found_forifwhile, found_end, trunc_flag); + strLSLOC = strLSLOCBak = "else "; + start = i + 1; + continue; + } + + // process until since ';' is optional before else + if (CUtil::FindKeyword(tmp, "until", 0, TO_END_OF_STRING, false) != string::npos) + { + strSize = CUtil::TruncateLine(i - 5 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, + found_block, found_forifwhile, found_end, trunc_flag); + strLSLOC = strLSLOCBak = "until "; + start = i + 1; + continue; + } + + if (!found_block) + { + if (CUtil::FindKeyword(tmp, "begin", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "asm", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "case",0, TO_END_OF_STRING, false) != string::npos) + { + found_block = true; + + // record nested loop level + if (print_cmplx) + { + if (found_loop) + { + found_loop = false; + loopLevel.push_back("do"); + + loopCnt = 0; + for (lit = loopLevel.begin(); lit < loopLevel.end(); lit++) + { + if ((*lit) != "") + loopCnt++; + } + if ((unsigned int)result->cmplx_nestloop_count.size() < loopCnt) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopCnt-1]++; + } + else + loopLevel.push_back(""); + } + } + } + else + { + // only add new SLOC if 'of' is at the end of line and follows 'case', etc. + tempi = CUtil::FindKeyword(templine, "of", 0, TO_END_OF_STRING, false); + if (tempi == templine.length() - 2) + { + strSize = CUtil::TruncateLine(line.length() - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, + found_block, found_forifwhile, found_end, trunc_flag); + start = line.length(); + found_block = false; + continue; + } + } + + // check for '= record' + if (CUtil::FindKeyword(tmp, "= array", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "= record", 0, TO_END_OF_STRING, false) != string::npos) + { + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, + found_block, found_forifwhile, found_end, trunc_flag); + start = i + 1; + + if (print_cmplx) + loopLevel.push_back(""); + + continue; + } + } + } + + tmp = CUtil::TrimString(line.substr(start, i - start)); + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + tmp = CUtil::TrimString(lineBak.substr(start, i - start)); + strLSLOCBak += tmp.substr(0, strSize); + } + if (tmp == "") + { + found_forifwhile = false; + } +} + +/*! +* Processes a logical line of code. +* This method is called after a logical SLOC is determined. +* The method adds LSLOC to the result, increases counts, and resets variables. +* +* \param result counter results +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param found_block found block flag +* \param found_forifwhile found for, if, or while flag +* \param found_end found end flag +* \param trunc_flag truncate lines? +*/ +void CPascalCounter::FoundSLOC(results* result, size_t lineNumber, string &strLSLOC, string &strLSLOCBak, bool &found_block, + bool &found_forifwhile, bool &found_end, bool &trunc_flag) +{ + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + // add to the list for comparison purpose + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + { + // determine logical type, data declaration or executable + unsigned int cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", &result->data_name_count, false); + if (cnt > 0) + result->data_lines[LOG] += 1; + else + result->exec_lines[LOG] += 1; + + // reset all variables whenever a new statement/logical SLOC is found + strLSLOC = ""; + strLSLOCBak = ""; + found_block = false; + found_forifwhile = false; + found_end = false; + } +} + +/*! + * Parses lines for function/procedure names. + * + * \param line line to be processed + * \param lastline last line processed + * \param functionStack stack of functions + * \param functionName function name found + * + * \return 1 if function name is found + * \return 0 if it is still in some function + * \return 2 if the code line doesn't belong to any function + */ +int CPascalCounter::ParseFunctionName(const string &line, string &/*lastline*/, + filemap &functionStack, string &functionName, unsigned int &functionCount) +{ + string str; + size_t idx; + unsigned int fcnt; + + idx = CUtil::FindKeyword(line, "procedure"); + if (idx != string::npos) + { + if (idx + 10 < line.length()) + { + str = line.substr(idx + 10); + lineElement element(++functionCount, str); + functionStack.push_back(element); + } + } + else + { + idx = CUtil::FindKeyword(line, "function"); + if (idx != string::npos) + { + if (idx + 9 < line.length()) + { + str = line.substr(idx + 9); + lineElement element(++functionCount, str); + functionStack.push_back(element); + } + } + } + + if (functionStack.empty()) + { + // dealing with some code out of any subroutines, it a "main" code + return 2; + } + + idx = CUtil::FindKeyword(line, "end"); + if (idx != string::npos) + { + str = functionStack.back().line; + fcnt = functionStack.back().lineNumber; + functionStack.pop_back(); + idx = str.find("("); + if (idx != string::npos) + { + functionName = CUtil::ClearRedundantSpaces(str.substr(0, idx)); + functionCount = fcnt; + return 1; + } + } + return 0; +} + +/*! + * Counts file language complexity based on specified language keywords/characters. + * + * \param fmap list of processed file lines + * \param result counter results + * + * \return method status + */ +int CPascalCounter::CountComplexity(filemap* fmap, results* result) +{ + if (classtype == UNKNOWN || classtype == DATAFILE) + return 0; + filemap::iterator fit; + size_t idx; + unsigned int cnt, ret, cyclomatic_cnt = 0, ignore_cyclomatic_cnt = 0, main_cyclomatic_cnt = 0, function_count = 0, cyclomatic_logic_cnt = 0, main_cyclomatic_logic_cnt = 1, cyclomatic_case_cnt = 0, main_cyclomatic_case_cnt = 1; + string line, lastline, file_ext, function_name = ""; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + filemap function_stack; + stack cyclomatic_stack; + stack cyclomatic_logic_stack; + stack cyclomatic_case_stack; + map function_map; + map logical_map; + map case_map; + bool process_cyclomatic_complexity = false; + + //anthony begin + StringVector switch_case_key_list; + StringVector switch_case_stack; + switch_case_key_list.push_back(";"); + //andthony end + + // check whether to process cyclomatic complexity + if (cmplx_cyclomatic_list.size() > 0) + { + process_cyclomatic_complexity = true; + if (skip_cmplx_cyclomatic_file_extension_list.size() > 0) + { + idx = result->file_name.find_last_of("."); + if (idx != string::npos) + { + file_ext = result->file_name.substr(idx); + file_ext = CUtil::ToLower(file_ext); + if (find(skip_cmplx_cyclomatic_file_extension_list.begin(), skip_cmplx_cyclomatic_file_extension_list.end(), file_ext) != skip_cmplx_cyclomatic_file_extension_list.end()) + process_cyclomatic_complexity = false; + } + } + } + + // process each line + for (fit = fmap->begin(); fit != fmap->end(); fit++) + { + line = fit->line; + + if (CUtil::CheckBlank(line)) + continue; + + line = " " + line; + + // mathematical functions + cnt = 0; + CUtil::CountTally(line, math_func_list, cnt, 1, exclude, "", "", &result->math_func_count, casesensitive); + result->cmplx_math_lines += cnt; + + // trigonometric functions + cnt = 0; + CUtil::CountTally(line, trig_func_list, cnt, 1, exclude, "", "", &result->trig_func_count, casesensitive); + result->cmplx_trig_lines += cnt; + + // logarithmic functions + cnt = 0; + CUtil::CountTally(line, log_func_list, cnt, 1, exclude, "", "", &result->log_func_count, casesensitive); + result->cmplx_logarithm_lines += cnt; + + // calculations + cnt = 0; + CUtil::CountTally(line, cmplx_calc_list, cnt, 1, exclude, "", "", &result->cmplx_calc_count, casesensitive); + result->cmplx_calc_lines += cnt; + + // conditionals + cnt = 0; + CUtil::CountTally(line, cmplx_cond_list, cnt, 1, exclude, "", "", &result->cmplx_cond_count, casesensitive); + result->cmplx_cond_lines += cnt; + + // logical operators + cnt = 0; + CUtil::CountTally(line, cmplx_logic_list, cnt, 1, exclude, "", "", &result->cmplx_logic_count, casesensitive); + result->cmplx_logic_lines += cnt; + + // preprocessor directives + cnt = 0; + CUtil::CountTally(line, cmplx_preproc_list, cnt, 1, exclude, "", "", &result->cmplx_preproc_count, casesensitive); + result->cmplx_preproc_lines += cnt; + + // assignments + cnt = 0; + CUtil::CountTally(line, cmplx_assign_list, cnt, 1, exclude, "", "", &result->cmplx_assign_count, casesensitive); + result->cmplx_assign_lines += cnt; + + // pointers + cnt = 0; + // Pointers are embedded syntax so there is NO exclude string or include strings + CUtil::CountTally(line, cmplx_pointer_list, cnt, 1, "", "", "", &result->cmplx_pointer_count, casesensitive); + result->cmplx_pointer_lines += cnt; + + // cyclomatic complexity + if (process_cyclomatic_complexity) + { + // search for cyclomatic complexity keywords + unsigned int temp = 0; + CUtil::CountTally(line, cmplx_cyclomatic_list, temp, 1, exclude, "", "", 0, casesensitive); + cyclomatic_cnt += temp; + + CUtil::CountTally(line, cmplx_cyclomatic_case_list, cyclomatic_case_cnt, 1, exclude, "", "", 0, casesensitive); + cyclomatic_case_cnt += temp; + + //anthony 2013.11.5 + //search for keyword "case" + size_t idx_case = CUtil::FindKeyword(line, "case"); + if (idx_case != string::npos) { + switch_case_stack.push_back("case"); + } + + //only if switch_case_stack is not empty, we will search keyword "begin" and ";" and "end;" + if(!switch_case_stack.empty()){ + size_t idx = CUtil::FindKeyword(line, "begin"); + if (idx != string::npos) { + switch_case_stack.push_back("begin"); + } + + //if the top of switch_case_stack is "case", then for each ";" we increment cyclomatic_cnt + //if the top of switch_case_stack is "begin", we don't need to count ";" because each + //"begin...end " block counts one + if(switch_case_stack.back().compare("case") == 0){ + unsigned int semicol_cnt = 0; + CUtil::CountTally(line, switch_case_key_list, semicol_cnt, 1, "end", "", "", 0, casesensitive); + if(semicol_cnt != 0){ + cyclomatic_cnt++; + } + } + + //search for keyword "end;" + idx = CUtil::FindKeyword(line, "end;"); + + if (idx != string::npos) { + //if the top of switch_case_stack is "begin", we increment cyclomatic_cnt because we get + //one "begin...end" block, and pop out. + if(switch_case_stack.back().compare("begin")==0){ + switch_case_stack.pop_back(); + cyclomatic_cnt++; + } + //otherwise, we reach the end of a "case" structure, we simply pop out. + else{ + switch_case_stack.pop_back(); + } + } + } + + // search for keywords to exclude + if (ignore_cmplx_cyclomatic_list.size() > 0) + CUtil::CountTally(line, ignore_cmplx_cyclomatic_list, ignore_cyclomatic_cnt, 1, exclude, "", "", 0, casesensitive); + + // search for cyclomatic complexity logical keywords + if (cmplx_cyclomatic_logic_list.size() > 0) + CUtil::CountTally(line, cmplx_cyclomatic_logic_list, cyclomatic_logic_cnt, 1, exclude, "", "", 0, casesensitive); + + // parse function name if found + ret = (unsigned)ParseFunctionName(line, lastline, function_stack, function_name, function_count); + if (ret != 1 && !cyclomatic_stack.empty() && cyclomatic_stack.size() == function_stack.size()) + { + // remove count stack entry for non-function names + cyclomatic_cnt += cyclomatic_stack.top(); + ignore_cyclomatic_cnt = 0; + cyclomatic_stack.pop(); + } + if (ret != 1 && !cyclomatic_logic_stack.empty() && cyclomatic_logic_stack.size() == function_stack.size()) + { + // remove count stack entry for non-function names + cyclomatic_logic_cnt += cyclomatic_logic_stack.top(); + cyclomatic_logic_stack.pop(); + } + if (ret != 1 && !cyclomatic_case_stack.empty() && cyclomatic_case_stack.size() == function_stack.size()) + { + // remove count stack entry for non-function names + cyclomatic_case_cnt += cyclomatic_case_stack.top(); + cyclomatic_case_stack.pop(); + } + if (ret == 1) + { + // capture count at end of function + lineElement element(cyclomatic_cnt - ignore_cyclomatic_cnt + 1, function_name); + function_map[function_count] = element; + + lineElement n_element(cyclomatic_cnt - ignore_cyclomatic_cnt + cyclomatic_logic_cnt + 1, function_name); + logical_map[function_count] = n_element; + + lineElement c_element(cyclomatic_case_cnt - ignore_cyclomatic_cnt + 1, function_name); + case_map[function_count] = c_element; + + if (!function_stack.empty()) + { + // grab previous function from stack to continue + if (!cyclomatic_stack.empty()) + { + cyclomatic_cnt = cyclomatic_stack.top(); + cyclomatic_stack.pop(); + } + if (!cyclomatic_logic_stack.empty()) + { + cyclomatic_logic_cnt = cyclomatic_logic_stack.top(); + cyclomatic_logic_stack.pop(); + } + if (!cyclomatic_case_stack.empty()) + { + cyclomatic_case_cnt = cyclomatic_case_stack.top(); + cyclomatic_case_stack.pop(); + } + } + else { + cyclomatic_cnt = 0; + cyclomatic_logic_cnt = 0; + cyclomatic_case_cnt = 0; + } + function_name = ""; + ignore_cyclomatic_cnt = 0; + } + else if (ret == 2) + { + if (main_cyclomatic_cnt < 1) + main_cyclomatic_cnt = 1; // add 1 for main function here in case no other decision points are found in main + // some code doesn't belong to any function + main_cyclomatic_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt; + main_cyclomatic_logic_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt + cyclomatic_logic_cnt; + main_cyclomatic_case_cnt += cyclomatic_case_cnt - ignore_cyclomatic_cnt; + cyclomatic_cnt = ignore_cyclomatic_cnt = cyclomatic_logic_cnt = cyclomatic_case_cnt = 0; + } + else { + if (!function_stack.empty() && (function_stack.size() > cyclomatic_stack.size() + 1 || (cyclomatic_stack.empty() && function_stack.size() > 1))) + { + // capture previous complexity count from open function + cyclomatic_stack.push(cyclomatic_cnt - ignore_cyclomatic_cnt); + cyclomatic_cnt = ignore_cyclomatic_cnt = 0; + } + if (!function_stack.empty() && (function_stack.size() > cyclomatic_logic_stack.size() + 1 || (cyclomatic_logic_stack.empty() && function_stack.size() > 1))) + { + // capture previous complexity count from open function + cyclomatic_logic_stack.push(cyclomatic_logic_cnt); + cyclomatic_logic_cnt = 0; + } + if (!function_stack.empty() && (function_stack.size() > cyclomatic_case_stack.size() + 1 || (cyclomatic_case_stack.empty() && function_stack.size() > 1))) + { + // capture previous complexity count from open function + cyclomatic_case_stack.push(cyclomatic_case_cnt); + cyclomatic_case_cnt = 0; + } + } + } + } + + // done with a file + if (main_cyclomatic_cnt > 0) + { + // add "main" code + lineElement element(main_cyclomatic_cnt, "main"); + lineElement n_element(main_cyclomatic_logic_cnt, "main"); + lineElement c_element(main_cyclomatic_case_cnt, "main"); + function_map[0] = element; + logical_map[0] = n_element; + case_map[0] = c_element; + } + else + { + // finish the first function if not closed + while (!function_stack.empty()) + { + function_name = function_stack.back().line; + function_count = function_stack.back().lineNumber; + function_stack.pop_back(); + + if (!function_stack.empty()) + { + // grab previous function from stack to continue + if (!cyclomatic_stack.empty()) + { + cyclomatic_cnt = cyclomatic_stack.top(); + cyclomatic_stack.pop(); + } + } + else + { + // capture count at end of function + lineElement element(cyclomatic_cnt + 1, function_name); + lineElement n_element(cyclomatic_cnt + cyclomatic_logic_cnt + 1, function_name); + lineElement c_element(cyclomatic_case_cnt - ignore_cyclomatic_cnt + 1, function_name); + function_map[function_count] = element; + logical_map[function_count] = n_element; + case_map[function_count] = c_element; + } + } + } + + // process ordered functions + for (map::iterator it = function_map.begin(); it != function_map.end(); ++it) + result->cmplx_cycfunct_count.push_back(it->second); + if(cmplx_cyclomatic_logic_list.size() > 0) + { + for (map::iterator it = logical_map.begin(); it != logical_map.end(); ++it) + result->cmplx_cycfunct_CC2_count.push_back(it->second); + } + if(cmplx_cyclomatic_case_list.size() > 0) + { + for (map::iterator it = case_map.begin(); it != case_map.end(); ++it) + result->cmplx_cycfunct_CC3_count.push_back(it->second); + } + return 1; +} diff --git a/src/CPascalCounter.h b/src/CPascalCounter.h new file mode 100644 index 0000000..06563f7 --- /dev/null +++ b/src/CPascalCounter.h @@ -0,0 +1,48 @@ +//! Code counter class definition for the Pascal language. +/*! +* \file CPascalCounter.h +* +* This file contains the code counter class definition for the Pascal language. +*/ + +#ifndef CPascalCounter_h +#define CPascalCounter_h + +#include "CCodeCounter.h" + +//! Pascal code counter class. +/*! +* \class CPascalCounter +* +* Defines the Pascal counter class. +*/ +class CPascalCounter : public CCodeCounter +{ +public: + CPascalCounter(); + +protected: + virtual int CountCommentsSLOC(filemap* fmap, results* result, filemap* fmapBak = NULL); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapmBak = NULL); + void LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + bool &found_block, bool &found_forifwhile, bool &found_end, bool &found_loop, StringVector &loopLevel); + void FoundSLOC(results* result, size_t lineNumber, string &strLSLOC, string &strLSLOCBak, bool &found_block, + bool &found_forifwhile, bool &found_end, bool &trunc_flag); + int ParseFunctionName(const string &line, string &lastline, filemap &functionStack, string &functionName, + unsigned int &functionCount); + int CountComplexity(filemap* fmap, results* result); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CPascalCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CPascalCounter(const CPascalCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CPascalCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CPascalCounter operator=(const CPascalCounter); // Declare without implementation +}; + +#endif diff --git a/src/CPerlCounter.cpp b/src/CPerlCounter.cpp new file mode 100644 index 0000000..49b49f4 --- /dev/null +++ b/src/CPerlCounter.cpp @@ -0,0 +1,990 @@ +//! Code counter class methods for the Perl language. +/*! +* \file CPerlCounter.cpp +* +* This file contains the code counter class methods for the Perl language. +*/ + +#include "CPerlCounter.h" + +/*! +* Constructs a CPerlCounter object. +*/ +CPerlCounter::CPerlCounter() +{ + classtype = PERL; + language_name = "Perl"; + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("Perl", file_extension); + //file_extension.push_back(".pl"); + //file_extension.push_back(".pm"); + + LineCommentStart.push_back("#"); + + QuoteStart = "\"'/"; + QuoteEnd = "\"'/"; + QuoteEscapeRear = '\"'; + + directive.push_back("import"); + directive.push_back("no"); + directive.push_back("package"); + directive.push_back("require"); + directive.push_back("use"); + + data_name_list.push_back("AUTOLOAD"); + data_name_list.push_back("BEGIN"); + data_name_list.push_back("CHECK"); + data_name_list.push_back("CORE"); + data_name_list.push_back("DESTROY"); + data_name_list.push_back("END"); + data_name_list.push_back("INIT"); + data_name_list.push_back("NULL"); + + exec_name_list.push_back("catch"); + exec_name_list.push_back("elsif"); + exec_name_list.push_back("eval"); + exec_name_list.push_back("for"); + exec_name_list.push_back("foreach"); + exec_name_list.push_back("if"); + exec_name_list.push_back("sub"); + exec_name_list.push_back("switch"); + exec_name_list.push_back("try"); + exec_name_list.push_back("unless"); + exec_name_list.push_back("until"); + exec_name_list.push_back("while"); + + math_func_list.push_back("abs"); + math_func_list.push_back("exp"); + math_func_list.push_back("int"); + math_func_list.push_back("rand"); + math_func_list.push_back("sqrt"); + math_func_list.push_back("srand"); + math_func_list.push_back("time"); + + trig_func_list.push_back("acos"); + trig_func_list.push_back("acosh"); + trig_func_list.push_back("asinh"); + trig_func_list.push_back("atanh"); + trig_func_list.push_back("asin"); + trig_func_list.push_back("atan"); + trig_func_list.push_back("atan2"); + trig_func_list.push_back("cos"); + trig_func_list.push_back("cosh"); + trig_func_list.push_back("sin"); + trig_func_list.push_back("sinh"); + trig_func_list.push_back("tan"); + trig_func_list.push_back("tanh"); + + log_func_list.push_back("log"); + + cmplx_calc_list.push_back("%"); + cmplx_calc_list.push_back("^"); + cmplx_calc_list.push_back("++"); + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("--"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("/"); + cmplx_calc_list.push_back(">>"); + cmplx_calc_list.push_back("<<"); + + cmplx_cond_list.push_back("elsif"); + cmplx_cond_list.push_back("for"); + cmplx_cond_list.push_back("foreach"); + cmplx_cond_list.push_back("if"); + cmplx_cond_list.push_back("unless"); + cmplx_cond_list.push_back("until"); + cmplx_cond_list.push_back("while"); + + cmplx_logic_list.push_back("&&"); + cmplx_logic_list.push_back("||"); + cmplx_logic_list.push_back("=="); + cmplx_logic_list.push_back("<=>"); + cmplx_logic_list.push_back("!"); + cmplx_logic_list.push_back("and"); + cmplx_logic_list.push_back("not"); + cmplx_logic_list.push_back("or"); + cmplx_logic_list.push_back("xor"); + cmplx_logic_list.push_back("~"); + cmplx_logic_list.push_back(">"); + cmplx_logic_list.push_back("<"); + cmplx_logic_list.push_back(">="); + cmplx_logic_list.push_back("=<"); + cmplx_logic_list.push_back("lt"); + cmplx_logic_list.push_back("gt"); + cmplx_logic_list.push_back("ge"); + cmplx_logic_list.push_back("le"); + cmplx_logic_list.push_back("eq"); + cmplx_logic_list.push_back("ne"); + cmplx_logic_list.push_back("cmp"); + + cmplx_preproc_list.push_back("import"); + cmplx_preproc_list.push_back("no"); + cmplx_preproc_list.push_back("package"); + cmplx_preproc_list.push_back("require"); + cmplx_preproc_list.push_back("use"); + + cmplx_assign_list.push_back("="); + + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("elsif"); + cmplx_cyclomatic_list.push_back("case"); + cmplx_cyclomatic_list.push_back("while"); + cmplx_cyclomatic_list.push_back("until"); + cmplx_cyclomatic_list.push_back("for"); + cmplx_cyclomatic_list.push_back("foreach"); + cmplx_cyclomatic_list.push_back("catch"); + cmplx_cyclomatic_list.push_back("unless"); + cmplx_cyclomatic_list.push_back("?"); + + cmplx_cyclomatic_logic_list.push_back("||"); + cmplx_cyclomatic_logic_list.push_back("&&"); + //cmplx_cyclomatic_logic_list.push_back("^"); + cmplx_cyclomatic_logic_list.push_back("and"); + cmplx_cyclomatic_logic_list.push_back("or"); + //cmplx_cyclomatic_logic_list.push_back("xor"); + + cmplx_cyclomatic_case_list.push_back("case"); + cmplx_cyclomatic_switch_list.push_back("switch"); +} + +/*! +* Perform preprocessing of file lines before counting. +* +* \param fmap list of file lines +* +* \return method status +*/ +int CPerlCounter::PreCountProcess(filemap* fmap) +{ + size_t i, j; + filemap::iterator fit; + for (fit = fmap->begin(); fit != fmap->end(); fit++) + { + if (fit->line.empty()) + continue; + // check for $#, $', $", $`, {$...} + for (i = fit->line.length() - 1; i > 0; i--) + { + if (fit->line[i-1] == '{' && fit->line[i] == '$') + { + fit->line[i-1] = '$'; + for (j = i+1; j < fit->line.length(); j++) + { + if (fit->line[j] == '}') + { + fit->line[j] = '$'; + break; + } + } + } + if (fit->line[i-1] == '$' && + (fit->line[i] == '#' || fit->line[i] == '\'' || fit->line[i] == '"' || fit->line[i] == '`')) + fit->line[i] = '$'; + } + } + return 0; +} + +/*! +* Handles special case for Perl regexp operators m// s/// tr///. +* +* \param strline string to be processed +* \param idx_start index of line character to start search +* \param contd specifies the quote string is continued from the previous line +* \param CurrentQuoteEnd end quote character of the current status +* +* \return method status +*/ +int CPerlCounter::ReplaceQuote(string &strline, size_t &idx_start, bool &contd, char &CurrentQuoteEnd) +{ + static bool inRegexp = false; + static int slashCount = 1; // =1 for match m//, =1 for s/// and tr/// + size_t idx = idx_start; + size_t i = idx; + + if (inRegexp || strline[idx] == '/') + { + // replace all "\\" by "$$" + size_t start = idx_start; + while ((start = strline.find("\\\\", start)) != string::npos) + { + strline.replace(start, 2, "$$"); + start += 2; + } + + while (i < strline.length()) + { + if (inRegexp) + { + if ((strline[i] == '/' && (i == 0 || (i > 0 && strline[i - 1] != '\\'))) + || (contd && strline[i] == ';')) + { + // replace everything in the regexp + slashCount--; + if (slashCount == 0) + { + strline.replace(idx, i - idx + 1, i - idx + 1, '$'); + inRegexp = false; + contd = false; + idx = i + 1; + idx_start = idx; + return 1; + } + } + } + else if (strline[i] == '/') + { + if ((i > 0 && strline[i-1] == 's') || (i > 1 && strline[i-1] == 'r' && strline[i-2] == 't')) + slashCount = 2; + else + slashCount = 1; + idx = i; + inRegexp = true; + } + + // quick fix to replace '#' + if (i > 0 && strline[i] == '#' && strline[i-1] == '$') + strline[i] = '$'; + + i++; + } + + if (inRegexp) + { + strline.replace(idx, i - idx, i - idx, '$'); + contd = true; + } + } + idx_start = idx; + + if (!inRegexp) + return CCodeCounter::ReplaceQuote(strline, idx_start, contd, CurrentQuoteEnd); + + return 1; +} + +/*! +* Counts directive lines of code. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CPerlCounter::CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak) +{ + bool contd = false, trunc_flag = false; + size_t idx, strSize; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$/\\<>.+?!@#$%^&*()-+*"; + string strDirLine = ""; + + filemap::iterator itfmBak = fmapBak->begin(); + for (filemap::iterator iter = fmap->begin(); iter!=fmap->end(); iter++, itfmBak++) + { + if (CUtil::CheckBlank(iter->line)) + continue; + size_t lineNumber = iter->lineNumber; + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(" " + iter->line, directive, cnt, 1, exclude, "", "", &result->directive_count); + } + + if (!contd) + { + // if not a continuation of a previous directive + for (vector::iterator viter = directive.begin(); viter != directive.end(); viter++) + { + // ensures the keyword stands alone, avoid, e.g., pack instead of package + if (((idx = CUtil::FindKeyword(iter->line, *viter)) != string::npos) && idx == 0) + { + contd = true; + break; + } + } + if (contd) + { + strSize = CUtil::TruncateLine(itfmBak->line.length(), 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine = itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + } + else + { + // continuation of a previous directive + strSize = CUtil::TruncateLine(itfmBak->line.length(), strDirLine.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine += "\n" + itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + + if (contd) + { + // if a directive or continuation of a directive (no continuation symbol found) + if (iter->line[iter->line.length()-1] == ';') + { + contd = false; + if (result->addSLOC(strDirLine, lineNumber, trunc_flag)) + result->directive_lines[LOG]++; + } + iter->line = ""; + } + } + return 1; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CPerlCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + string strLSLOC; + string strLSLOCBak; + unsigned int l_paren_cnt = 0; + bool l_forflag, found_forifwhile, found_while; + char prev_char = 0; + l_forflag = found_forifwhile = found_while = false; + + bool comment = false; + unsigned int cnt = 0; + size_t comPos = string::npos; + size_t p; + filemap::iterator fit, fitbak; + string line, lineBak; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"; + bool data_continue = false; + unsigned int phys_exec_lines = 0; + unsigned int phys_data_lines = 0; + unsigned int temp_lines = 0; + unsigned int openBrackets = 0; + StringVector loopLevel; + + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + // insert blank at the beginning(for searching keywords) + line = ' ' + fit->line; + lineBak = ' ' + fitbak->line; + size_t lineNumber = fit->lineNumber; + + if (!CUtil::CheckBlank(line)) + { + if (comment == true) + { + p = line.find("\n"); + if (p != string::npos) + { + line.replace(0, p + 2, " "); + lineBak.replace(0, p + 2, " "); + } + else + continue; + } + + comPos = line.find("#"); + if (comPos == string::npos) + comment = false; + else + { + comment = true; + p = line.substr(comPos + 3).find(""); + if (p != string::npos) + { + line.replace(comPos, p - comPos + 1, " "); + lineBak.replace(comPos, p - comPos + 1, " "); + comment = false; + } + else + { + line.replace(comPos, line.size() - comPos, " "); + lineBak.replace(comPos, lineBak.size() - comPos, " "); + } + } + + LSLOC(result, line, lineNumber, lineBak, strLSLOC, strLSLOCBak, l_paren_cnt, l_forflag, found_forifwhile, + found_while, prev_char, data_continue, temp_lines, phys_exec_lines, phys_data_lines, openBrackets, loopLevel); + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count); + } + + result->exec_lines[PHY] += phys_exec_lines; + phys_exec_lines = 0; + + result->data_lines[PHY] += phys_data_lines; + phys_data_lines = 0; + } + } + return 1; +} + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results +* \param line processed physical line of code +* \param lineBak original physical line of code +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param paren_cnt count of parenthesis +* \param forflag found for flag +* \param found_forifwhile found for, if, or while flag +* \param found_while found while flag +* \param prev_char previous character +* \param data_continue continuation of a data declaration line +* \param temp_lines tracks physical line count +* \param phys_exec_lines number of physical executable lines +* \param phys_data_lines number of physical data lines +* \param openBrackets number of open brackets (no matching close bracket) +* \param loopLevel nested loop level +*/ +void CPerlCounter::LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, unsigned int &paren_cnt, + bool &forflag, bool &found_forifwhile, bool &found_while, char &prev_char, bool &data_continue, + unsigned int &temp_lines, unsigned int &phys_exec_lines, unsigned int &phys_data_lines, + unsigned int &openBrackets, StringVector &loopLevel) +{ + size_t start = 0; // starting index of the working string + size_t i = 0, strSize, pos; + bool do_boolean, trunc_flag = false; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"; + unsigned int cnt = 0; + unsigned int loopCnt = 0; + StringVector::iterator lit; + string tmp = CUtil::TrimString(strLSLOC); + string tmp2; + + // check for the keyword do + do_boolean = (CUtil::FindKeyword(tmp, "do") == tmp.length() - 2); + + // check the entire line for SLOC present in it + while (i < line.length()) + { + if (line[i] == ';' || line[i] == '{') + { + // LSLOC terminators + // ';' any valid perl command ends with a ';' terminator + // do statements start with a '{' and ends with '}' + + if (line[i] == ';' && paren_cnt > 0) + { + // for a 'for' statement counter is incremented. + i++; + continue; + } + + // record open bracket for nested loop processing + if (print_cmplx) + { + if (line[i] == '{') + { + openBrackets++; + if ((unsigned int)loopLevel.size() < openBrackets) + loopLevel.push_back(""); + } + else + { + if ((unsigned int)loopLevel.size() > openBrackets && openBrackets > 0) + loopLevel.pop_back(); + } + } + + if (found_while && found_forifwhile) + { + found_while = false; + found_forifwhile = false; + start = i + 1; + i++; + continue; + } + + if (line[i] == '{') + { + // case for(...); and if (...) { + // these specials are handled + if (found_forifwhile) + { + found_forifwhile = false; + start = i + 1; + i++; + continue; + } + + // check if 'do' precedes '{' + if (!do_boolean) + { + // find for 'do' in string before tmp string + tmp = CUtil::TrimString(line.substr(start, i - start)); + + // check for 'do' statement + do_boolean = (tmp == "do"); + } + if (do_boolean) + { + if (print_cmplx) + { + if (loopLevel.size() > 0) loopLevel.pop_back(); + loopLevel.push_back("do"); + } + + do_boolean = false; + start = i + 1; + i++; + continue; // do not store '{' following 'do' + } + } + + if (line[i] == ';' && prev_char == '}') + { + i++; + continue; + } + + // the 'for(...)' or 'while(..)' or anything with the '{' on the next line gets counted as an extra SLOC + // so to avoid that increment the counter and continue + if (line[i] == '{' && prev_char == ')') + { + i++; + continue; + } + + // check for expression modifiers using 'foreach', 'while', 'if', 'unless', 'until' (for example, statement unless condition;) + pos = string::npos; + if (line[i] == ';') + { + // check for empty statement (=1 LSLOC) + if (CUtil::TrimString(line.substr(start, i + 1 - start)) == ";" && strLSLOC.length() < 1) + { + strLSLOC = ";"; + strLSLOCBak = ";"; + } + else + { + tmp = CUtil::TrimString(strLSLOC + line.substr(start, i + 1 - start)); + pos = CUtil::FindKeyword(tmp, "foreach"); + if (pos == string::npos) + { + pos = CUtil::FindKeyword(tmp, "while"); + if (pos == string::npos) + + pos = CUtil::FindKeyword(tmp, "if"); + if (pos == string::npos) + { + pos = CUtil::FindKeyword(tmp, "unless"); + if (pos == string::npos) + pos = CUtil::FindKeyword(tmp, "until"); + } + } + } + } + if (pos != string::npos) + { + // capture statement before modifier + tmp2 = CUtil::TrimString(strLSLOCBak + lineBak.substr(start, i - start)); + strSize = CUtil::TruncateLine(pos, 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC = tmp.substr(0, strSize); + strLSLOCBak = tmp2.substr(0, strSize); + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + strLSLOC = ""; + strLSLOCBak = ""; + + strSize = CUtil::TruncateLine(tmp.length() - pos, 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC = tmp.substr(pos, strSize); + strLSLOCBak = tmp2.substr(pos, strSize); + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + found_forifwhile = false; + } + else + { + strSize = CUtil::TruncateLine(i - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + tmp = strLSLOC; + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + { + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", &result->data_name_count); + + temp_lines++; + if (data_continue == true) + { + result->data_lines[LOG]++; + phys_data_lines = temp_lines; + } + else + { + if (cnt > 0) + { + result->data_lines[LOG]++; + phys_data_lines = temp_lines; + } + else + { + result->exec_lines[LOG]++; + phys_exec_lines = temp_lines; + } + } + } + else if (data_continue == true) + phys_data_lines = temp_lines; + else + phys_exec_lines = temp_lines; + } + data_continue = false; + temp_lines = 0; + strLSLOC = ""; + strLSLOCBak = ""; + start = i + 1; + } + else if (line[i] == '}') + { + // also, {} is also skipped, empty block is not counted + if (prev_char == ';' || prev_char == '{') + start = i + 1; + + // record close bracket for nested loop processing + if (print_cmplx) + { + if (openBrackets > 0) + openBrackets--; + if (loopLevel.size() > 0) + loopLevel.pop_back(); + } + } + else if (line[i] == '(') + { + if (!forflag) + { + // handle 'for', 'foreach', 'while', 'if', 'elsif, and 'unless' + tmp = "xxx " + CUtil::TrimString(line.substr(start, i)); + if ((CUtil::FindKeyword(tmp, "for") != string::npos) || (CUtil::FindKeyword(tmp, "foreach") != string::npos) || + (CUtil::FindKeyword(tmp, "while")!= string::npos) || (CUtil::FindKeyword(tmp, "if") != string::npos) || + (CUtil::FindKeyword(tmp, "elsif") != string::npos) || (CUtil::FindKeyword(tmp, "unless") != string::npos) || + (CUtil::FindKeyword(tmp, "until") != string::npos)) + { + forflag = true; + paren_cnt++; + + if (print_cmplx && (loopLevel.size() > openBrackets) && (openBrackets > 0)) + loopLevel.pop_back(); + + if (CUtil::FindKeyword(tmp, "while")!= string::npos) + { + if (print_cmplx) + loopLevel.push_back("while"); + found_while = true; + } + else if (CUtil::FindKeyword(tmp, "until")!= string::npos) + { + if (print_cmplx) + loopLevel.push_back("until"); + found_while = true; + } + else if (print_cmplx) + { + if (CUtil::FindKeyword(tmp, "for") != string::npos) + loopLevel.push_back("for"); + else if (CUtil::FindKeyword(tmp, "foreach") != string::npos) + loopLevel.push_back("foreach"); + + // record nested loop level + if (CUtil::FindKeyword(tmp, "if") == string::npos && CUtil::FindKeyword(tmp, "elsif") == string::npos && + CUtil::FindKeyword(tmp, "unless") == string::npos) + { + loopCnt = 0; + for (lit = loopLevel.begin(); lit < loopLevel.end(); lit++) + { + if ((*lit) != "") + loopCnt++; + } + if ((unsigned int)result->cmplx_nestloop_count.size() < loopCnt) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopCnt-1]++; + } + } + } + } + else + paren_cnt++; + } + else if (line[i] == ')') + { + /* + cases + 'while(...);', + 'while(...) {' and + '} while(...);' + is handled in this case + */ + if (forflag) + { + if (paren_cnt > 0) + paren_cnt--; + if (paren_cnt == 0) + { + // handling 'for', 'foreach', 'while', 'if', 'elsif', 'unless', 'until' + // check for expression modifiers using 'foreach', 'while', 'if', 'unless', 'until' (for example, statement unless (condition);) + tmp = CUtil::TrimString(strLSLOC + line.substr(start, i + 1 - start)); + pos = CUtil::FindKeyword(tmp, "foreach"); + if (pos == string::npos) + { + pos = CUtil::FindKeyword(tmp, "while"); + if (pos == string::npos) + { + pos = CUtil::FindKeyword(tmp, "if"); + if (pos == string::npos) + { + pos = CUtil::FindKeyword(tmp, "unless"); + if (pos == string::npos) + pos = CUtil::FindKeyword(tmp, "until"); + } + } + } + if (pos != string::npos) + { + // capture statement before modifier + tmp2 = CUtil::TrimString(strLSLOCBak + lineBak.substr(start, i + 1 - start)); + strSize = CUtil::TruncateLine(pos, 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC = tmp.substr(0, strSize); + strLSLOCBak = tmp2.substr(0, strSize); + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + strLSLOC = ""; + strLSLOCBak = ""; + + strSize = CUtil::TruncateLine(tmp.length() - pos, 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC = tmp.substr(pos, strSize); + strLSLOCBak = tmp2.substr(pos, strSize); + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + found_forifwhile = false; + + // skip trailing ';' + tmp = CUtil::TrimString(line.substr(i + 1)); + if (tmp.length() > 0 && tmp[0] == ';') + i++; + } + else + { + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + tmp = strLSLOC; + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + found_forifwhile = true; + } + strLSLOC = ""; + strLSLOCBak = ""; + phys_exec_lines = temp_lines; + temp_lines = 0; + start = i + 1; + forflag = false; + } + } + } + + if (line[i] != ' ' && line[i] != '\t') + { + // if ;}}} --> don't count }}} at all + // also, if {}}} --> don't count }}} at all + if (!(line[i] == '}' && (prev_char == ';' || prev_char == '{'))) // see case '}' above + prev_char = line[i]; + + // change to not found if a char appears before + if (line[i] != ')' && found_forifwhile) + found_forifwhile = false; + + if (CUtil::FindKeyword(line, "or", i, i + 2, true) == i) + { + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + tmp = strLSLOC; + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + phys_exec_lines = temp_lines; + temp_lines = 0; + strLSLOC = ""; + strLSLOCBak = ""; + start = i; + } + } + i++; + } + + tmp2 = CUtil::TrimString(line.substr(start, i - start)); + strSize = CUtil::TruncateLine(tmp2.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp2.substr(0, strSize); + tmp2 = CUtil::TrimString(lineBak.substr(start, i - start)); + strLSLOCBak += tmp2.substr(0, strSize); + } + if (tmp == "") + found_forifwhile = found_while = false; + + // make sure that we are not beginning to process a new data line + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", NULL); + + if (cnt > 0) + data_continue = true; + if (data_continue) + temp_lines++; + if (temp_lines == 0 && phys_data_lines == 0 && phys_exec_lines == 0) + phys_exec_lines = 1; +} + +/*! +* Parses lines for function/method names. +* +* \param line line to be processed +* \param lastline last line processed +* \param functionStack stack of functions +* \param functionName function name found +* \param functionCount function count found +* +* \return 1 if function name is found +*/ +int CPerlCounter::ParseFunctionName(const string &line, string &lastline, + filemap &functionStack, string &functionName, unsigned int &functionCount) +{ + string tline, str; + size_t i, idx, tidx, cnt; + unsigned int fcnt; + + tline = CUtil::TrimString(line); + idx = tline.find('{'); + if (idx != string::npos) + { + // check whether it is at first index, if yes then function name is at above line + if (idx == 0) + { + lineElement element(++functionCount, lastline); + functionStack.push_back(element); + lastline.erase(); + } + else + { + str = tline.substr(0, idx); + if (str.find('(') != string::npos && str[0] != '(') + lastline = str; + else + lastline += " " + str; + lineElement element(++functionCount, CUtil::TrimString(lastline)); + functionStack.push_back(element); + lastline.erase(); + } + } + else if (tline.length() > 0 && tline[tline.length() - 1] != ';' && + lastline.length() > 0 && lastline[lastline.length() - 1] != ';') + { + // append until all parenthesis are closed + tidx = lastline.find('('); + if (tidx != string::npos) + { + cnt = 1; + while (tidx != string::npos) + { + tidx = lastline.find('(', tidx + 1); + if (tidx != string::npos) + cnt++; + } + tidx = lastline.find(')'); + while (tidx != string::npos) + { + cnt++; + tidx = lastline.find(')', tidx + 1); + } + if (cnt % 2 != 0) + lastline += " " + tline; + else + lastline = tline; + } + else + lastline = tline; + } + else + lastline = tline; + + idx = line.find('}'); + if (idx != string::npos && !functionStack.empty()) + { + str = functionStack.back().line; + fcnt = functionStack.back().lineNumber; + functionStack.pop_back(); + idx = CUtil::FindKeyword(str, "sub"); + if (idx != string::npos && idx + 4 < str.length()) + { + functionName = CUtil::ClearRedundantSpaces(str.substr(idx + 4)); + functionCount = fcnt; + lastline.erase(); + return 1; + } + lastline.erase(); + } + + // check stack for any "sub" + idx = string::npos; + if (!functionStack.empty()) + { + for (i = 0; i < functionStack.size(); i++) + { + idx = CUtil::FindKeyword(functionStack[i].line, "sub"); + if (idx != string::npos) + break; + } + } + if (idx == string::npos) + { + // dealing with some code out of any subroutines, it a "main" code + return 2; + } + return 0; +} diff --git a/src/CPerlCounter.h b/src/CPerlCounter.h new file mode 100644 index 0000000..67a807e --- /dev/null +++ b/src/CPerlCounter.h @@ -0,0 +1,49 @@ +//! Code counter class definition for the Perl language. +/*! +* \file CPerlCounter.h +* +* This file contains the code counter class definition for the Perl language. +*/ + +#ifndef CPerlCounter_h +#define CPerlCounter_h + +#include "CCodeCounter.h" + +//! Perl code counter class. +/*! +* \class CPerlCounter +* +* Defines the Perl code counter class. +*/ +class CPerlCounter : public CCodeCounter +{ +public: + CPerlCounter(); + +protected: + virtual int PreCountProcess(filemap* fmap); + virtual int ReplaceQuote(string &strline, size_t &idx_start, bool &contd, char &CurrentQuoteEnd); + virtual int CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak = NULL); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak = NULL); + void LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + unsigned int &paren_cnt, bool &forflag, bool &found_forifwhile, bool &found_while, char &prev_char, + bool &data_continue, unsigned int &temp_lines, unsigned int &phys_exec_lines, unsigned int &phys_data_lines, + unsigned int &openBrackets, StringVector &loopLevel); + int ParseFunctionName(const string &line, string &lastline, + filemap &functionStack, string &functionName, unsigned int &functionCount); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CPerlCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CPerlCounter(const CPerlCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CPerlCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CPerlCounter operator=(const CPerlCounter); // Declare without implementation +}; + +#endif diff --git a/src/CPhpCounter.cpp b/src/CPhpCounter.cpp new file mode 100644 index 0000000..e684d75 --- /dev/null +++ b/src/CPhpCounter.cpp @@ -0,0 +1,877 @@ +//! Code counter class methods for the PHP language. +/*! +* \file CPhpCounter.cpp +* +* This file contains the code counter class methods for the PHP language. +*/ + +#include "CPhpCounter.h" + +/*! +* Constructs a CPhpCounter object. +*/ +CPhpCounter::CPhpCounter() +{ + classtype = PHP; + language_name = "PHP"; + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("PHP", file_extension); + //file_extension.push_back(".*php"); + + QuoteStart = "\"'"; + QuoteEnd = "\"'"; + QuoteEscapeFront = '\\'; + ContinueLine = "\\"; + BlockCommentStart.push_back("/*"); + BlockCommentEnd.push_back("*/"); + + LineCommentStart.push_back("//"); + LineCommentStart.push_back("#"); + + exclude_keywords.push_back("endif"); + exclude_keywords.push_back("endfor"); + exclude_keywords.push_back("endforeach"); + exclude_keywords.push_back("endswitch"); + exclude_keywords.push_back("endwhile"); + + exclude_loop.push_back("endfor"); + exclude_loop.push_back("endforeach"); + exclude_loop.push_back("endwhile"); + + directive.push_back("define"); + directive.push_back("include"); + directive.push_back("include_once"); + directive.push_back("require"); + directive.push_back("require_once"); + + data_name_list.push_back("array"); + data_name_list.push_back("bool"); + data_name_list.push_back("class"); + data_name_list.push_back("const"); + data_name_list.push_back("declare"); + data_name_list.push_back("extends"); + data_name_list.push_back("float"); + data_name_list.push_back("function"); + data_name_list.push_back("global"); + data_name_list.push_back("int"); + data_name_list.push_back("interface"); + data_name_list.push_back("NULL"); + data_name_list.push_back("object"); + data_name_list.push_back("private"); + data_name_list.push_back("protected"); + data_name_list.push_back("public"); + data_name_list.push_back("string"); + data_name_list.push_back("var"); + + exec_name_list.push_back("break"); + exec_name_list.push_back("case"); + exec_name_list.push_back("catch"); + exec_name_list.push_back("continue"); + exec_name_list.push_back("default"); + exec_name_list.push_back("die"); + exec_name_list.push_back("do"); + exec_name_list.push_back("echo"); + exec_name_list.push_back("else"); + exec_name_list.push_back("exception"); + exec_name_list.push_back("exit"); + exec_name_list.push_back("for"); + exec_name_list.push_back("foreach"); + exec_name_list.push_back("if"); + exec_name_list.push_back("isset"); + exec_name_list.push_back("new"); + exec_name_list.push_back("print"); + exec_name_list.push_back("return"); + exec_name_list.push_back("switch"); + exec_name_list.push_back("this"); + exec_name_list.push_back("throw"); + exec_name_list.push_back("try"); + exec_name_list.push_back("while"); + + math_func_list.push_back("abs"); + math_func_list.push_back("base_convert"); + math_func_list.push_back("bindec"); + math_func_list.push_back("ceil"); + math_func_list.push_back("decbin"); + math_func_list.push_back("dechex"); + math_func_list.push_back("decoct"); + math_func_list.push_back("deg2rad"); + math_func_list.push_back("exp"); + math_func_list.push_back("expm1"); + math_func_list.push_back("floor"); + math_func_list.push_back("fmod"); + math_func_list.push_back("getrandmax"); + math_func_list.push_back("hexdec"); + math_func_list.push_back("hypot"); + math_func_list.push_back("is_finite"); + math_func_list.push_back("is_infinite"); + math_func_list.push_back("is_nan"); + math_func_list.push_back("lcg_value"); + math_func_list.push_back("max"); + math_func_list.push_back("min"); + math_func_list.push_back("mt_getrandmax"); + math_func_list.push_back("mt_rand"); + math_func_list.push_back("mt_srand"); + math_func_list.push_back("octdec"); + math_func_list.push_back("pi"); + math_func_list.push_back("pow"); + math_func_list.push_back("rad2deg"); + math_func_list.push_back("rand"); + math_func_list.push_back("round"); + math_func_list.push_back("sqrt"); + math_func_list.push_back("srand"); + + trig_func_list.push_back("acos"); + trig_func_list.push_back("acosh"); + trig_func_list.push_back("asin"); + trig_func_list.push_back("asinh"); + trig_func_list.push_back("atan"); + trig_func_list.push_back("atan2"); + trig_func_list.push_back("atanh"); + trig_func_list.push_back("cos"); + trig_func_list.push_back("cosh"); + trig_func_list.push_back("sin"); + trig_func_list.push_back("sinh"); + trig_func_list.push_back("tan"); + trig_func_list.push_back("tanh"); + + log_func_list.push_back("log"); + log_func_list.push_back("log10"); + log_func_list.push_back("log1p"); + + cmplx_calc_list.push_back("%"); + cmplx_calc_list.push_back("++"); + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("--"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("/"); + cmplx_calc_list.push_back(">>"); + cmplx_calc_list.push_back("<<"); + + cmplx_cond_list.push_back("else"); + cmplx_cond_list.push_back("else if"); + cmplx_cond_list.push_back("elseif"); + cmplx_cond_list.push_back("for"); + cmplx_cond_list.push_back("if"); + cmplx_cond_list.push_back("case"); + cmplx_cond_list.push_back("switch"); + cmplx_cond_list.push_back("while"); + + cmplx_logic_list.push_back("&"); + cmplx_logic_list.push_back("|"); + cmplx_logic_list.push_back("^"); + cmplx_logic_list.push_back("~"); + cmplx_logic_list.push_back("=="); + cmplx_logic_list.push_back("==="); + cmplx_logic_list.push_back("!="); + cmplx_logic_list.push_back("!=="); + cmplx_logic_list.push_back("<>"); + cmplx_logic_list.push_back("&&"); + cmplx_logic_list.push_back("||"); + cmplx_logic_list.push_back("!"); + cmplx_logic_list.push_back("and"); + cmplx_logic_list.push_back("not"); + cmplx_logic_list.push_back("or"); + cmplx_logic_list.push_back("xor"); + cmplx_logic_list.push_back(">"); + cmplx_logic_list.push_back("<"); + cmplx_logic_list.push_back(">="); + cmplx_logic_list.push_back("=<"); + + cmplx_assign_list.push_back("="); + cmplx_assign_list.push_back("=>"); + + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("elsif"); + cmplx_cyclomatic_list.push_back("while"); + cmplx_cyclomatic_list.push_back("for"); + cmplx_cyclomatic_list.push_back("foreach"); + cmplx_cyclomatic_list.push_back("catch"); + cmplx_cyclomatic_list.push_back("case"); + + cmplx_cyclomatic_logic_list.push_back("&&"); + cmplx_cyclomatic_logic_list.push_back("||"); + //cmplx_cyclomatic_logic_list.push_back("^"); + cmplx_cyclomatic_logic_list.push_back("and"); + cmplx_cyclomatic_logic_list.push_back("or"); + //cmplx_cyclomatic_logic_list.push_back("xor"); + + cmplx_cyclomatic_case_list.push_back("case"); + cmplx_cyclomatic_switch_list.push_back("switch"); +} + +/*! +* Counts directive lines of code. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CPhpCounter::CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak) +{ + bool contd = false, trunc_flag = false; + size_t idx, strSize; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string strDirLine = ""; + filemap::iterator itfmBak = fmapBak->begin(); + + for (filemap::iterator iter = fmap->begin(); iter!=fmap->end(); iter++, itfmBak++) + { + if (CUtil::CheckBlank(iter->line)) + continue; + size_t lineNumber = iter->lineNumber; + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(" " + iter->line, directive, cnt, 1, exclude, "", "", &result->directive_count); + } + + if (!contd) + { + // if not a continuation of a previous directive + for (vector::iterator viter = directive.begin(); viter != directive.end(); viter++) + { + if (((idx = CUtil::FindKeyword(iter->line, *viter)) != string::npos) && idx == 0) + { + contd = true; + break; + } + } + if (contd) + { + strSize = CUtil::TruncateLine(itfmBak->line.length(), 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine = itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + } + else + { + // continuation of a previous directive + strSize = CUtil::TruncateLine(itfmBak->line.length(), strDirLine.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine += "\n" + itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + + if (contd) + { + // drop continuation symbol + if (strDirLine[strDirLine.length()-1] == '\\') + strDirLine = strDirLine.substr(0, strDirLine.length()-1); + + // if a directive or continuation of a directive (no continuation symbol found) + if (iter->line[iter->line.length()-1] != ',' && iter->line[iter->line.length()-1] != '\\') + { + contd = false; + if (result->addSLOC(strDirLine, lineNumber, trunc_flag)) + result->directive_lines[LOG]++; + } + iter->line = ""; + } + } + return 1; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CPhpCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + unsigned int paren_count = 0; + bool for_flag = false; + bool found_for = false; + bool found_forifwhile = false; + bool found_while = false; + char prev_char = 0; + bool data_continue = false; + bool inArrayDec = false; + string strLSLOC = ""; + string strLSLOCBak = ""; + + filemap::iterator fit, fitbak; + string line, lineBak; + StringVector loopLevel; + + unsigned int phys_exec_lines = 0; + unsigned int phys_data_lines = 0; + unsigned int temp_lines = 0; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + // insert blank at the beginning (for searching keywords) + line = ' ' + fit->line; + lineBak = ' ' + fitbak->line; + + // do not process blank lines + // blank line means blank_line/comment_line/directive + if (!CUtil::CheckBlank(line)) + { + LSLOC(result, line, fit->lineNumber, lineBak, strLSLOC, strLSLOCBak, paren_count, for_flag, found_forifwhile, found_while, + prev_char, data_continue, temp_lines, phys_exec_lines, phys_data_lines, inArrayDec, found_for, loopLevel); + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count); + } + + result->exec_lines[PHY] += phys_exec_lines; + phys_exec_lines = 0; + + result->data_lines[PHY] += phys_data_lines; + phys_data_lines = 0; + + } + } + return 1; +} + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results +* \param line processed physical line of code +* \param lineBak original physical line of code +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param paren_cnt count of parenthesis +* \param forflag found for flag +* \param found_forifwhile found for, if, or while flag +* \param found_while found while flag +* \param prev_char previous character +* \param data_continue continuation of a data declaration line +* \param temp_lines tracks physical line count +* \param phys_exec_lines number of physical executable lines +* \param phys_data_lines number of physical data lines +* \param inArrayDec marks an array declaration +* \param found_for found for loop +* \param loopLevel nested loop level +*/ +void CPhpCounter::LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, unsigned int &paren_cnt, + bool &forflag, bool &found_forifwhile, bool &found_while, char &prev_char, bool &data_continue, + unsigned int &temp_lines, unsigned int &phys_exec_lines, unsigned int &phys_data_lines, + bool &inArrayDec, bool &found_for, StringVector &loopLevel) +{ + size_t start = 0; + size_t i = 0, strSize; + bool found_do, found_try, found_else, found_declare, trunc_flag = false; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$:"; + unsigned int cnt = 0; + + string tmp = CUtil::TrimString(strLSLOC); + + // do, try + found_do = (CUtil::FindKeyword(tmp, "do") != string::npos); + found_try = (CUtil::FindKeyword(tmp, "try") != string::npos); + // else, declare are treated differently, else and declare are included in SLOC, do and try are not + found_else = (CUtil::FindKeyword(tmp, "else") != string::npos); + found_declare = (CUtil::FindKeyword(tmp, "declare") != string::npos); + + while (i < line.length()) // there may be more than 1 logical SLOC in this line + { + switch (line[i]) + { + case ';': case '{': case ':': // LSLOC terminators + // ';' for normal executable or declaration statement + // '{' or ':' for starting a function or 'do' stmt or a block (which is counted) + // get the previous logical mark until i-1 index is the new LSLOC + // except 'do' precedes '{' + // except '}' precedes ';' ?? + // do nothing inside 'for' statement + if (found_for == true && paren_cnt > 0 && line[i] == ';') + break; + + // record open bracket for nested loop processing + // check for excluded loop keywords for alternate control syntax + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(line, exclude_loop, cnt, 1, exclude, "", ""); + if (cnt > 0) + { + if ((unsigned int)loopLevel.size() > 0) + loopLevel.pop_back(); + } + else if (line[i] == '{') + loopLevel.push_back(""); + else if (line[i] == ';') + { + if ((unsigned int)loopLevel.size() > 0 && loopLevel.back() != "") + { + tmp = loopLevel.back(); + if (tmp[tmp.length()-1] != ':') + loopLevel.pop_back(); + } + } + else if (line[i] == ':') + { + if ((unsigned int)loopLevel.size() > 0 && loopLevel.back() != "") + { + tmp = CUtil::TrimString(line.substr(0, i)); + if (CUtil::FindKeyword(tmp, loopLevel.back()) != string::npos) + { + tmp = loopLevel.back() + ":"; + loopLevel.pop_back(); + loopLevel.push_back(tmp); + } + } + } + } + + // check for excluded keywords for alternate control syntax (don't count as LSLOC) + cnt = 0; + CUtil::CountTally(line, exclude_keywords, cnt, 1, exclude, "", ""); + if (cnt > 0) + { + start = i + 1; + break; + } + + // case 'if(...):', 'while(...):, for(...):, foreach(...):, switch(...):' + // this case is handled in case ')' + // skip other ':' + if (line[i] == ':') + { + if (found_forifwhile) + { + found_forifwhile = false; + start = i + 1; + } + break; + } + + // case 'while(...);', 'while(...) {', '} while(...);' + // this case is handled in case ')' + if (found_while && found_forifwhile) + { + found_while = false; + found_forifwhile = false; + start = i + 1; + break; + } + + if (line[i] == '{' || line[i] == ':') + { + if (prev_char == '=') + inArrayDec = true; + + // continue until seeing ';' + if (inArrayDec) + break; + + // case for(...);, if (...) {, and if (...):, elseif (...) {, and elseif (...): + // these specials are handled + if (found_forifwhile) + { + found_forifwhile = false; + start = i + 1; + break; + } + + // check if 'do' precedes '{' or ':' + if (!found_do && !found_try && !found_else && !found_declare) + { + // find 'do' in string before tmp string + tmp = CUtil::TrimString(line.substr(start, i - start)); + found_do = (tmp == "do"); // found 'do' statement + found_try = (tmp == "try"); // found 'try' statement + found_else = (tmp == "else"); // found 'else' statement + found_declare = (CUtil::FindKeyword(tmp, "declare") != string::npos); // found 'declare' statement + } + if (found_do || found_try || found_else) + { + if (found_do && print_cmplx) + { + if (loopLevel.size() > 0) + loopLevel.pop_back(); + loopLevel.push_back("do"); + } + found_do = false; + found_try = false; + if (!found_else) + { + // everything before 'do', 'try' are cleared + strLSLOC = ""; + strLSLOCBak = ""; + start = i + 1; + } + break; // do not store '{' or ':' following 'do' + } + } + + // wrong, e.g., a[]={1,2,3}; + if (line[i] == ';' && prev_char == '}') + { + // check if in array declaration or not + // if no, skip, otherwise, complete the SLOC containing array declaration + if (!inArrayDec) + { + start = i + 1; + break; + } + } + + inArrayDec = false; + + // check for empty statement (=1 LSLOC) + if (CUtil::TrimString(line.substr(start, i + 1 - start)) == ";" && strLSLOC.length() < 1) + { + strLSLOC = ";"; + strLSLOCBak = ";"; + } + else + { + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + { + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", &result->data_name_count); + + temp_lines++; + if (data_continue == true && line[i] == ';') + { + result->data_lines[LOG]++; + phys_data_lines = temp_lines; + } + else + { + if (cnt > 0 && line[i] == ';') + { + result->data_lines[LOG]++; + phys_data_lines = temp_lines; + } + else + { + if (found_declare) + { + result->directive_lines[PHY] += temp_lines; + result->directive_lines[LOG]++; + } + else + { + result->exec_lines[LOG]++; + phys_exec_lines = temp_lines; + } + } + } + } + else if (data_continue == true && line[i] == ';') + phys_data_lines = temp_lines; + else if (found_declare) + result->directive_lines[PHY] += temp_lines; + else + phys_exec_lines = temp_lines; + data_continue = false; + temp_lines = 0; + strLSLOC = strLSLOCBak = ""; + start = i + 1; + + // reset some flagging parameters + forflag = false; + paren_cnt = 0; + found_while = false; + found_forifwhile = false; + found_for = false; + found_declare = false; + + break; + case '(': + if (forflag) + paren_cnt++; + else + { + // handle 'for', 'foreach', 'while', 'if' the same way + tmp = CUtil::TrimString(line.substr(start, i)); + if (CUtil::FindKeyword(tmp, "for") != string::npos + || CUtil::FindKeyword(tmp, "foreach") != string::npos + || CUtil::FindKeyword(tmp, "while")!= string::npos + || CUtil::FindKeyword(tmp, "if") != string::npos + || CUtil::FindKeyword(tmp, "elseif") != string::npos + || CUtil::FindKeyword(tmp, "switch") != string::npos) + { + forflag = true; + paren_cnt++; + + if (CUtil::FindKeyword(tmp, "for") != string::npos) + { + if (print_cmplx) + loopLevel.push_back("for"); + found_for = true; + } + else if (CUtil::FindKeyword(tmp, "while")!= string::npos) + { + if (print_cmplx) + loopLevel.push_back("while"); + found_while = true; + } + else if (CUtil::FindKeyword(tmp, "foreach") != string::npos) + loopLevel.push_back("foreach"); + + else if (print_cmplx && CUtil::FindKeyword(tmp, "foreach") != string::npos) + loopLevel.push_back("foreach"); + + // record nested loop level + if (print_cmplx) + { + if (CUtil::FindKeyword(tmp, "if") == string::npos && + CUtil::FindKeyword(tmp, "elseif") == string::npos && + CUtil::FindKeyword(tmp, "switch") == string::npos) + { + unsigned int loopCnt = 0; + for (StringVector::iterator lit = loopLevel.begin(); lit < loopLevel.end(); lit++) + { + if ((*lit) != "") + loopCnt++; + } + if ((unsigned int)result->cmplx_nestloop_count.size() < loopCnt) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopCnt-1]++; + } + } + } + } + break; + case ')': + if (forflag) + { + if (paren_cnt > 0) + paren_cnt--; + if (paren_cnt == 0) + { + // handle 'for', 'foreach', 'while', 'if', 'elseif', 'switch' + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + strLSLOCBak = strLSLOC = ""; + phys_exec_lines = temp_lines; + temp_lines = 0; + start = i + 1; + found_forifwhile = true; + forflag = false; + found_for = false; + } + } + break; + case '}': + // skip '}' when found ';' and then '}' because '{' is counted already + // also, {} is also skipped, counted + if (prev_char == ';' || prev_char == '{' || prev_char == '}') + { + if (!inArrayDec) + start = i + 1; + } + + // record close bracket for nested loop processing + if (print_cmplx) + { + if ((unsigned int)loopLevel.size() > 0) + loopLevel.pop_back(); + if ((unsigned int)loopLevel.size() > 0 && loopLevel.back() != "") + { + tmp = loopLevel.back(); + if (tmp[tmp.length()-1] != ':') + loopLevel.pop_back(); + } + } + break; + } + + if (line[i] != ' ' && line[i] != '\t') + { + // if ;}}} --> don't count }}} at all + // also, if {}}} --> don't count }}} at all + // if ( !(line[i] == '}' && (prev_char == ';' || prev_char == '{'))) // see case '}' above + prev_char = line[i]; + + // change to not found if a char appears before + if (line[i] != ')' && found_forifwhile) + found_forifwhile = false; + } + + i++; + } + + tmp = CUtil::TrimString(line.substr(start, i - start)); + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + tmp = CUtil::TrimString(lineBak.substr(start, i - start)); + strLSLOCBak += tmp.substr(0, strSize); + + // drop continuation symbol + if (strLSLOC[strLSLOC.length()-1] == '\\') + { + strLSLOC = strLSLOC.substr(0, strLSLOC.length()-1); + strLSLOCBak = strLSLOCBak.substr(0, strLSLOCBak.length()-1); + } + } + + // make sure that we are not beginning to process a new data line + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", NULL); + + if (cnt > 0) + data_continue = true; + if (data_continue) + temp_lines++; + if (temp_lines == 0 && phys_data_lines == 0 && phys_exec_lines == 0) + phys_exec_lines = 1; +} + +/*! + * Parses lines for function/method names. + * + * \param line line to be processed + * \param lastline last line processed + * \param functionStack stack of functions + * \param functionName function name found + * + * \return 1 if function name is found + */ +int CPhpCounter::ParseFunctionName(const string &line, string &lastline, + filemap &functionStack, string &functionName, unsigned int &functionCount) +{ + + string tline, str; + size_t i, idx, tidx, cnt; + unsigned int fcnt; + + tline = CUtil::TrimString(line); + + // look for start of a block + // -- a block could be a function, or an "if" "for" "while" statement + idx = tline.find('{'); + if (idx != string::npos) + { + // check whether it is at first index, if yes then block name is at above line + if (idx == 0) + { + // record block name + lineElement element(++functionCount, lastline); + functionStack.push_back(element); + lastline.erase(); + } + else + { + // concatenate with last line if necessary and record block name + str = tline.substr(0, idx); + if (str.find('(') != string::npos && str[0] != '(') + lastline = str; + else + lastline += " " + str; + lineElement element(++functionCount, CUtil::TrimString(lastline)); + functionStack.push_back(element); + lastline.erase(); + } + } + + // didn't find start of a block + // Regular line -- it's not terminated and its last line is also not terminated + else if (tline.length() > 0 && tline[tline.length() - 1] != ';' && + lastline.length() > 0 && lastline[lastline.length() - 1] != ';') + { + // append until all parenthesis are closed + tidx = lastline.find('('); + if (tidx != string::npos) + { + cnt = 1; + while (tidx != string::npos) + { + tidx = lastline.find('(', tidx + 1); + if (tidx != string::npos) + cnt++; + } + tidx = lastline.find(')'); + while (tidx != string::npos) + { + cnt++; + tidx = lastline.find(')', tidx + 1); + } + if (cnt % 2 != 0) + lastline += " " + tline; + else + lastline = tline; + } + else + lastline = tline; + } + + // Regular line + else + lastline = tline; + + // Find end of a block + idx = line.find('}'); + if (idx != string::npos && !functionStack.empty()) + { + // pop start of the block from functionStack + str = functionStack.back().line; + fcnt = functionStack.back().lineNumber; + functionStack.pop_back(); + idx = CUtil::FindKeyword(str, "function"); + + // The block is a function + if (idx != string::npos && idx + 9 < str.length()) + { + functionName = CUtil::ClearRedundantSpaces(str.substr(idx + 9)); + functionCount = fcnt; + lastline.erase(); + return 1; + } + lastline.erase(); + } + + // check stack for any "function" return 2 if not found + idx = string::npos; + if (!functionStack.empty()) + { + for (i = 0; i < functionStack.size(); i++) + { + idx = CUtil::FindKeyword(functionStack[i].line, "function"); + if (idx != string::npos) + break; + } + } + if (idx == string::npos) + { + // dealing with some code out of any subroutines, it a "main" code + return 2; + } + return 0; +} diff --git a/src/CPhpCounter.h b/src/CPhpCounter.h new file mode 100644 index 0000000..7cb55a9 --- /dev/null +++ b/src/CPhpCounter.h @@ -0,0 +1,50 @@ +//! Code counter class definition for the PHP language. +/*! +* \file CPhpCounter.h +* +* This file contains the code counter class definition for the PHP language. +*/ + +#ifndef CPhpCounter_h +#define CPhpCounter_h + +#include "CCodeCounter.h" + +//! PHP code counter class. +/*! +* \class CPhpCounter +* +* Defines the PHP code counter class. +* NOTE: PHP variables are case sensitive, but PHP functions are case insensitive. +*/ +class CPhpCounter : public CCodeCounter +{ +public: + CPhpCounter(); + +protected: + StringVector exclude_loop; //!< List of keywords to exclude for loops + + virtual int CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak = NULL); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak = NULL); + void LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + unsigned int &paren_cnt, bool &forflag, bool &found_forifwhile, bool &found_while, char &prev_char, + bool &data_continue, unsigned int &temp_lines, unsigned int &phys_exec_lines, unsigned int &phys_data_lines, + bool &inArrayDec, bool &found_for, StringVector &loopLevel); + int ParseFunctionName(const string &line, string &lastline, + filemap &functionStack, string &functionName, unsigned int &functionCount); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CPhpCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CPhpCounter(const CPhpCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CPhpCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CPhpCounter operator=(const CPhpCounter); // Declare without implementation +}; + +#endif diff --git a/src/CPythonCounter.cpp b/src/CPythonCounter.cpp new file mode 100644 index 0000000..920a74e --- /dev/null +++ b/src/CPythonCounter.cpp @@ -0,0 +1,1153 @@ +//! Code counter class methods for the Python language. +/*! +* \file CPythonCounter.cpp +* +* This file contains the code counter class methods for the Python language. +*/ + +#include "CPythonCounter.h" +#include "UCCExceptDump.h" // Modification: 2015.12 + +/*! +* Constructs a CPythonCounter object. +*/ +CPythonCounter::CPythonCounter() +{ + classtype = PYTHON; + language_name = "Python"; + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("Python", file_extension); + //file_extension.push_back(".py"); + + BlockCommentStart.push_back("\"\"\""); + BlockCommentEnd.push_back("\"\"\""); + BlockCommentStart.push_back("'''"); + BlockCommentEnd.push_back("'''"); + LineCommentStart.push_back("#"); + QuoteStart = "\"\'"; + QuoteEnd = "\"\'"; + QuoteEscapeRear = '\\'; + + loop_keywords.push_back("for"); + loop_keywords.push_back("while"); + + directive.push_back("do"); + directive.push_back("from"); + directive.push_back("import"); + directive.push_back("no"); + directive.push_back("package"); + directive.push_back("use"); + directive.push_back("require"); + + exec_name_list.push_back("and"); + exec_name_list.push_back("as"); + exec_name_list.push_back("assert"); + exec_name_list.push_back("break"); + exec_name_list.push_back("continue"); + exec_name_list.push_back("def"); + exec_name_list.push_back("del"); + exec_name_list.push_back("elif"); + exec_name_list.push_back("else"); + exec_name_list.push_back("except"); + exec_name_list.push_back("exec"); + exec_name_list.push_back("exit"); + exec_name_list.push_back("finally"); + exec_name_list.push_back("for"); + exec_name_list.push_back("global"); + exec_name_list.push_back("if"); + exec_name_list.push_back("in"); + exec_name_list.push_back("is"); + exec_name_list.push_back("lambda"); + exec_name_list.push_back("not"); + exec_name_list.push_back("or"); + exec_name_list.push_back("pass"); + exec_name_list.push_back("print"); + exec_name_list.push_back("raise"); + exec_name_list.push_back("return"); + exec_name_list.push_back("try"); + exec_name_list.push_back("while"); + exec_name_list.push_back("with"); + exec_name_list.push_back("yield"); + + math_func_list.push_back("math.ceil"); + math_func_list.push_back("math.copysign"); + math_func_list.push_back("math.degrees"); + math_func_list.push_back("math.e"); + math_func_list.push_back("math.exp"); + math_func_list.push_back("math.fabs"); + math_func_list.push_back("math.factorial"); + math_func_list.push_back("math.floor"); + math_func_list.push_back("math.fmod"); + math_func_list.push_back("math.frexp"); + math_func_list.push_back("math.fsum"); + math_func_list.push_back("math.hypot"); + math_func_list.push_back("math.ldexp"); + math_func_list.push_back("math.modf"); + math_func_list.push_back("math.pi"); + math_func_list.push_back("math.pow"); + math_func_list.push_back("math.radians"); + math_func_list.push_back("math.sqrt"); + math_func_list.push_back("math.trunc"); + math_func_list.push_back("cmath.phase"); + math_func_list.push_back("cmath.polar"); + math_func_list.push_back("cmath.rect"); + + trig_func_list.push_back("math.acos"); + trig_func_list.push_back("math.acosh"); + trig_func_list.push_back("math.asinh"); + trig_func_list.push_back("math.atanh"); + trig_func_list.push_back("math.asin"); + trig_func_list.push_back("math.atan"); + trig_func_list.push_back("math.atan2"); + trig_func_list.push_back("math.cos"); + trig_func_list.push_back("math.cosh"); + trig_func_list.push_back("math.sin"); + trig_func_list.push_back("math.sinh"); + trig_func_list.push_back("math.tan"); + trig_func_list.push_back("math.tanh"); + + log_func_list.push_back("math.log"); + log_func_list.push_back("math.log10"); + log_func_list.push_back("math.log1p"); + + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("/"); + cmplx_calc_list.push_back("%"); + cmplx_calc_list.push_back("**"); + + cmplx_cond_list.push_back("elif"); + cmplx_cond_list.push_back("else"); + cmplx_cond_list.push_back("except"); + cmplx_cond_list.push_back("for"); + cmplx_cond_list.push_back("if"); + cmplx_cond_list.push_back("try"); + cmplx_cond_list.push_back("while"); + + cmplx_logic_list.push_back("=="); + cmplx_logic_list.push_back("!="); + cmplx_logic_list.push_back(">"); + cmplx_logic_list.push_back("<"); + cmplx_logic_list.push_back(">="); + cmplx_logic_list.push_back("=<"); + + cmplx_assign_list.push_back("="); + + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("elif"); + cmplx_cyclomatic_list.push_back("for"); + cmplx_cyclomatic_list.push_back("while"); + cmplx_cyclomatic_list.push_back("except"); + //cmplx_cyclomatic_list.push_back("case"); + + cmplx_cyclomatic_logic_list.push_back("and"); + cmplx_cyclomatic_logic_list.push_back("or"); + + //cmplx_cyclomatic_case_list.push_back("case"); +} + +/*! +* Replaces up to ONE quoted string inside a string starting at idx_start. +* Uses a string instead of a character to allow processing multi-line +* literals """ and '''. +* +* \param strline string to be processed +* \param idx_start index of line character to start search +* \param contd specifies the quote string is continued from the previous line +* \param CurrentQuoteEnd end quote string of the current status +* +* \return method status +*/ +int CPythonCounter::ReplaceQuote(string &strline, size_t &idx_start, bool &contd, string &CurrentQuoteEnd) +{ + size_t idx_end, idx_quote; + + if (contd) + { + // python: use string instead of character to check for """ and ''' + idx_start = 0; + if (strline.length() >= CurrentQuoteEnd.length() && + strline.substr(0, CurrentQuoteEnd.length()) == CurrentQuoteEnd) + { + idx_start = CurrentQuoteEnd.length(); + contd = false; + return 1; + } + strline[0] = '$'; + } + else + { + // handle two quote chars in some languages, both " and ' may be accepted + idx_start = FindQuote(strline, QuoteStart, idx_start, QuoteEscapeFront); + if (idx_start != string::npos) + { + idx_quote = QuoteStart.find_first_of(strline[idx_start]); + CurrentQuoteEnd = QuoteEnd[idx_quote]; + // python: check for """ or ''' + if (strline.length() >= idx_start + 3) + { + if (CurrentQuoteEnd == "\"") + { + if (strline.substr(idx_start, 3) == "\"\"\"") + CurrentQuoteEnd = "\"\"\""; + } + else if (CurrentQuoteEnd == "'") + { + if (strline.substr(idx_start, 3) == "'''") + CurrentQuoteEnd = "'''"; + } + } + } + else + { + idx_start = strline.length(); + return 0; + } + } + + // python: handle """ and ''' + if (CurrentQuoteEnd.length() == 3) + { + if (idx_start + 3 >= strline.length()) + idx_end = string::npos; + else + { + idx_end = strline.find(CurrentQuoteEnd, idx_start + 3); + if (idx_end != string::npos) + idx_end += 2; // shift to last quote character + } + } + else + idx_end = CUtil::FindCharAvoidEscape(strline, CurrentQuoteEnd[0], idx_start + 1, QuoteEscapeFront); + if (idx_end == string::npos) + { + idx_end = strline.length() - 1; + strline.replace(idx_start + 1, idx_end - idx_start, idx_end - idx_start, '$'); + contd = true; + idx_start = idx_end + 1; + } + else + { + if (CurrentQuoteEnd.length() != 3 && (QuoteEscapeRear) && (strline.length() > idx_end + 1) && (strline[idx_end+1] == QuoteEscapeRear)) + { + strline[idx_end] = '$'; + strline[idx_end+1] = '$'; + } + else + { + contd = false; + strline.replace(idx_start + 1, idx_end - idx_start - 1, idx_end - idx_start - 1, '$'); + idx_start = idx_end + 1; + } + } + return 1; +} + +/*! +* Counts the number of comment lines, removes comments, and +* replaces quoted strings by special chars, e.g., $ +* All arguments are modified by the method. +* Special processing for """ and ''' which can be multi-line literal +* or a multi-line comment if it stands alone. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CPythonCounter::CountCommentsSLOC(filemap* fmap, results* result, filemap *fmapBak) +{ + if (BlockCommentStart.empty() && LineCommentStart.empty()) + return 0; + if (classtype == UNKNOWN || classtype == DATAFILE) + return 0; + + bool contd = false; + bool contd_nextline; + int comment_type = 0; + /* + comment_type: + 0 : not a comment + 1 : line comment, whole line + 2 : line comment, embedded + 3 : block comment, undecided + 4 : block comment, embedded + */ + + size_t idx_start, idx_end, comment_start; + size_t quote_idx_start; + string curBlckCmtStart, curBlckCmtEnd, tmp; + string CurrentQuoteEnd = ""; + bool quote_contd = false; + filemap::iterator itfmBak = fmapBak->begin(); + + quote_idx_start = 0; + + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++, itfmBak++) + { + contd_nextline = false; + + quote_idx_start = 0; + idx_start = 0; + + if (CUtil::CheckBlank(iter->line)) + continue; + if (quote_contd) + { + // Replace quote until next character + ReplaceQuote(iter->line, quote_idx_start, quote_contd, CurrentQuoteEnd); + if (quote_contd) + continue; + } + + if (contd) + comment_type = 3; + + while (!contd_nextline && idx_start < iter->line.length()) + { + // need to handle multiple quote chars in some languages, both " and ' may be accepted + quote_idx_start = FindQuote(iter->line, QuoteStart, quote_idx_start, QuoteEscapeFront); + comment_start = idx_start; + if (!contd) + { + FindCommentStart(iter->line, comment_start, comment_type, curBlckCmtStart, curBlckCmtEnd); + if (comment_start != string::npos && comment_type > 2) + { + // python: check whether this is a multi-line literal or a block comment + tmp = CUtil::TrimString(iter->line, -1); + if (iter->line.length() - tmp.length() != comment_start) + { + quote_idx_start = comment_start; + comment_start = string::npos; + } + } + } + + if (comment_start == string::npos && quote_idx_start == string::npos) + break; + + if (comment_start != string::npos) + idx_start = comment_start; + + // if found quote before comment, e.g., "this is quote");//comment + if (quote_idx_start != string::npos && (comment_start == string::npos || quote_idx_start < comment_start)) + { + ReplaceQuote(iter->line, quote_idx_start, quote_contd, CurrentQuoteEnd); + if (quote_idx_start > idx_start && quote_idx_start != iter->line.length()) + { + // comment delimiter inside quote + idx_start = quote_idx_start; + continue; + } + } + else if (comment_start != string::npos) + { + // comment delimiter starts first + switch (comment_type) + { + case 1: // line comment, definitely whole line + iter->line = ""; + itfmBak->line = ""; + result->comment_lines++; + contd_nextline = true; + break; + case 2: // line comment, possibly embedded + iter->line = iter->line.substr(0, idx_start); + itfmBak->line = itfmBak->line.substr(0, idx_start); + // trim trailing space + iter->line = CUtil::TrimString(iter->line, 1); + itfmBak->line = CUtil::TrimString(itfmBak->line, 1); + if (iter->line.empty()) + result->comment_lines++; // whole line + else + result->e_comm_lines++; // embedded + contd_nextline = true; + break; + case 3: // block comment + case 4: + if (contd) + idx_end = iter->line.find(curBlckCmtEnd); + else + idx_end = iter->line.find(curBlckCmtEnd, idx_start + curBlckCmtStart.length()); + + if (idx_end == string::npos) + { + if (comment_type == 3) + { + iter->line = ""; + itfmBak->line = ""; + result->comment_lines++; + } + else if (comment_type == 4) + { + iter->line = iter->line.substr(0, idx_start); + itfmBak->line = itfmBak->line.substr(0, idx_start); + // trim trailing space + iter->line = CUtil::TrimString(iter->line, 1); + itfmBak->line = CUtil::TrimString(itfmBak->line, 1); + if (iter->line.empty()) + result->comment_lines++; // whole line + else + result->e_comm_lines++; // embedded + } + contd = true; + contd_nextline = true; + break; + } + else + { + contd = false; + iter->line.erase(idx_start, idx_end - idx_start + curBlckCmtEnd.length()); + itfmBak->line.erase(idx_start, idx_end - idx_start + curBlckCmtEnd.length()); + if (iter->line.empty()) + result->comment_lines++; + else + { + // trim trailing space + iter->line = CUtil::TrimString(iter->line, 1); + itfmBak->line = CUtil::TrimString(itfmBak->line, 1); + if (iter->line.empty()) + result->comment_lines++; // whole line + else + result->e_comm_lines++; // embedded + } + + // quote chars found may be erased as it is inside comment + quote_idx_start = idx_start; + } + break; + default: + cout << "Error in CountCommentsSLOC()" << endl; + break; + } + } + } + } + return 1; +} + +/*! +* Counts directive lines of code. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CPythonCounter::CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak) +{ + bool contd = false, trunc_flag = false; + size_t idx, strSize; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string strDirLine = ""; + + filemap::iterator itfmBak = fmapBak->begin(); + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++, itfmBak++) + { + if (CUtil::CheckBlank(iter->line)) + continue; + size_t lineNumber = iter->lineNumber; + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(" " + iter->line, directive, cnt, 1, exclude, "", "", &result->directive_count); + } + + if (!contd) + { + // if not a continuation of a previous directive + for (vector::iterator viter = directive.begin(); viter != directive.end(); viter++) + { + if ((idx = iter->line.find((*viter), 0)) != string::npos && idx == 0) + { + contd = true; + break; + } + } + if (contd) + { + strSize = CUtil::TruncateLine(itfmBak->line.length(), 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine = itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + } + else + { + // continuation of a previous directive + strSize = CUtil::TruncateLine(itfmBak->line.length(), strDirLine.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine += "\n" + itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + + if (contd) + { + // drop continuation symbol + if (strDirLine[strDirLine.length()-1] == '\\') + strDirLine = strDirLine.substr(0, strDirLine.length()-1); + + // if a directive or continuation of a directive (no continuation symbol found) + if (iter->line[iter->line.length()-1] != '_') + { + contd = false; + if (result->addSLOC(strDirLine, lineNumber, trunc_flag)) + result->directive_lines[LOG]++; + } + iter->line = ""; + } + } + return 1; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CPythonCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + unsigned int paren_count = 0; + string strLSLOC = ""; + string strLSLOCBak = ""; + + filemap::iterator fit, fitbak; + string line, lineBak; + UIntVector loopWhiteSpace; + + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + line = fit->line; + lineBak = fitbak->line; + + // do not process blank lines + // blank line means blank_line/comment_line/directive + if (!CUtil::CheckBlank(line)) + { + // does the ReplaceQuote get the continuation character \ replaced? + LSLOC(result, line, fit->lineNumber, lineBak, strLSLOC, strLSLOCBak, paren_count, loopWhiteSpace); + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count); + } + } + } + return 1; +} + +// Logical Counting Consideration +/* +Not counted line: +else: +\ (continuation char) +in () +in [] +in {} +end of line preceding by operator characters + - * / = < > | & is in % ^ \ ~ not , : +compound statement, with : and ; +esp. compound statement with : in middle line, not in () [] {} or in else: +*/ + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results +* \param line processed physical line of code +* \param lineBak original physical line of code +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param paren_cnt count of parenthesis +* \param loopWhiteSpace count of white space to determine loop ends +*/ +void CPythonCounter::LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + unsigned int &paren_cnt, UIntVector &loopWhiteSpace) +{ +#define CONT_STR_LENGTH 18 + string continuation_str[] = {"is", "in", "not", "+", "-", "*", "/", "=", "<", ">", "|", "&", "%", "^", "\\", "~", ",", "$"}; + + size_t start = 0; // starting index of the working string + size_t i = 0, idx, strSize; + int n; + bool trunc_flag = false; + unsigned int cnt = 0, numWS; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + string tmp; + + // process: + // paren_cnt is used with {} [] () + // 1. check if the current char is in one of the parentheses + // 2. if no, check if the line has : or ; (statement separators), except else: + // 3. if yes, count and put the statement in the result + // 4. if the line does not ends with a continuation string or a statement separator (handled) + // and the line is not in one of the parentheses + // then count and put the statement in the result + // 5. physical count considers all lines executables (or directives, no declarations) + + // check for loop ends, new loops, and record white space in order to determine ends + if (print_cmplx) + { + // check white space for loop ends + if (loopWhiteSpace.size() > 0) + { + // get white space + tmp = line; + tmp = CUtil::TrimString(tmp, -1); + numWS = (unsigned)(line.length() - tmp.length()); + + // check for loop ends + for (n = (int)loopWhiteSpace.size() - 1; n >= 0; n--) + { + if (loopWhiteSpace.at( (unsigned)n ) != numWS) + break; + else + loopWhiteSpace.pop_back(); + } + } + + // check for loop keywords (for, while) + cnt = 0; + CUtil::CountTally(line, loop_keywords, cnt, 1, exclude, "", "", NULL); + if (cnt > 0) + { + if (loopWhiteSpace.size() < 1) + { + // get white space + tmp = line; + tmp = CUtil::TrimString(tmp, -1); + numWS = (unsigned)(line.length() - tmp.length()); + } + + // add nested loop white space and record nested loop level + for (i = 0; i < cnt; i++) + { + loopWhiteSpace.push_back(numWS); + + if ((unsigned int)result->cmplx_nestloop_count.size() < loopWhiteSpace.size()) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopWhiteSpace.size()-1]++; + } + } + } + + line = CUtil::TrimString(line); + lineBak = CUtil::TrimString(lineBak); + size_t line_length = line.length(); + bool lineContinued = false; + + while (i < line_length) + { + switch (line[i]) + { + case '{': case '[': case '(': // parentheses opener + paren_cnt++; + break; + case '}': case ']': case ')': // parentheses closer + if (paren_cnt > 0) + paren_cnt--; + break; + } + + // 2. if no parentheses enclosing, and if the char is a statement separator + if (paren_cnt == 0 && (line[i] == ';' || line[i] == ':')) + { + tmp = CUtil::ClearRedundantSpaces(line); + // if line[..i] is else: then exit the outer if + if (tmp.rfind("else:") != tmp.length() - 5) + { + // 3. + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, i); + strLSLOCBak += lineBak.substr(start, i); + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + { + // increase logical SLOC here + result->exec_lines[LOG]++; + } + strLSLOC = strLSLOCBak = ""; + start = i + 1; + } + else + lineContinued = true; + } + i++; + } + + if (paren_cnt == 0) + { + // add logical SLOC if the line does not end with a continuation string/char + if (!lineContinued) + { + for (i = 0; i < CONT_STR_LENGTH; i++) + { + if (continuation_str[i].length() == 1) + { + if (line[line_length - 1] == continuation_str[i][0]) + { + lineContinued = true; + break; + } + } + else + { + idx = CUtil::FindKeyword(line, continuation_str[i]); + if (idx != string::npos && idx == line_length - continuation_str[i].length() - 1) + { + lineContinued = true; + break; + } + } + } + } + + if (!lineContinued) + { + strSize = CUtil::TruncateLine(line_length - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, line_length); + strLSLOCBak += lineBak.substr(start, line_length); + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + { + // increase logical SLOC here + result->exec_lines[LOG]++; + } + strLSLOC = strLSLOCBak = ""; + } + else + { + tmp = CUtil::TrimString(line.substr(start, line_length - start)); + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + tmp = CUtil::TrimString(lineBak.substr(start, line_length - start)); + strLSLOCBak += tmp.substr(0, strSize); + } + } + } + result->exec_lines[PHY]++; +} + +/*! + * Counts file language complexity based on specified language keywords/characters. + * + * \param fmap list of processed file lines + * \param result counter results + * + * \return method status + */ +int CPythonCounter::CountComplexity(filemap* fmap, results* result) +{ + if (classtype == UNKNOWN || classtype == DATAFILE) + return 0; + + ENTER_1( "Start" ); // Due to Polymorphism change level above +// try +// { + + filemap::iterator fit; + unsigned int cnt, ret, cyclomatic_cnt = 0, ignore_cyclomatic_cnt = 0, main_cyclomatic_cnt = 1, /*function_count = 0, */cyclomatic_logic_cnt = 0, main_cyclomatic_logic_cnt = 1; // warning fix + bool isMainExisted = false; + string line, needIndentation = "", file_ext, function_name = ""; + vector indenStack; + indenStack.push_back(0); + int numWS; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + StringVector function_stack; + stack cyclomatic_stack; + stack cyclomatic_logic_stack; + +// from above we know Python has Cyclomatic Complexity keywords with no skip + + SAVE_TO_2( "Start CPythonCounter::CountComplexity" ); + + // process each line + bool notEndFile = true; + filemap::iterator fitEnd = fmap->end(); + currentPhyLine = 0; + for (fit = fmap->begin(); notEndFile; fit++) + { + currentPhyLine++; + if ( fitEnd == fit ) { + notEndFile = false; + } + if (!notEndFile) { + line = " endofmain"; + //to handle last line of a file belongs to one function: + // return(1) --> end of function. + }else{ + line = fit->line; + SAVE_TO_2( "CUtil::CheckBlank" ); + if (CUtil::CheckBlank(line)) + continue; + + line = " " + line; + } + + //cout << line << "meiyige"; + // mathematical functions + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Math" ); + CUtil::CountTally(line, math_func_list, cnt, 1, exclude, "", "", &result->math_func_count, casesensitive); + result->cmplx_math_lines += cnt; + + // trigonometric functions + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Trigonometric" ); + CUtil::CountTally(line, trig_func_list, cnt, 1, exclude, "", "", &result->trig_func_count, casesensitive); + result->cmplx_trig_lines += cnt; + + // logarithmic functions + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Logarithmic" ); + CUtil::CountTally(line, log_func_list, cnt, 1, exclude, "", "", &result->log_func_count, casesensitive); + result->cmplx_logarithm_lines += cnt; + + // calculations + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Calculations" ); + CUtil::CountTally(line, cmplx_calc_list, cnt, 1, exclude, "", "", &result->cmplx_calc_count, casesensitive); + result->cmplx_calc_lines += cnt; + + // conditionals + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Conditionals" ); + CUtil::CountTally(line, cmplx_cond_list, cnt, 1, exclude, "", "", &result->cmplx_cond_count, casesensitive); + result->cmplx_cond_lines += cnt; + + // logical operators + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Logical" ); + CUtil::CountTally(line, cmplx_logic_list, cnt, 1, exclude, "", "", &result->cmplx_logic_count, casesensitive); + result->cmplx_logic_lines += cnt; + + // preprocessor directives + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Preprocessor" ); + CUtil::CountTally(line, cmplx_preproc_list, cnt, 1, exclude, "", "", &result->cmplx_preproc_count, casesensitive); + result->cmplx_preproc_lines += cnt; + + // assignments + cnt = 0; + SAVE_TO_2( "CUtil::CountTally Assignments" ); + CUtil::CountTally(line, cmplx_assign_list, cnt, 1, exclude, "", "", &result->cmplx_assign_count, casesensitive); + result->cmplx_assign_lines += cnt; + + // Pointers not in Python + +// Python has Cyclomatic Complexity keywords with no skip +// cyclomatic complexity + + // search for cyclomatic complexity keywords + SAVE_TO_2( "CUtil::CountTally Cyclomatic Complexity" ); + CUtil::CountTally(line, cmplx_cyclomatic_list, cyclomatic_cnt, 1, exclude, "", "", 0, casesensitive); + + // Python does not have Cyclomatic Complexity keywords to exclude + /* + if (ignore_cmplx_cyclomatic_list.size() > 0) + { + SAVE_TO_2( "CUtil::CountTally exclude keywords" ); + CUtil::CountTally(line, ignore_cmplx_cyclomatic_list, ignore_cyclomatic_cnt, 1, exclude, "", "", 0, casesensitive); + } + */ + + // Python has cyclomatic complexity logical keywords + SAVE_TO_2( "CUtil::CountTally complexity Logical" ); + CUtil::CountTally(line, cmplx_cyclomatic_logic_list, cyclomatic_logic_cnt, 1, exclude, "", "", 0, casesensitive); + + // parse function name if found + SAVE_TO_2( "ParseFunctionName" ); + ret = (unsigned)ParseFunctionName(line, needIndentation, function_stack, function_name, indenStack, numWS); + + if (ret != 1 && !cyclomatic_stack.empty() && cyclomatic_stack.size() == function_stack.size()-1) + { + // remove count stack entry for non-function names + cyclomatic_cnt += cyclomatic_stack.top(); + ignore_cyclomatic_cnt = 0; + cyclomatic_stack.pop(); + } + if (ret != 1 && !cyclomatic_logic_stack.empty() && cyclomatic_logic_stack.size() == function_stack.size()-1) + { + // remove count stack entry for non-function names + cyclomatic_logic_cnt += cyclomatic_logic_stack.top(); + cyclomatic_logic_stack.pop(); + } + + // modify the condition when comes to function_stack.size() [add 1] + if (ret == 1) // and modify this part + { + //this line is not in the current function, handle it later + int remember = (int)( cyclomatic_cnt - ignore_cyclomatic_cnt + 1 ); + int remember2 = remember + (int)cyclomatic_logic_cnt; + + + cyclomatic_cnt = 0; + ignore_cyclomatic_cnt = 0; + cyclomatic_logic_cnt = 0; + SAVE_TO_2( "CUtil::CountTally Cyclomatic" ); + CUtil::CountTally(line, cmplx_cyclomatic_list, cyclomatic_cnt, 1, exclude, "", "", 0, casesensitive); + + // Python does not have Cyclomatic Complexity keywords to exclude + /* + // get this line's complexity + if (ignore_cmplx_cyclomatic_list.size() > 0) + { + SAVE_TO_2( "CUtil::CountTally Ignore Cyclomatic" ); + CUtil::CountTally(line, ignore_cmplx_cyclomatic_list, ignore_cyclomatic_cnt, 1, exclude, "", "", 0, casesensitive); + } + */ + + // Python has Complex Cyclomatic logic keywords + SAVE_TO_2( "CUtil::CountTally Cyclomatic Logic" ); + CUtil::CountTally(line, cmplx_cyclomatic_logic_list, cyclomatic_logic_cnt, 1, exclude, "", "", 0, casesensitive); + + //substract the current line's complexity + remember -= cyclomatic_cnt - ignore_cyclomatic_cnt; + remember2 -= cyclomatic_cnt - ignore_cyclomatic_cnt + cyclomatic_logic_cnt; + + // capture count at end of function + lineElement element((unsigned)remember, function_name); + result->cmplx_cycfunct_count.push_back(element); + lineElement n_element((unsigned)remember2, function_name); + result->cmplx_cycfunct_CC2_count.push_back(n_element); + + function_name = ""; + if (function_stack.size() > 1) + { + // grab previous function from stack to continue + if (!cyclomatic_stack.empty()) + { + int flag = 0; + //both the stacks' first element is redundant so " ** > 1 " + while (indenStack.size() > 1 && function_stack.size() > 1) { + if (flag == 1) { + indenStack.pop_back(); + function_stack.pop_back(); + } + if (numWS >= indenStack.back()) { + //the current line belongs to one of the functions in the function_stack + break; + } + + int temp = (int)cyclomatic_stack.top() + 1; + cyclomatic_stack.pop(); + int temp2 = (int)cyclomatic_logic_stack.top(); + cyclomatic_logic_stack.pop(); + string str = function_stack.back(); + + size_t idx = str.find("("); + if (idx != string::npos) + { + SAVE_TO_2( "CUtil::ClearRedundantSpaces" ); + function_name = CUtil::ClearRedundantSpaces(str.substr(0, idx)); + } + lineElement element( (unsigned)temp, function_name); + result->cmplx_cycfunct_count.push_back( element ); + lineElement n_element( (unsigned)( temp + temp2 ), function_name); + result->cmplx_cycfunct_CC2_count.push_back(n_element); + + function_name = ""; + + flag = 1; + } + if (indenStack.size() > 1) + { + //the current line belongs to one of the functions in the function_stack + cyclomatic_cnt += cyclomatic_stack.top() - ignore_cyclomatic_cnt; + cyclomatic_stack.pop(); + cyclomatic_logic_cnt += cyclomatic_logic_stack.top(); + cyclomatic_logic_stack.pop(); + }else{ + //the current line not belongs to any of the functions in the function_stack, so it should belong to main + if (line != " endofmain") + { + main_cyclomatic_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt; + main_cyclomatic_logic_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt + cyclomatic_logic_cnt; + isMainExisted = true; + } + cyclomatic_cnt = cyclomatic_logic_cnt = 0; + } + } + } + else{ + if (line != " endofmain") { + main_cyclomatic_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt; + main_cyclomatic_logic_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt + cyclomatic_logic_cnt; + isMainExisted = true; + } + cyclomatic_cnt = cyclomatic_logic_cnt = 0; + } + ignore_cyclomatic_cnt = 0; + } + else if (ret == 2) + { + // some code doesn't belong to any function + if (line != " endofmain") { + main_cyclomatic_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt; + main_cyclomatic_logic_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt + cyclomatic_logic_cnt; + isMainExisted = true; + + } + cyclomatic_cnt = ignore_cyclomatic_cnt = cyclomatic_logic_cnt = 0; + } + else { + if (function_stack.size() > 1 && (function_stack.size() - 1 > cyclomatic_stack.size() + 1 || (cyclomatic_stack.empty() && function_stack.size() - 1 > 1))) + { + // capture previous complexity count from open function + cyclomatic_stack.push(cyclomatic_cnt - ignore_cyclomatic_cnt); + cyclomatic_cnt = ignore_cyclomatic_cnt = 0; + } + if (function_stack.size() > 1 && (function_stack.size() - 1 > cyclomatic_logic_stack.size() + 1 || (cyclomatic_logic_stack.empty() && function_stack.size() - 1 > 1))) + { + // capture previous complexity count from open function + cyclomatic_logic_stack.push(cyclomatic_logic_cnt); + cyclomatic_logic_cnt = 0; + } + } + + // Avoid going past end. + if ( fitEnd == fit ) + break; + } + + // done with a file, if has "main" code add it + if (isMainExisted) + { + lineElement element(main_cyclomatic_cnt, "main"); + lineElement n_element(main_cyclomatic_logic_cnt, "main"); + result->cmplx_cycfunct_count.push_back(element); + result->cmplx_cycfunct_CC2_count.push_back(n_element); + } + +/* + } + catch(const std::exception& e) + { + string dumpInfo; + string what = e.what(); + StackDump( dumpInfo, EXCEPTION_HANDLER_PARSER, this->parse_threadIdx, EXCEPTION_STD_EXCEPTION, what, this ); + throw( e ); + } +*/ + return 1; +} + +/*! + * Parses lines for function/method names. + * + * \param line line to be processed + * \param needIndentation last line processed + * \param functionStack stack of functions + * \param functionName function name found + * + * \return 1 if function name is found + */ +int CPythonCounter::ParseFunctionName(const string &line, string & needIndentation, StringVector &functionStack, string &functionName, vector &indenStack, int & numWS) +{ + string str; + size_t idx; + int functionWS = 0; + if(indenStack.empty()){ + indenStack.push_back(0); + } + functionWS = indenStack.back(); // current function's indentation + if (functionStack.empty()) { + functionStack.push_back(""); + needIndentation = ""; + } + + string tmp; + //get white spaces + tmp = line; + tmp = CUtil::TrimString(tmp, -1); + + numWS = (int)( line.length() - tmp.length() ); + + //last line is with "def", if needIndentation=="YES" + //not consider the body of function is in the same line of "def" + if(needIndentation.length() >= 3 && needIndentation == "YES"){ + indenStack.push_back(numWS); + needIndentation = ""; + } + string mark = needIndentation; + + idx = CUtil::FindKeyword(line, "def"); + if (idx != string::npos) + { + if (idx + 4 < line.length()) + { + str = line.substr(idx + 4); + functionStack.push_back(str); + needIndentation = "YES"; //indicate next line need to get the indentation + } + } + if (functionStack.size() == 1 || functionStack.size() == 0) + { + // dealing with some code out of any subroutines, it is a "main" code + return 2; + } + + if(mark != "YES"){ + if (functionWS > numWS && functionStack.size() > 1 ) { + //the first element in functionStack is "" + + //we need to get the function and pop the function from the functionStack + if(needIndentation=="YES"){ + //we have already pushed the new function to the functionStack + string tempp = functionStack.back(); //pop new function + functionStack.pop_back(); + + str = functionStack.back(); + functionStack.pop_back(); + + functionStack.push_back(tempp); // push new function + indenStack.pop_back(); //pop the function's indentation in the stack + } + else{ + str = functionStack.back(); + functionStack.pop_back(); + indenStack.pop_back(); //pop the function's indentation in the stack + + } + + //get function's name + idx = str.find("("); + if (idx != string::npos) + { + functionName = CUtil::ClearRedundantSpaces(str.substr(0, idx)); + return 1; + } + } + } + return 0; +} diff --git a/src/CPythonCounter.h b/src/CPythonCounter.h new file mode 100644 index 0000000..8b745e1 --- /dev/null +++ b/src/CPythonCounter.h @@ -0,0 +1,50 @@ +//! Code counter class definition for the Python language. +/*! +* \file CPythonCounter.h +* +* This file contains the code counter class definition for the Python language. +*/ + +#ifndef CPythonCounter_h +#define CPythonCounter_h + +#include "CCodeCounter.h" + +//! Python code counter class. +/*! +* \class CPythonCounter +* +* Defines the Python code counter class. +*/ +class CPythonCounter : public CCodeCounter +{ +public: + CPythonCounter(); + +protected: + StringVector loop_keywords; //!< List of keywords to indicate the beginning of a loop + using CCodeCounter::ReplaceQuote; // fix warning + virtual int ReplaceQuote(string &strline, size_t &idx_start, bool &contd, string &CurrentQuoteEnd); + virtual int CountCommentsSLOC(filemap* fmap, results* result, filemap* fmapBak = NULL); + virtual int CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak = NULL); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak = NULL); + void LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + unsigned int &paren_cnt, UIntVector &loopWhiteSpace); + using CCodeCounter::ParseFunctionName; // fix warning + int ParseFunctionName(const string &line, string &lastline, StringVector &functionStack, string &functionName, vector &condenStack, int & numWS); + int CountComplexity(filemap* fmap, results* result); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CPythonCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CPythonCounter(const CPythonCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CPythonCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CPythonCounter operator=(const CPythonCounter); // Declare without implementation +}; + +#endif diff --git a/src/CRubyCounter.cpp b/src/CRubyCounter.cpp new file mode 100644 index 0000000..66b4894 --- /dev/null +++ b/src/CRubyCounter.cpp @@ -0,0 +1,668 @@ +//! Code counter class methods for the Ruby language. +/*! +* \file CRubyCounter.cpp +* +* This file contains the code counter class methods for the Ruby language. +*/ + +#include "CRubyCounter.h" + +/*! +* Constructs a CRubyCounter object. +*/ +CRubyCounter::CRubyCounter() +{ + classtype = RUBY; + language_name = "Ruby"; + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("Ruby", file_extension); + //file_extension.push_back(".rb"); + + QuoteStart = "\"'%/<"; + QuoteEnd = "\"'/"; + QuoteEscapeFront = '\\'; + ContinueLine = ".,\\+-*/"; + + BlockCommentStart.push_back("=begin"); + BlockCommentEnd.push_back("=end"); + + LineCommentStart.push_back("#"); + delimiter = ""; + + exec_name_list.push_back("alias"); + exec_name_list.push_back("begin"); + exec_name_list.push_back("break"); + exec_name_list.push_back("case"); + exec_name_list.push_back("catch"); + exec_name_list.push_back("collect"); + exec_name_list.push_back("continue"); + exec_name_list.push_back("default"); + exec_name_list.push_back("die"); + exec_name_list.push_back("do"); + exec_name_list.push_back("each"); + exec_name_list.push_back("else"); + exec_name_list.push_back("elsif"); + exec_name_list.push_back("end"); + exec_name_list.push_back("ensure"); + exec_name_list.push_back("exception"); + exec_name_list.push_back("exit"); + exec_name_list.push_back("for"); + exec_name_list.push_back("if"); + exec_name_list.push_back("module"); + exec_name_list.push_back("new"); + exec_name_list.push_back("next"); + exec_name_list.push_back("puts"); + exec_name_list.push_back("print"); + exec_name_list.push_back("redo"); + exec_name_list.push_back("rescue"); + exec_name_list.push_back("retry"); + exec_name_list.push_back("return"); + exec_name_list.push_back("switch"); + exec_name_list.push_back("throw"); + exec_name_list.push_back("try"); + exec_name_list.push_back("undef"); + exec_name_list.push_back("unless"); + exec_name_list.push_back("until"); + exec_name_list.push_back("when"); + exec_name_list.push_back("while"); + exec_name_list.push_back("yield"); + + math_func_list.push_back("atan2"); + math_func_list.push_back("cos"); + math_func_list.push_back("exp"); + math_func_list.push_back("frexp"); + math_func_list.push_back("ldexp"); + math_func_list.push_back("rand"); + math_func_list.push_back("sin"); + math_func_list.push_back("sqrt"); + math_func_list.push_back("srand"); + math_func_list.push_back("tan"); + + log_func_list.push_back("log"); + log_func_list.push_back("log10"); + + cmplx_calc_list.push_back("%"); + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("/"); + cmplx_calc_list.push_back("**"); + + cmplx_cond_list.push_back("begin"); + cmplx_cond_list.push_back("case"); + cmplx_cond_list.push_back("else"); + cmplx_cond_list.push_back("elsif"); + cmplx_cond_list.push_back("ensure"); + cmplx_cond_list.push_back("for"); + cmplx_cond_list.push_back("if"); + cmplx_cond_list.push_back("rescue"); + cmplx_cond_list.push_back("unless"); + cmplx_cond_list.push_back("until"); + cmplx_cond_list.push_back("when"); + cmplx_cond_list.push_back("while"); + + cmplx_logic_list.push_back("&&"); + cmplx_logic_list.push_back("||"); + cmplx_logic_list.push_back("=="); + cmplx_logic_list.push_back("<=>"); + cmplx_logic_list.push_back("!"); + cmplx_logic_list.push_back("and"); + cmplx_logic_list.push_back("not"); + cmplx_logic_list.push_back("or"); + cmplx_logic_list.push_back("~"); + cmplx_logic_list.push_back(">"); + cmplx_logic_list.push_back("<"); + cmplx_logic_list.push_back(">="); + cmplx_logic_list.push_back("=<"); + cmplx_logic_list.push_back("==="); + + cmplx_assign_list.push_back("="); + cmplx_assign_list.push_back("+="); + cmplx_assign_list.push_back("-="); + cmplx_assign_list.push_back("*="); + cmplx_assign_list.push_back("/="); + cmplx_assign_list.push_back("%="); + cmplx_assign_list.push_back("**="); + + /* Adding Complexity count in the language */ + + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("elsif"); + cmplx_cyclomatic_list.push_back("while"); + cmplx_cyclomatic_list.push_back("when"); + cmplx_cyclomatic_list.push_back("for"); + cmplx_cyclomatic_list.push_back("unless"); + cmplx_cyclomatic_list.push_back("until"); + cmplx_cyclomatic_list.push_back("each"); + cmplx_cyclomatic_list.push_back("rescue"); + + cmplx_cyclomatic_logic_list.push_back("&&"); + cmplx_cyclomatic_logic_list.push_back("||"); + cmplx_cyclomatic_logic_list.push_back("and"); + cmplx_cyclomatic_logic_list.push_back("or"); + + cmplx_cyclomatic_case_list.push_back("when"); + cmplx_cyclomatic_switch_list.push_back("case"); +} + +/*! +* Replaces quoted strings inside a string starting at idx_start with '$'. +* Handles special cases for Ruby string literals. +* +* \param strline string to be processed +* \param idx_start index of line character to start search +* \param contd specifies the quote string is continued from the previous line +* \param CurrentQuoteEnd end quote character of the current status +* +* \return method status +*/ +int CRubyCounter::ReplaceQuote(string &strline, size_t &idx_start, bool &contd, char &CurrentQuoteEnd) +{ + //%DStrD # multiple lines allowed + //%QDStrD # multiple lines allowed + //%qDStrD # multiple lines allowed + //< + // blank is also used as a delimiter for string literal started with % + size_t i = 0, startpos = 0, endpos = string::npos; + bool foundQuote = false; + size_t strlen = strline.length(); + unsigned int paren = 1; + + if (contd) + { + string tstrline = CUtil::TrimString(strline, 1); + if ((tstrline.length() == 0 && delimiter == "\n") || // end of string literal with the new line as delimiter, e.g., % + delimiter == tstrline) + { + // end of here document + for (i = 0; i < ContinueLine.length(); i++) + { + if (delimiter[0] == ContinueLine[i]) + { + // avoid continuation + strline = "$"; + break; + } + } + contd = false; + delimiter = ""; + idx_start = strline.length(); + return 0; + } + else + { + // replace the whole line + strline.replace(idx_start, strlen - idx_start, strlen - idx_start, '$'); + if (strline.length() > 0) + strline[strline.length() - 1] = ContinueLine[0]; // continue the line + else + strline = string(1, ContinueLine[0]); + return 0; + } + } + else + { + while (i < strlen && !contd) + { + switch (strline[i]) + { + case '%': + if (!foundQuote) + { + foundQuote = true; + startpos = i; + if (i + 1 < strlen) + { + if (strline[i + 1] == 'Q' || + strline[i + 1] == 'q' || + strline[i + 1] == 'r' || + strline[i + 1] == 's' || + strline[i + 1] == 'W' || + strline[i + 1] == 'w' || + strline[i + 1] == 'x') + i++; + } + if (i + 1 == strlen) + { + contd = true; // continued in the next line + idx_start = i; + delimiter = "\n"; + strline[i] = ContinueLine[0]; + return 0; + } + else + { + delimiter = string(1, strline[i + 1]); + paren = 1; + } + i++; + } + break; + case '<': + if (!foundQuote) + { + if (i + 2 < strlen && strline[i + 1] == '<' && strline[i + 2] != ' ' && strline[i + 2] != '\t') + { + // here document + // foundQuote = true; + // get the last delimiter specifying the end of the 'here document' + + // find the last delimiter, check for the last delimiter word not in a comment + idx = strline.find_last_of("#"); + if (idx == string::npos) + idx = strlen; + idx = idx - 1; + string tstrline = CUtil::TrimString(strline.substr(i + 2, idx + 1 - i - 2)); + + strline[idx] = ContinueLine[0]; // indicate that the string will continue in the next line, so one SLOC is counted + idx_start = idx + 1; + + idx = tstrline.find_last_of("<<"); // another here-doc delimiter? + + // if yes, get the last delimiter + if (idx != string::npos) + tstrline = tstrline.substr(idx + 1, tstrline.length()); + + tstrline = CUtil::TrimString(tstrline); + + size_t i1 = 0; + if (tstrline.length() > 2 && (tstrline.substr(0, 2) == "-\"" || tstrline.substr(0, 2) == "-'")) + { + i1 = 2; + char qChar = tstrline[1]; + while (i1 < tstrline.length() && tstrline[i1] != qChar) + i1++; + } + else + { + while (i1 < tstrline.length() && heredoc_deli.find(tstrline[i1]) != string::npos) + i1++; + } + + delimiter = tstrline.substr(0, i1); + delimiter = CUtil::TrimString(delimiter); + + delimiter = CUtil::EraseString(delimiter, "\""); + delimiter = CUtil::EraseString(delimiter, "\'"); + delimiter = CUtil::EraseString(delimiter, "-"); + + contd = true; + return 0; + } + else + { + idx_start = strline.length(); + return 1; + } + } + break; + case '/': + if (!foundQuote) + { + delimiter = "/"; + foundQuote = true; + startpos = i; + } + else + { + strline.replace(startpos, i - startpos + 1, i - startpos + 1, '$'); + delimiter = ""; + foundQuote = false; + idx_start = i + 1; + } + break; + default: + if (foundQuote) + { + if (delimiter.length() == 1 && delimiter[0] == strline[i] && + openParentheses.find(delimiter) != string::npos) + { + paren++; + } + + // check if the char is a closing bracket + if ((idx = closedParentheses.find(strline[i])) != string::npos) + { + if (delimiter.length() > 0 && openParentheses[idx] == delimiter[0] && paren > 0) + paren--; + if (paren == 0) + { + // here we go, replace string starting from startpos? + endpos = i; + } + } + else + { + if (delimiter.length() > 0 && strline[i] == delimiter[0] && paren == 1) + { + // here we go, replace string starting from startpos + endpos = i; + } + } + + if (endpos != string::npos) + { + foundQuote = false; + // now, replace string from startpos to endpos + strline.replace(startpos, endpos - startpos + 1, endpos - startpos + 1, '$'); + contd = false; + delimiter = ""; + endpos = string::npos; + idx_start = i + 1; + } + } + break; + } + i++; + if (i > idx_start && !foundQuote) + break; + } + } + int ret = CCodeCounter::ReplaceQuote(strline, idx_start, contd, CurrentQuoteEnd); + if (contd && !foundQuote) + { + strline[strline.length() - 1] = ContinueLine[0]; + if (delimiter == "") + delimiter = CurrentQuoteEnd; + } + return ret; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CRubyCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + string strLSLOC = ""; + string strLSLOCBak = ""; + + filemap::iterator fit, fitbak; + string line, lineBak; + StringVector loopLevel; + + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + delimiter = ""; + + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + line = fit->line; + lineBak = fitbak->line; + + // do not process blank lines + // blank line means blank_line/comment_line/directive + if (!CUtil::CheckBlank(line)) + { + LSLOC(result, line, fit->lineNumber, lineBak, strLSLOC, strLSLOCBak); + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(" " + line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count); + } + + // no data declaration in Ruby + result->exec_lines[PHY]++; + } + } + return 1; +} + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results +* \param line processed physical line of code +* \param lineBak original physical line of code +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +*/ +void CRubyCounter::LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak) +{ + const string control_modifiers[] = {"if", "for", "unless", "while", "until", "when", "close", "elsif", "else", "then"}; + static size_t control_mod_cnt = 10; + size_t start = 0; // starting index of the working string + size_t i, j, strSize = string::npos; + size_t idx = string::npos; + bool trunc_flag = false; + + string tmp = CUtil::TrimString(strLSLOC); + string tline = CUtil::TrimString(line); + bool line_skipped = false; + + if (tline == "end" || tline == "else" || tline == "}" || tline == "]") + { + if (tmp.length() > 0) + line_skipped = true; + else + return; + } + + while (start < line.length()) + { + strSize = string::npos; + + // get first control modifier + idx = line.find(';', start); + i = line.find(':', start); + if (i != string::npos && (idx == string::npos || i < idx)) + { + // avoid processing :: and splitting ternary operator x ? y : z + if (i >= line.length() - 1 || line[i + 1] != ':') + { + for (j = i - 1; j > start; j--) + { + if (line[j] == '?') + break; + } + if (j <= start) + idx = i; + } + } + for (j = 0; j < control_mod_cnt; j++) + { + i = CUtil::FindKeyword(line, control_modifiers[j], start); + if (i != string::npos && (idx == string::npos || i < idx)) + { + if (CUtil::FindKeyword(tline, control_modifiers[j]) != 0) + idx = i; + } + } + + // process modifier + if (idx != string::npos) + { + if (line[idx] == ';') + { + strSize = CUtil::TruncateLine(idx + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 1 && CUtil::TrimString(line.substr(start, strSize - 1)) != ";") + { + // only include ';' if it stands alone + strSize--; + } + idx++; + } + else if (line[idx] == ':') + { + strSize = CUtil::TruncateLine(idx + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + idx++; + } + else if (line.length() >= idx + 4 && line.substr(idx, 4) == "then") + { + idx += 4; + strSize = CUtil::TruncateLine(idx - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + } + else + strSize = CUtil::TruncateLine(idx - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + } + + if (strSize > 0 && strSize != string::npos) // only if (idx != 0 && idx != string::npos) returns true + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + else if (!line_skipped) + { + strSize = CUtil::TruncateLine(line.length() - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + + if (tline.length() > 0) + { + for (i = 0; i < ContinueLine.length(); i++) + { + if (tline.find_last_of(ContinueLine[i]) == tline.length() - 1) + { + // continued to the next line + if (tline[tline.length() - 1] == '\\') + { + i = strLSLOC.find_last_of('\\'); + if (i != string::npos) + { + strLSLOC[i] = ' '; + strLSLOCBak[i] = ' '; + } + } + return; + } + } + } + } + if (CUtil::TrimString(strLSLOC).length() == 0) + return; + + // add SLOC + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + strLSLOC = strLSLOCBak = ""; + + if (idx != 0 && idx != string::npos) + { + start = idx; + tline = CUtil::TrimString(line.substr(start)); + } + else + start = string::npos; + } +} + +/*! +* Parses lines for function/method names. +* +* \param line line to be processed +* \param lastline last line processed +* \param functionStack stack of functions +* \param functionName function name found +* \param functionCount function count found +* +* \return 1 if function name is found +*/ +int CRubyCounter::ParseFunctionName(const string &line, string &/*lastline*/, + filemap &functionStack, string &functionName, unsigned int &functionCount) +{ + string str; + size_t idx; + unsigned int fcnt; + + string end_marker = "end"; + size_t block_keyword_idx; + string block_keywords[] = {"begin", "case", "if", "do", "for", "unless", "until"}; + static size_t end_marker_count; + /* + * Checking for beginning of function + */ + idx = CUtil::FindKeyword(line, "def"); + if (idx != string::npos) + { + if (idx + 4 < line.length()) + { + str = line.substr(idx + 4); + str = str.substr(0, CUtil::FindKeyword(str, ":")); + lineElement element(++functionCount, str); + functionStack.push_back(element); + } + } + else + { + idx = CUtil::FindKeyword(line, "class"); + if (idx != string::npos) + { + if (idx + 6 < line.length()) + { + str = line.substr(idx + 4); + str = str.substr(0, CUtil::FindKeyword(str, ":")); + lineElement element(++functionCount, str); + functionStack.push_back(element); + } + } + } + /* + * Checking for other keywords + * That also end with block so that function does not get pooped prematurely + */ + for (block_keyword_idx = 0; block_keyword_idx < 7; block_keyword_idx ++) + { + idx = CUtil::FindKeyword(line, block_keywords[block_keyword_idx]); + if (idx != string::npos) + { + end_marker_count += 1; + } + } + if (functionStack.empty()) + { + return 2; + } + idx= CUtil::FindKeyword(line, "end"); + if (idx != string::npos) + { + /* + * This was not a function but a inside block + */ + if (end_marker_count > 0) + { + end_marker_count -= 1; + if (functionStack.empty()) + { + return 2; + } + } + else + { + str = functionStack.back().line; + fcnt = functionStack.back().lineNumber; + functionStack.pop_back(); + functionName = CUtil::ClearRedundantSpaces(str); + functionCount = fcnt; + return 1; + } + } + return 0; +} diff --git a/src/CRubyCounter.h b/src/CRubyCounter.h new file mode 100644 index 0000000..f808095 --- /dev/null +++ b/src/CRubyCounter.h @@ -0,0 +1,46 @@ +//! Code counter class definition for the Ruby language. +/*! +* \file CRubyCounter.h +* +* This file contains the code counter class definition for the Ruby language. +*/ + +#ifndef CRubyCounter_h +#define CRubyCounter_h + +#include "CCodeCounter.h" + +//! Ruby code counter class. +/*! +* \class CRubyCounter +* +* Defines the Ruby code counter class. +*/ +class CRubyCounter : public CCodeCounter +{ +public: + CRubyCounter(); + +protected: + virtual int ReplaceQuote(string &strline, size_t &idx_start, bool &contd, char &CurrentQuoteEnd); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak = NULL); + void LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak); + int ParseFunctionName(const string &line, string &lastline, + filemap &functionStack, string &functionName, unsigned int &functionCount); + + string delimiter; // used to store delimiter of string literals across lines + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CRubyCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CRubyCounter(const CRubyCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CRubyCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CRubyCounter operator=(const CRubyCounter); // Declare without implementation +}; + +#endif diff --git a/src/CScalaCounter.cpp b/src/CScalaCounter.cpp new file mode 100644 index 0000000..3a53d96 --- /dev/null +++ b/src/CScalaCounter.cpp @@ -0,0 +1,1083 @@ +//! Code counter class methods for the Scala language. +/*! +* \file CScalaCounter.cpp +* +* This file contains the code counter class methods for the Scala language. +* +* Because documents can go out of date +* there are comments about the development of this module +* AND +* other ideas about improving UCC +* which may be found at the end of this file or other file(s) as noted. +* +* You are strongly encouraged to fully read these comments before +* making changes and also to keep the comments in step with the new code! +* +* Added to 2015.12 by Randy Maxwell +* Changes started on 2015_10_16 +* Changes ended on 2015_10_24 +*/ + +#include "CScalaCounter.h" + +/*! +* Constructs a CScalaCounter object. +*/ +CScalaCounter::CScalaCounter( string lang ) : CCJavaCsScalaCounter( lang ) +{ +#ifdef ENABLE_1_PASS_PARSER + // Use the new single pass parser + use_1pass_parser = false; +#endif + + // Set the internal default parsing values. + // These default Scala parsing values agree with Scala version and authority. + // See comments below for support of other Scala versions from other creators. + // + classtype = SCALA; + language_name = "Scala"; + language_version = "2.11.5"; + language_version_authority = "scala.org"; // creator of this version + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("Scala", file_extension); + + //file_extension.push_back(".scala"); + +// INVARIANT ! ! ! NO DUPLICATE STRINGS +// +// ALL these declarations are used to build a 1 pass parser element array in CCodeCounter. +// Do NOT declare any duplicate keys (strings) here! +// CCJavaCsScalaCounter will declare SEEMING duplicates that get changed to aux meanings flags. +// We keep this way so that the multi pass parsing code will still work. +// IF 1 pass parsing really works out well (as Randy hopes) the code can be refactored later. +// + + // Below comments give differences from the Java parse support that + // was first used as a example to do this Scala parse support. + // + // Please keep these comments as there may be a significant fraction + // of Scala developers coming from a Java background and we might want + // to give these details out in a document sometime. + // + + // Compiler directive (or preprocessor) keywords + directive.push_back("import"); + directive.push_back("package"); + + // Data keywords; please see comments below about these + // + data_name_list.push_back("abstract"); // don't see how class inheritance is data + data_name_list.push_back("Array"); + // data_name_list.push_back("ArrayList"); // this is Java and not Scala + data_name_list.push_back("Boolean"); // different capitalization from Java + data_name_list.push_back("Byte"); // different capitalization from Java + data_name_list.push_back("Char"); // different capitalization from Java + data_name_list.push_back("class"); // don't see how a class is data + data_name_list.push_back("Double"); // different capitalization from Java + data_name_list.push_back("extends"); // don't see how class inheritance is data + data_name_list.push_back("Float"); // different capitalization from Java + data_name_list.push_back("HashMap"); + data_name_list.push_back("HashSet"); + data_name_list.push_back("implements"); // don't see how class inheritance is data + data_name_list.push_back("Int"); // different capitalization from Java + // data_name_list.push_back("interface"); not in Scala + data_name_list.push_back("LinkedHashMap"); + data_name_list.push_back("LinkedList"); + data_name_list.push_back("Long"); // different capitalization from Java + // data_name_list.push_back("native"); not in Scala + data_name_list.push_back("object"); // don't see how an object is data + data_name_list.push_back("override"); // don't see how class interface is data + data_name_list.push_back("private"); // don't see how access visibility is data + data_name_list.push_back("protected"); // don't see how access visibility is data + // data_name_list.push_back("public"); not in Scala, public is defaut visibility + data_name_list.push_back("sealed"); + data_name_list.push_back("Short"); // different capitalization from Java + data_name_list.push_back("static"); + data_name_list.push_back("String"); + data_name_list.push_back("TreeMap"); + data_name_list.push_back("val"); // immuteable data + data_name_list.push_back("var"); // muteable data + data_name_list.push_back("Vector"); + // data_name_list.push_back("void"); not in Scala + // data_name_list.push_back("volatile"); not in Scala + + // Executable keywords + // + exec_name_list.push_back("break"); + exec_name_list.push_back("case"); + exec_name_list.push_back("catch"); + // exec_name_list.push_back("continue"); not in Scala + exec_name_list.push_back("def"); + // exec_name_list.push_back("default"); not in Scala + exec_name_list.push_back("do"); + exec_name_list.push_back("else"); + exec_name_list.push_back("finally"); + exec_name_list.push_back("for"); + exec_name_list.push_back("if"); + exec_name_list.push_back("match"); // somewhat like Java switch statement + exec_name_list.push_back("new"); + exec_name_list.push_back("return"); + exec_name_list.push_back("super"); + // exec_name_list.push_back("switch"); not in Scala + exec_name_list.push_back("this"); + exec_name_list.push_back("throw"); + // exec_name_list.push_back("throws"); not in Scala + exec_name_list.push_back("try"); + exec_name_list.push_back("while"); + + // Math, Trig and Log functions + // Scala math functions are implicitly imported due to + // import scala._ + // being implicitly done for all Scala source files. + // + // So one way is that the source code would prefix all the below function names with + // math. + // to declare the name of the collection for disambiguation to satisfy the compiler. + // + // Another way is to explicitly + // import scala.math._ + // to get all of them or + // import scala.math.cos + // to get the cosine of an angle function for example. + // in any case the Scala syntax is simply calling the function without the + // math. + // prefix. + // + math_func_list.push_back("abs"); + math_func_list.push_back("cbrt"); + math_func_list.push_back("ceil"); + // math_func_list.push_back("copySign"); not in Scala math package + math_func_list.push_back("E"); + math_func_list.push_back("exp"); + math_func_list.push_back("expm1"); + math_func_list.push_back("floor"); + // math_func_list.push_back("getExponent"); not in Scala math package + math_func_list.push_back("hypot"); + math_func_list.push_back("IEEEremainder"); + math_func_list.push_back("max"); + math_func_list.push_back("min"); + // math_func_list.push_back("nextAfter"); not in Scala math package + // math_func_list.push_back("nextUp"); not in Scala math package + math_func_list.push_back("Pi"); // different capitalization from Java + math_func_list.push_back("pow"); + math_func_list.push_back("random"); + math_func_list.push_back("rint"); + math_func_list.push_back("round"); + // math_func_list.push_back("scalb"); not in Scala math package + math_func_list.push_back("signum"); + math_func_list.push_back("sqrt"); + math_func_list.push_back("toRadians"); + math_func_list.push_back("toDegrees"); + math_func_list.push_back("ulp"); + + trig_func_list.push_back("acos"); + trig_func_list.push_back("asin"); + trig_func_list.push_back("atan"); + trig_func_list.push_back("atan2"); + trig_func_list.push_back("cos"); + trig_func_list.push_back("cosh"); + trig_func_list.push_back("sin"); + trig_func_list.push_back("sinh"); + trig_func_list.push_back("tan"); + trig_func_list.push_back("tanh"); + + log_func_list.push_back("log"); + log_func_list.push_back("log10"); + log_func_list.push_back("log1p"); + + +/* +Use this (needs to be enhanced for 1 pass approach) + + cmplx_cyclomatic_logic_list.push_back("||"); + cmplx_cyclomatic_logic_list.push_back("&&"); + + cmplx_cyclomatic_case_list.push_back("case"); + cmplx_cyclomatic_switch_list.push_back("switch"); + +BELOW IS WRONG ! +Do NOT add more to cmplx_cyclomatic_list ! ! ! + + if ( "SCALA" == lang ) + cmplx_cyclomatic_list.push_back("match"); // switch not in Scala + else + cmplx_cyclomatic_list.push_back("switch"); + cmplx_cyclomatic_listVals.push_back( CYCLOMATIC_CC3 ); + + // Cyclomatic Complexity ring 2: same as CC1 with 2 CC2 Operators/equivalent Keywords list. Logical AND OR + aux_meaning_keys.push_back("&&"); + aux_meaning_vals.push_back( CYCLOMATIC_CC2 ); + + aux_meaning_keys.push_back("||"); + aux_meaning_vals.push_back( CYCLOMATIC_CC2 ); +*/ + +// TODO: Add more keywords/values for Object Orientation and Functional Programming metrics collection + + +} + +/*! +* Counts directive lines of code. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CScalaCounter::CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak) +{ + bool contd = false; + bool trunc_flag = false; + size_t idx, strSize; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string strDirLine = ""; + + filemap::iterator itfmBak = fmapBak->begin(); + currentPhyLine = 0; + for ( filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++, itfmBak++ ) + { + currentPhyLine++; + if ( CUtil::CheckBlank( iter->line ) ) + continue; + + if ( print_cmplx ) + { + cnt = 0; + CUtil::CountTally( " " + iter->line, directive, cnt, 1, exclude, "", "", &result->directive_count ); + } + + if ( !contd ) + { + // if not a continuation of a previous directive + for ( vector::iterator viter = directive.begin(); viter != directive.end(); viter++ ) + { + // ensures the keyword stands alone, avoid, e.g., #ifabc + if ( ((idx = CUtil::FindKeyword(iter->line, *viter)) != string::npos) && idx == 0 ) + { + contd = true; + break; + } + } + if (contd) + { + strSize = CUtil::TruncateLine(itfmBak->line.length(), 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine = itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + } + else + { + // continuation of a previous directive + strSize = CUtil::TruncateLine(itfmBak->line.length(), strDirLine.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine += "\n" + itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + + if (contd) + { + // drop continuation symbol NO Line Continuation for SCALA + //if (strDirLine[strDirLine.length()-1] == '\\') + // strDirLine = strDirLine.substr(0, strDirLine.length()-1); + + // if a directive or continuation of a directive (no continuation symbol found) + // if (iter->line[iter->line.length()-1] != ',' && iter->line[iter->line.length()-1] != '\\') + // There is NO continuation symbol in Scala + if (iter->line[iter->line.length()-1] != ',' ) + { + contd = false; + if (result->addSLOC(strDirLine, trunc_flag)) + result->directive_lines[LOG]++; + } + iter->line = ""; + } + } + return 1; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CScalaCounter::LanguageSpecificProcess( filemap* fmap, results* result, filemap* fmapBak ) +{ + unsigned int paren_count = 0; + bool for_flag = false; + bool found_for = false; + bool found_forifwhile = false; + bool found_while = false; + char prev_char = 0; + bool data_continue = false; + bool inArrayDec = false; + string strLSLOC = ""; + string strLSLOCBak = ""; + unsigned int openBrackets = 0; + + filemap::iterator fit, fitbak; + string line, lineBak; + StringVector loopLevel; + + unsigned int phys_exec_lines = 0; + unsigned int phys_data_lines = 0; + unsigned int temp_lines = 0; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + line = fit->line; + + // insert blank at the beginning (for searching keywords) + line = ' ' + line; + lineBak = ' ' + fitbak->line; + + // do not process blank lines + // blank line means blank_line/comment_line/directive + if ( ! CUtil::CheckBlank( line ) ) + { + LSLOC( result, line, lineBak, strLSLOC, strLSLOCBak, paren_count, for_flag, found_forifwhile, found_while, + prev_char, data_continue, temp_lines, phys_exec_lines, phys_data_lines, inArrayDec, found_for, + openBrackets, loopLevel ); + + if ( print_cmplx ) + { + cnt = 0; + CUtil::CountTally( line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count ); + } + + result->exec_lines[PHY] += phys_exec_lines; + phys_exec_lines = 0; + + result->data_lines[PHY] += phys_data_lines; + phys_data_lines = 0; + } + } + return 1; +} + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results +* \param line processed physical line of code +* \param lineBak original physical line of code +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param paren_cnt count of parenthesis +* \param forflag found for flag +* \param found_forifwhile found for, if, or while flag +* \param found_while found while flag +* \param prev_char previous character +* \param data_continue continuation of a data declaration line +* \param temp_lines tracks physical line count +* \param phys_exec_lines number of physical executable lines +* \param phys_data_lines number of physical data lines +* \param inArrayDec marks an array declaration +* \param found_for found for loop +* \param openBrackets number of open brackets (no matching close bracket) +* \param loopLevel nested loop level +*/ +void CScalaCounter::LSLOC(results* result, string line, string lineBak, string &strLSLOC, string &strLSLOCBak, unsigned int &paren_cnt, + bool &forflag, bool &found_forifwhile, bool &found_while, char &prev_char, bool &data_continue, + unsigned int &temp_lines, unsigned int &phys_exec_lines, unsigned int &phys_data_lines, + bool &inArrayDec, bool &found_for, unsigned int &openBrackets, StringVector &loopLevel) +{ + // paren_cnt is used with 'for' statement only + size_t start = 0; //starting index of the working string + size_t i = 0, strSize; + bool found_do = false; + bool found_try = false; + bool found_else = false; + bool trunc_flag = false; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$:"; + string dataExclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$:()."; // avoid double count of casts as data and executable lines (e.g. set { m_uiValue = (uint)value; } + + unsigned int cnt = 0; + + string tmp = CUtil::TrimString( strLSLOC ); + if ( tmp.size() > 0 ) + { + // do, try + found_do = ( CUtil::FindKeyword(tmp, "do") != string::npos ); + found_try = ( CUtil::FindKeyword(tmp, "try") != string::npos ); + // else is treated differently, else is included in SLOC, do and try are not + found_else = ( CUtil::FindKeyword(tmp, "else") != string::npos ); + } + + // there may be more than 1 logical SLOC in this line + while ( i < line.length() ) + { + switch ( line[i] ) + { + case ';': case '{': // LSLOC terminators + // ';' for normal executable or declaration statement + // '{' for starting a function or 'do' stmt or a block (which is counted) + // get the previous logical mark until i-1 index is the new LSLOC + // except 'do' precedes '{' + // except '}' precedes ';' ?? + // do nothing inside 'for' statement + if ( found_for == true && paren_cnt > 0 && line[i] == ';' ) + break; + + // record open bracket for nested loop processing + if ( print_cmplx ) + { + if ( line[i] == '{' ) + { + openBrackets++; + if ( (unsigned int)loopLevel.size() < openBrackets ) + loopLevel.push_back(""); + } + else + { + if ( (unsigned int)loopLevel.size() > openBrackets && openBrackets > 0 ) + loopLevel.pop_back(); + } + } + + // case 'while(...);', 'while(...) {', and '} while(...);' + // this case is handled in case ')' + if (found_while && found_forifwhile) + { + found_while = false; + found_forifwhile = false; + start = i + 1; + break; + } + + if ( line[i] == '{' ) + { + if (prev_char == '=') + inArrayDec = true; + + // continue until seeing ';' + if ( inArrayDec ) + break; + + // case for(...); and if (...) { + // these specials are handled + if ( found_forifwhile ) + { + found_forifwhile = false; + start = i + 1; + break; + } + + // check if 'do' precedes '{' + if ( !found_do && !found_try && !found_else ) + { + // find for 'do' in string before tmp string + tmp = CUtil::TrimString(line.substr(start, i - start)); + found_do = (tmp == "do"); // found 'do' statement + found_try = (tmp == "try"); // found 'try' statement + // same as else + found_else = (tmp == "else"); // found 'else' statement + } + if (found_do || found_try || found_else) + { + if (found_do && print_cmplx) + { + if (loopLevel.size() > 0) loopLevel.pop_back(); + loopLevel.push_back("do"); + } + found_do = false; + found_try = false; + if (!found_else) + { + // everything before 'do', 'try' are cleared + strLSLOC = ""; + strLSLOCBak = ""; + start = i + 1; + } + break; // do not store '{' following 'do' + } + } + + // wrong, e.g., a[]={1,2,3}; + if (line[i] == ';' && prev_char == '}') + { + // check if in array declaration or not + // if no, skip, otherwise, complete the SLOC containing array declaration + if (!inArrayDec) + { + start = i + 1; + break; + } + } + + inArrayDec = false; + + // check for empty statement (=1 LSLOC) + if (CUtil::TrimString(line.substr(start, i + 1 - start)) == ";" && strLSLOC.length() < 1) + { + strLSLOC = ";"; + strLSLOCBak = ";"; + } + else + { + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + } + if (result->addSLOC(strLSLOCBak, trunc_flag)) + { + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, dataExclude, "", "", &result->data_name_count); + + temp_lines++; + if (data_continue == true && line[i] == ';') + { + result->data_lines[LOG]++; + phys_data_lines = temp_lines; + } + else + { + if (cnt > 0 && line[i] == ';') + { + result->data_lines[LOG]++; + phys_data_lines = temp_lines; + } + else + { + result->exec_lines[LOG]++; + phys_exec_lines = temp_lines; + } + } + } + else if (data_continue == true && line[i] == ';') + phys_data_lines = temp_lines; + else + phys_exec_lines = temp_lines; + data_continue = false; + temp_lines = 0; + strLSLOC = strLSLOCBak = ""; + start = i + 1; + + // reset some flagging parameters + forflag = false; + paren_cnt = 0; + found_while = false; + found_forifwhile = false; + found_for = false; + + break; + case '(': + if (forflag) + paren_cnt++; + else + { + // handle 'for', 'while', 'if' the same way + tmp = CUtil::TrimString(line.substr(start, i)); + if (CUtil::FindKeyword(tmp, "for") != string::npos + // || CUtil::FindKeyword(tmp, "foreach") != string::npos foreach NOT in Scala + || CUtil::FindKeyword(tmp, "while") != string::npos + || CUtil::FindKeyword(tmp, "if") != string::npos) + { + forflag = true; + paren_cnt++; + + if (print_cmplx && (unsigned int)loopLevel.size() > openBrackets && openBrackets > 0) + loopLevel.pop_back(); + + if (CUtil::FindKeyword(tmp, "for") != string::npos) + { + if (print_cmplx) + loopLevel.push_back("for"); + found_for = true; + } + else if (CUtil::FindKeyword(tmp, "while") != string::npos) + { + if (print_cmplx) + loopLevel.push_back("while"); + found_while = true; + } + //else if (print_cmplx && CUtil::FindKeyword(tmp, "foreach") != string::npos) + // loopLevel.push_back("foreach"); + + // record nested loop level + if (print_cmplx) + { + if (CUtil::FindKeyword(tmp, "if") == string::npos) + { + unsigned int loopCnt = 0; + for (StringVector::iterator lit = loopLevel.begin(); lit < loopLevel.end(); lit++) + { + if ((*lit) != "") + loopCnt++; + } + if ((unsigned int)result->cmplx_nestloop_count.size() < loopCnt) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopCnt-1]++; + } + } + } + } + break; + case ')': + if (forflag) + { + if (paren_cnt > 0) + paren_cnt--; + if (paren_cnt == 0) + { + // handle 'for', 'while', 'if' + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + if (result->addSLOC(strLSLOCBak, trunc_flag)) + result->exec_lines[LOG]++; + strLSLOCBak = strLSLOC = ""; + phys_exec_lines = temp_lines; + temp_lines = 0; + start = i + 1; + found_forifwhile = true; + forflag = false; + found_for = false; + } + } + break; + case '}': + // skip '}' when found ';' and then '}' because '{' is counted already + // also, {} is also skipped, counted + if (prev_char == ';' || prev_char == '{' || prev_char == '}') + if (!inArrayDec) start = i + 1; + + // record close bracket for nested loop processing + if (print_cmplx) + { + if (openBrackets > 0) + openBrackets--; + if (loopLevel.size() > 0) + loopLevel.pop_back(); + } + break; + } + + if (line[i] != ' ' && line[i] != '\t') + { + // if ;}}} --> don't count }}} at all + // also, if {}}} --> don't count }}} at all + // if ( !(line[i] == '}' && (prev_char == ';' || prev_char == '{'))) // see case '}' above + prev_char = line[i]; + + // change to not found if a char appears before + if (line[i] != ')' && found_forifwhile) + found_forifwhile = false; + } + i++; + } + + tmp = CUtil::TrimString(line.substr(start, i - start)); + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + tmp = CUtil::TrimString(lineBak.substr(start, i - start)); + strLSLOCBak += tmp.substr(0, strSize); + + // drop continuation symbol NOT USED FOR SCALA + //if (strLSLOC[strLSLOC.length()-1] == '\\') + //{ + // strLSLOC = strLSLOC.substr(0, strLSLOC.length()-1); + // strLSLOCBak = strLSLOCBak.substr(0, strLSLOCBak.length()-1); + //} + } + + // make sure that we are not beginning to process a new data line + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", NULL); + + if (cnt > 0) + data_continue = true; + if (data_continue) + temp_lines++; + if (temp_lines == 0 && phys_data_lines == 0 && phys_exec_lines == 0) + phys_exec_lines = 1; +} + +/*! +* Parses lines for function/method names. +* +* \param line line to be processed +* \param lastline last line processed +* \param functionStack stack of functions +* \param functionName function name found +* \param functionCount function count found +* +* \return 1 if function name is found +*/ +int CScalaCounter::ParseFunctionName(const string &line, string &lastline, + filemap &functionStack, string &functionName, unsigned int &functionCount) +{ + string tline, str; + size_t idx, tidx, cnt, cnt2; + unsigned int fcnt, cyclomatic_cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + tline = CUtil::TrimString(line); + idx = tline.find('{'); + if (idx != string::npos) + { + // check whether it is at first index, if yes then function name is at above line + if (idx == 0) + { + lineElement element(++functionCount, lastline); + functionStack.push_back(element); + lastline.erase(); + } + else + { + str = tline.substr(0, idx); + tidx = cnt = cnt2 = 0; + if (str[0] != '(' && str[0] != ':' && (lastline.length() < 1 || lastline[lastline.length() - 1] != ':')) + { + while (tidx != string::npos) + { + tidx = str.find('(', tidx); + if (tidx != string::npos) + { + cnt++; + tidx++; + } + } + if (cnt > 0) + { + tidx = 0; + while (tidx != string::npos) + { + tidx = str.find(')', tidx); + if (tidx != string::npos) + { + cnt2++; + tidx++; + } + } + } + } + // make sure parentheses are closed and no parent class listed + if ((cnt > 0 && cnt == cnt2) || (lastline.length() > 0 && lastline[lastline.length() - 1] == ';')) + lastline = str; + else + lastline += " " + str; + lineElement element(++functionCount, CUtil::TrimString(lastline)); + functionStack.push_back(element); + lastline.erase(); + } + } + else if (tline.length() > 0 && tline[tline.length() - 1] != ';' && + lastline.length() > 0 && lastline[lastline.length() - 1] != ';') + { + // append until all parentheses are closed + tidx = lastline.find('('); + if (tidx != string::npos) + { + cnt = 1; + while (tidx != string::npos) + { + tidx = lastline.find('(', tidx + 1); + if (tidx != string::npos) + cnt++; + } + tidx = lastline.find(')'); + while (tidx != string::npos) + { + cnt++; + tidx = lastline.find(')', tidx + 1); + } + if (cnt % 2 != 0) + lastline += " " + tline; + else + lastline = tline; + } + else + lastline = tline; + } + else + lastline = tline; + + idx = line.find('}'); + if (idx != string::npos && !functionStack.empty()) + { + str = functionStack.back().line; + fcnt = functionStack.back().lineNumber; + functionStack.pop_back(); + idx = str.find('('); + + if (idx != string::npos) + { + // search for cyclomatic complexity keywords and other possible keywords + CUtil::CountTally(str, cmplx_cyclomatic_list, cyclomatic_cnt, 1, exclude, "", "", 0, casesensitive); + if (cyclomatic_cnt <= 0 && CUtil::FindKeyword(str, "match") == string::npos && + CUtil::FindKeyword(str, "try") == string::npos && CUtil::FindKeyword(str, "finally") == string::npos && + CUtil::FindKeyword(str, "return") == string::npos && str.find('=') == string::npos) + { + string myFunctionName = CUtil::ClearRedundantSpaces(str.substr(0, idx)); + + #ifdef _DEBUG + if ( myFunctionName.size() == 0 ) + { + // PrintCyclomaticComplexity will now skip any empty Function Names. + // Done this way as is easier and less Risk of adding Defects from complex Cyclomatic Complexity code + // TODO: Go through the CC code and the ParseFunctionName code and try to SAFELY improve. + // cout << endl << "CScalaCounter::ParseFunctionName() Empty function name allowed" << endl; + } + #endif + // May have the actual arg list so look for first ( and do not copy + size_t myIdx = myFunctionName.find( '(' ); + if ( myIdx != string::npos ) + myFunctionName = myFunctionName.substr( 0, myIdx ); + functionName = myFunctionName; + functionCount = fcnt; + lastline.erase(); + return 1; + } + } + lastline.erase(); + } + return 0; +} + +/*! +* How this Scala language parse module was developed: +* +* Goals: +* Support parsing of Scala language as it currently exists. +* Generate as accurate a count of Logical Source Lines Of Code LSLOC as possible (1). +* Provide for fairly easy changes in the future to support other Scala versions (2). +* +* Problem Space exploration: +* (1) The Scala language makes use of implicit construction of statements. +* There is no Scala line continuation character as in C/C++ or Java (those use \ ) +* Although a semicolon character (;) may be used to terminate a Scala statement +* the semicolon is OPTIONAL and NOT the recommended style. +* About the only time the semicolon appears is when +* more than 1 Scala statement appears on the same Physical line in the file. +* So correctly parsing Scala Logical lines (a complete statement) is less +* deterministic than for C/C++ or Java. In this respect Scala resembles Ruby. +* +* (2) There is an offical document covering all Scala syntax. +* EBNF representation can be found @ scala.org +* It seems the Scala compiler is written in Scala (about 30,000+ Lines Of Code). +* (2a) There are also other creators of Scala based language versions (not TypeSafe) +* or creators of other similar languages that started from Scala syntax. +* We might expect that there would be a fairly large overlap of keywords, +* implied meanings, and other language features. +* In other words there may be more similarities than differences. +* +* (3) Other open source language parsing tools that may help here: +* Doxygen +* also parses various source languages but the internals are different: +* It uses a C style front end parser, C Flex parse engine and rules, builds an +* Abstract Syntax Tree representation and then several different output options. +* I believe Doxygen has a better approach but there may be slower performance with +* an AST building overhead (but also potentially better capture of metrics as well). +* LEX or related tools +* Even with EBNF grammer not sure how to proceed here. +* Tried COCO /R +* Got stuck trying finish (maybe someone else can succeed) +* Looked then at PEG representation because there is direct support in Boost for PEG. +* I think going forward this makes the most sense for C++ approach. +* +* Solution Space (near term): +* With the UCC 2015.12 release (on which these changes are based) the C/C++ Java parse +* support included the most details of complexity metrics. So it made some sense to try +* to fit in with that approach. Another reason to use this approach is that Scala uses +* the same // notation for single line or trailing comments and same for multiline. +* +* Please see Informal Analysis of Parse Code below for the new Alternate approach. +* +* Solution Space (after near term): +* To better support (2 and 2a) it would be nice if the code were changed to: +* Set the default values used for parsing a language as is now done; +* (fixed strings and flag settings directly embedded in the code) and add code +* before parsing starts that reads OPTIONAL file(s) to change some or all of the +* detailed strings/flags. Initial implementation wanted was to add XML parsing +* but there is no support for XML parsing in the standard library. So a simple +* text file format seems to be the best approach. +* +* Allowing user editable files to set detailed parse values: +* Pros +* Users can just edit a file to support new versions +* or alternate language forms (in some cases). +* It may be possible for several new language parsers to be added with +* a very small effort. (maybe Groovy, Rust, ...) +* Cons +* The user may try to apply one or more detailed setting(s) +* that prevent generation of valid results. +* +* User may then contact support for UCC to get help! +* The Help feature can be extended to cover this situation. +* Could give example problem and solution and also refer to +* new -help_lang Scala (or other language) help feature. +* +* Because the default values are embedded in the code +* a small enhancement to the Help feature: +* -help_lang Scala (for exmaple) or +* -help_lang All (for all embedded languages supported) +* may output all the built in default settings for a language in a +* text file with the same format as used for reading those settings. +* So user has working example(s) of given language(s) parse settings. +* +* Design Decision: User editable files to set parse values? Yes! +* So I believe that the Pros outweigh the Cons. (Design tradeoff) +* Note that this approach may also help the Doxygen project as well +* (but that is off topic here :) +* +* Solution Space (mid term): +* (A) Extending Complexity metrics (collection & reporting): +* Current set of metrics seem to be based only on Procedural code. +* Object Oriented developers may want to know: +* What is the class inheritance depth? (nesting) +* How many methods are in a given class? (over 20 seems to be getting messy) +* and so on. +* Functional developers may want to know: +* What are the higher order functions used? +* How complex are the Lambda expressions used? +* and so on. +* Data structure and useage details: +* What is the structure/union complexity? (depth of nesting, number of elements, ...) +* How are various known data structures used? Arrays, Dictionaries, Lists, Maps, ... +* and so on. +* Visibility or Access to Code and/or Data: (types of Encapsulation and a measure of how much) +* Namespaces +* Public +* Protected +* Private +* Friend +* +* Add args to enable/disable: OO, Functional, Data structure metrics. +* So original performance without this collection would be as before. +* Note: Data structure metrics would require new code as currently +* (for example in C/C++) do not even parse .h files +* +* (B) Performance improvements: (Modified: 2015.12 Threads and others DONE) +* Minor (maybe lots of places in the code): +* There may be several instances of temporary object creation/destruction +* that is not really needed. Suggest using AMD CodeAnalyst or similar profiler. +* +* Major: +* Changed to use multiple threads. (done: see UCCThread) +* Gathering performance data running an optimized Release build shows +* the CPU and the Disk subsystems have margin for faster results. +* Added a command line arg to set the number of extra threads to create. +* Threads allow faster Reading/Counting just after Read and for Diff +* but not Duplicate detection due to how existing code works. +* Boost Thread library worked fine along with a Semaphore library. +* +* ================================================================ +* Informal Analysis of Parse Code +* +* Existing Parsing Approach: +* ------------------------- +* Searching for Directive (preprocessor) keywords (for example) +* (in a looping Linear way) +* For each Physical line in a file +* For each Directive keyword +* See if the keyword is within the line +* If found stop, else loop (possible Exhaustive Search) +* +* There are other similar loops for various other types of keywords +* +* So each Physical line in a file is parsed through more than once. +* +* In addition there are some loops where every character in the line +* is checked for special meanings {} () [] ; \ // and so on. +* +* Consider from a Searching algorithms point of view: +* this can approximate a form of an almost Exhaustive Search +* (or at least Linear Searches done more than once)! +* +* Alternate Parsing Approach: +* -------------------------- +* Ideally each Physical line in a file should be parsed just once. +* Instead of multiple passes to maybe find special keywords/symbols +* the code should just work through the Physical line once. +* +* How to implement this new single pass parse approach? +* For each Physical line in a file: +* Code is needed to get parts of a given Physical line so that: +* An API like strtok would be needed (using whitespace as delimiter?) +* Then for each piece of text found: +* Try to pick it apart based on various special characters. +* When no more smaller pieces can be found, +* Search a data structure for matches of a small piece. +* Based on the match found, set various flags, counters, etc. +* Keep going until no more pieces of the Physical line remain. +* Loop back to parse another Physical line. +* +* What Data structure(s) are useful to support the single pass? +* +* How about an Array of all the keywords and all the special symbols? +* The keyword/symbol Array would be sorted so a Binary search may be used. +* +* If the keyword/symbol Array has a match, then the Type of the match is needed. +* Consider a Type array with a 1 to 1 correspondence to the keyword array. +* The Type array would give guidance on how to proceed with parsing. +* Types may be: Math function(s), Data declarations, Directives (preprocessor), etc. +* +* +* If having 2 related Arrays seems to be awkward how about a Dictionary approach? +* +* Either way it seems feasible to only need 1 pass through the Physical lines. +* +* It is difficult to guesstimate but I would say a 2+ times speed up is likely. +* +* Informal Comparison of Existing and Alternate: +* --------------------------------------------- +* Existing approach is a nice modular approach where the various +* parse needs, blank line detection, handling embedded or multi-line +* comments, searching for different types of keywords, dealing with +* looping and nested code constructs have been separated out. +* +* It may be said that the Existing approach is like a Structured +* Programming approach. +* +* Alternate approach takes small text pieces that were isolated +* and then looks for them in a sorted Array (or Dictionary). +* +* If we consider the small text pieces as low level 'data' about +* the parsed code, then the Alternate is a Data Driven approach. +* +* Motivation for Alternate Parsing Approach: +* ----------------------------------------- +* OK, I admit like many other developers I can be basically lazy. +* So when doing some fairly mindless debugging to see details of +* HOW the Existing code works, I kept thinking about the strengths +* and weaknesses in use. +* +* So I could spend more time making incremental changes to a parse +* approach that I knew was correct for other languages but may not +* be a good approach for Scala (with implicit statement termination). +* +* Also I had prior experience working on a 4GL (natural) language +* with the Alternate approach of cached Dictionary lookups. +* +* So since I am contributing this Scala support on my own time, +* it makes sense to me to try out the Alternate way. +*/ diff --git a/src/CScalaCounter.h b/src/CScalaCounter.h new file mode 100644 index 0000000..e88b8b4 --- /dev/null +++ b/src/CScalaCounter.h @@ -0,0 +1,51 @@ +//! Code counter class definition for the Scala language. +/*! +* \file CScalaCounter.h +* +* This file contains the code counter class definition for the Scala language. +*/ + +#ifndef CScalaCounter_h +#define CScalaCounter_h + +#include "CCJavaCsScalaCounter.h" + +//! Scala code counter class. +/*! +* \class CScalaCounter +* +* Defines the Scala code counter class. +*/ + +// Inherit from CCJavaCsScalaCounter base class to get +// method/data interfaces to more extensive complexity support +class CScalaCounter : public CCJavaCsScalaCounter +{ +public: + // Set the language so base class constructors set values as needed + CScalaCounter(string lang = "SCALA"); + +protected: + virtual int CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapmBak = NULL); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapmBak = NULL); + void LSLOC(results* result, string line, string lineBak, string &strLSLOC, string &strLSLOCBak, unsigned int &paren_cnt, + bool &forflag, bool &found_forifwhile, bool &found_while, char &prev_char, bool &data_continue, + unsigned int &temp_lines, unsigned int &phys_exec_lines, unsigned int &phys_data_lines, + bool &inArrayDec, bool &found_for, unsigned int &openBrackets, StringVector &loopLevel); + virtual int ParseFunctionName(const string &line, string &lastline, + filemap &functionStack, string &functionName, unsigned int &functionCount); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CScalaCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CScalaCounter(const CScalaCounter& rhs); + + // Take care of warning C4626: 'CScalaCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CScalaCounter operator=(const CScalaCounter); +}; + +#endif diff --git a/src/CSqlCounter.cpp b/src/CSqlCounter.cpp new file mode 100644 index 0000000..c028324 --- /dev/null +++ b/src/CSqlCounter.cpp @@ -0,0 +1,989 @@ +//! Code counter class methods for the SQL language. +/*! +* \file CSqlCounter.cpp +* +* This file contains the code counter class methods for the SQL language. +*/ + +#include "CSqlCounter.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*! +* Constructs a CSqlCounter object. +*/ +CSqlCounter::CSqlCounter() +{ + classtype = SQL; + language_name = "SQL"; + casesensitive = false; + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("SQL", file_extension); + //file_extension.push_back(".sql"); + + QuoteStart = "\"'"; + QuoteEnd = "\"'"; + LineCommentStart.push_back("--"); + + BlockCommentStart.push_back("/*"); + BlockCommentEnd.push_back("*/"); + + data_name_list.push_back("bigint"); + data_name_list.push_back("binary"); + data_name_list.push_back("bit"); + data_name_list.push_back("blob"); + data_name_list.push_back("boolean"); + data_name_list.push_back("byte"); + data_name_list.push_back("char"); + data_name_list.push_back("character"); + data_name_list.push_back("date"); + data_name_list.push_back("datetime"); + data_name_list.push_back("decimal"); + data_name_list.push_back("double"); + data_name_list.push_back("enum"); + data_name_list.push_back("float"); + data_name_list.push_back("image"); + data_name_list.push_back("int"); + data_name_list.push_back("integer"); + data_name_list.push_back("interval"); + data_name_list.push_back("long"); + data_name_list.push_back("longblob"); + data_name_list.push_back("longtext"); + data_name_list.push_back("mediumblob"); + data_name_list.push_back("mediumint"); + data_name_list.push_back("mediumtext"); + data_name_list.push_back("memo"); + data_name_list.push_back("money"); + data_name_list.push_back("nchar"); + data_name_list.push_back("ntext"); + data_name_list.push_back("nvarchar"); + data_name_list.push_back("numeric"); + data_name_list.push_back("real"); + data_name_list.push_back("single"); + data_name_list.push_back("smalldatetime"); + data_name_list.push_back("smallint"); + data_name_list.push_back("smallmoney"); + data_name_list.push_back("text"); + data_name_list.push_back("time"); + data_name_list.push_back("timestamp"); + data_name_list.push_back("tinyint"); + data_name_list.push_back("tinytext"); + data_name_list.push_back("uniqueidentifier"); + data_name_list.push_back("varbinary"); + data_name_list.push_back("varchar"); + data_name_list.push_back("year"); + //add by KK @ 11/6 + data_name_list.push_back("number"); + data_name_list.push_back("cursor"); + data_name_list.push_back("exception"); + + + exec_name_list.push_back("alter"); + exec_name_list.push_back("close"); + exec_name_list.push_back("comment"); + exec_name_list.push_back("commit"); + exec_name_list.push_back("create"); + exec_name_list.push_back("declare"); + exec_name_list.push_back("delete"); + exec_name_list.push_back("deny"); + exec_name_list.push_back("drop"); + exec_name_list.push_back("except"); + exec_name_list.push_back("fetch"); + exec_name_list.push_back("grant"); + exec_name_list.push_back("group by"); + exec_name_list.push_back("having"); + exec_name_list.push_back("insert"); + exec_name_list.push_back("intersect"); + exec_name_list.push_back("join"); + exec_name_list.push_back("limit"); + exec_name_list.push_back("order by"); + exec_name_list.push_back("rename"); + exec_name_list.push_back("replace"); + exec_name_list.push_back("revoke"); + exec_name_list.push_back("rollback"); + exec_name_list.push_back("select"); + exec_name_list.push_back("set"); + exec_name_list.push_back("truncate"); + exec_name_list.push_back("union"); + exec_name_list.push_back("update"); + exec_name_list.push_back("where"); + // For PL/SQL + string exec_words[] = {"CHECK", "CONNECT", "CONTINUE", "ELSIF", "EXIT", "FOR", "GOTO", "IF", "LOCK", "LOOP", "MINUS", "RAISE", "RETURN", "START", "UNIQUE", "USE", "VIEW", "WHEN", "WHILE", "dbms_output.put_line"}; + for (unsigned int i = 0; i < sizeof(exec_words)/sizeof(exec_words[0]); i++) { + exec_name_list.push_back(exec_words[i] ); + } + + math_func_list.push_back("abs"); + math_func_list.push_back("avg"); + math_func_list.push_back("ceil"); + math_func_list.push_back("count"); + math_func_list.push_back("exp"); + math_func_list.push_back("floor"); + math_func_list.push_back("max"); + math_func_list.push_back("min"); + math_func_list.push_back("mod"); + math_func_list.push_back("power"); + math_func_list.push_back("round"); + math_func_list.push_back("sign"); + math_func_list.push_back("sqrt"); + math_func_list.push_back("stddev"); + math_func_list.push_back("sum"); + math_func_list.push_back("trunc"); + math_func_list.push_back("variance"); + + trig_func_list.push_back("acos"); + trig_func_list.push_back("acosh"); + trig_func_list.push_back("asin"); + trig_func_list.push_back("asinh"); + trig_func_list.push_back("atan"); + trig_func_list.push_back("atan2"); + trig_func_list.push_back("atanh"); + trig_func_list.push_back("cos"); + trig_func_list.push_back("cosh"); + trig_func_list.push_back("sin"); + trig_func_list.push_back("sinh"); + trig_func_list.push_back("tan"); + trig_func_list.push_back("tanh"); + + log_func_list.push_back("ln"); + log_func_list.push_back("log"); + + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("/"); + + cmplx_cond_list.push_back("except"); + cmplx_cond_list.push_back("group by"); + cmplx_cond_list.push_back("having"); + cmplx_cond_list.push_back("intersect"); + cmplx_cond_list.push_back("join"); + cmplx_cond_list.push_back("limit"); + cmplx_cond_list.push_back("order by"); + cmplx_cond_list.push_back("union"); + cmplx_cond_list.push_back("where"); + // For PL/SQL + cmplx_cond_list.push_back("when"); + cmplx_cond_list.push_back("else"); + cmplx_cond_list.push_back("elsif");// Because pl/sql has an "end if" to mark the end of if the if in "end if" is getting counted. How to handle that. + cmplx_cond_list.push_back("if"); + cmplx_cond_list.push_back("for"); // add for for pl/sql @ 10/30/14 + //add @ 10/23/14 + cmplx_cond_list.push_back("EXISTS"); + cmplx_cond_list.push_back("DISTINCT"); + cmplx_cond_list.push_back("ASC"); + cmplx_cond_list.push_back("DESC"); + + cmplx_logic_list.push_back("="); + cmplx_logic_list.push_back("!="); + cmplx_logic_list.push_back("<>"); + cmplx_logic_list.push_back(">"); + cmplx_logic_list.push_back("<"); + cmplx_logic_list.push_back(">="); + cmplx_logic_list.push_back("<="); + cmplx_logic_list.push_back("and"); + cmplx_logic_list.push_back("or"); + cmplx_logic_list.push_back("not"); + cmplx_logic_list.push_back("like"); + + cmplx_preproc_list.push_back("dictionary"); + + //cmplx_assign_list.push_back("="); + // For PL/SQL + cmplx_assign_list.push_back(":="); + //cmplx_assign_list.push_back("IS"); + + + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("elsif"); + cmplx_cyclomatic_list.push_back("when"); + cmplx_cyclomatic_list.push_back("while"); + cmplx_cyclomatic_list.push_back("for"); + + cmplx_cyclomatic_logic_list.push_back("or"); + cmplx_cyclomatic_logic_list.push_back("and"); + + cmplx_cyclomatic_case_list.push_back("when"); + cmplx_cyclomatic_switch_list.push_back("case"); + + ignore_cmplx_cyclomatic_list.push_back("end if"); + ignore_cmplx_cyclomatic_list.push_back("end case"); +} + +/*! +* Replaces quoted strings inside a string starting at idx_start with '$'. +* Handles special cases for SQL literal strings. +* +* \param strline string to be processed +* \param idx_start index of line character to start search +* \param contd specifies the quote string is continued from the previous line +* \param CurrentQuoteEnd end quote character of the current status +* +* \return method status +*/ +int CSqlCounter::ReplaceQuote(string &strline, size_t &idx_start, bool &contd, char &CurrentQuoteEnd) +{ + size_t idx = string::npos; + bool done = false; + while ( ! done ) + { + idx = strline.find("''", idx_start); + if (idx != string::npos) + strline.replace(idx, 2, 2, '$'); + else + break; + } + while ( ! done ) + { + idx = strline.find("\"\"", idx_start); + if (idx != string::npos) + strline.replace(idx, 2, 2, '$'); + else + break; + } + return CCodeCounter::ReplaceQuote(strline, idx_start, contd, CurrentQuoteEnd); +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CSqlCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + size_t i, j, k, ind, nextInd; + unsigned int sloc_count, dsloc_count, lineNum; + string stmt, stmtBak, exec_keyword, data_keyword; + filemap::iterator fit, fitbak; + + unsigned int data_count = 0; + unsigned int exec_count = 0; + bool trunc_flag = false; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string line = ""; + string lineBak = ""; + bool data_continue = false; + string strLSLOC = ""; + string strLSLOCBak = ""; + size_t lineNumber = 0; + + vector nestedSql, nestedSqlBak; + stack pistack, ppstack; + size_t mapi = 0, pi, pp; + + // process physical SLOC and capture embedded SLOC + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + // insert blank at the beginning(for searching keywords) + line = ' ' + fit->line; + lineBak = ' ' + fitbak->line; + lineNumber = fit->lineNumber; + + if (CUtil::CheckBlank(line)) + { + mapi++; + continue; + } + + // check physical lines + exec_count = 0; + CUtil::CountTally(line, exec_name_list, exec_count, 1, exclude, "", "", &result->exec_name_count, false); + data_count = 0; + CUtil::CountTally(line, data_name_list, data_count, 1, exclude, "", "", &result->data_name_count, false); + if (exec_count > 0 || (data_count <= 0 && !data_continue)) + { + data_continue = false; + result->exec_lines[PHY]++; + } + else + { + data_continue = true; + result->data_lines[PHY]++; + } + + for (i = 1; i < line.length(); i++) + { + if (line[i] == '(') + { + pistack.push(mapi); + ppstack.push(i - 1); + } + else if (line[i] == ')' && pistack.size() > 0) + { + // capture embedded SLOC + pi = pistack.top(); + pp = ppstack.top(); + pistack.pop(); + ppstack.pop(); + if (pi == mapi) + { + stmt = fit->line.substr(pp, i - pp); + stmtBak = fitbak->line.substr(pp, i - pp); + } + else + { + stmt = fmap->at(pi).line.substr(pp); + stmtBak = fmapBak->at(pi).line.substr(pp); + for (j = pi + 1; j < mapi; j++) + { + stmt += ' ' + fmap->at(j).line; + stmtBak += ' ' + fmapBak->at(j).line; + } + stmt += ' ' + fit->line.substr(0, i); + stmtBak += ' ' + fitbak->line.substr(0, i); + } + + // check for executable statement keywords + sloc_count = 0; + dsloc_count = 0; + CUtil::CountTally(stmt, exec_name_list, sloc_count, 1, exclude, "", "", NULL, false); + if (sloc_count > 0) + { + // extract embedded SLOC from main content + if (pi == mapi) + { + fit->line.erase(pp, i - pp); + fitbak->line.erase(pp, i - pp); + line.erase(pp + 1, i - pp - 1); + lineBak.erase(pp + 1, i - pp - 1); + i = pp + 1; + } + else + { + fmap->at(pi).line.erase(pp); + fmapBak->at(pi).line.erase(pp); + for (j = pi + 1; j < mapi; j++) + { + fmap->at(j).line.clear(); + fmapBak->at(j).line.clear(); + } + fit->line.erase(0, i); + fitbak->line.erase(0, i); + line.erase(1, i - 1); + lineBak.erase(1, i - 1); + i = 1; + } + stmt = CUtil::TrimString(stmt.substr(1, stmt.length() - 2)); + stmtBak = CUtil::TrimString(stmtBak.substr(1, stmtBak.length() - 2)); + nestedSql.push_back(stmt); + nestedSqlBak.push_back(stmtBak); + } + else + { + CUtil::CountTally(stmt, data_name_list, dsloc_count, 1, exclude, "", "", NULL, false); + if (dsloc_count > 0) + { + // mark data keywords (not counted as LSLOC) + for (j = 0; j < data_name_list.size(); j++) + { + ind = 0; + nextInd = 0; + data_keyword = data_name_list.at(j); + while (ind != string::npos) + { + ind = CUtil::FindKeyword(stmt, data_keyword, nextInd, TO_END_OF_STRING, false); + if (ind != string::npos) + { + stmt.replace(ind, 1, "$"); + nextInd = ind + 1; + } + } + ind = pp; + nextInd = pp; + if (pi == mapi) + { + while (ind != string::npos) + { + ind = CUtil::FindKeyword(fit->line, data_keyword, nextInd, i - 1, false); + if (ind != string::npos) + { + fit->line.replace(ind, 1, "$"); + nextInd = ind + 1; + } + } + } + else + { + while (ind != string::npos) + { + ind = CUtil::FindKeyword(fmap->at(pi).line, data_keyword, nextInd, TO_END_OF_STRING, false); + if (ind != string::npos) + { + fmap->at(pi).line.replace(ind, 1, "$"); + nextInd = ind + 1; + } + } + for (k = pi + 1; k < mapi; k++) + { + ind = 0; + nextInd = 0; + while (ind != string::npos) + { + ind = CUtil::FindKeyword(fmap->at(k).line, data_keyword, nextInd, TO_END_OF_STRING, false); + if (ind != string::npos) + { + fmap->at(k).line.replace(ind, 1, "$"); + nextInd = ind + 1; + } + } + } + ind = 0; + nextInd = 0; + while (ind != string::npos) + { + ind = CUtil::FindKeyword(fit->line, data_keyword, nextInd, i - 1, false); + if (ind != string::npos) + { + fit->line.replace(ind, 1, "$"); + nextInd = ind + 1; + } + } + } + } + } + } + } + } + mapi++; + } + data_continue = false; + + // add embedded SLOC to file maps + if (nestedSql.size() > 0) + { + lineNum = fmap->back().lineNumber; + for (i = 0; i < nestedSql.size(); i++) + { + lineNum++; + lineElement element(lineNum, nestedSql.at(i)); + fmap->push_back(element); + lineElement elementBak(lineNum, nestedSqlBak.at(i)); + fmapBak->push_back(elementBak); + } + nestedSql.clear(); + nestedSqlBak.clear(); + } + + // process logical SLOC + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + // insert blank at the beginning(for searching keywords) + line = ' ' + fit->line; + lineBak = ' ' + fitbak->line; + + if (CUtil::CheckBlank(line)) + continue; + lineNumber = fit->lineNumber; + // process logical SLOC + LSLOC(result, line, lineNumber, lineBak, strLSLOC, strLSLOCBak, data_continue); + } + if (strLSLOC.length() > 0) + { + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + { + if (data_continue) + result->data_lines[LOG]++; + else + result->exec_lines[LOG]++; + } + strLSLOC = strLSLOCBak = ""; + data_continue = false; + } + if (classtype == UNKNOWN || classtype == DATAFILE) + return 0; + // Cyclomatic complexity analysis for PL/SQL constructs starts + size_t idx; + string file_ext,function_name = "";; + unsigned int cyclomatic_cnt = 0, ignore_cyclomatic_cnt = 0, main_cyclomatic_cnt = 0, function_count = 0, cyclomatic_logic_cnt = 0, main_cyclomatic_logic_cnt = 1, cyclomatic_case_cnt = 0, main_cyclomatic_case_cnt = 1, cyclomatic_default_cnt = 0, cyclomatic_switch_cnt = 0; + filemap function_stack; + stack cyclomatic_stack; + map function_map; + map logical_map; + map case_map; + bool process_cyclomatic_complexity = false; + // check whether to process cyclomatic complexity + if (cmplx_cyclomatic_list.size() > 0) + { + process_cyclomatic_complexity = true; + if (skip_cmplx_cyclomatic_file_extension_list.size() > 0) + { + idx = result->file_name.find_last_of("."); + if (idx != string::npos) + { + file_ext = result->file_name.substr(idx); + file_ext = CUtil::ToLower(file_ext); + if (find(skip_cmplx_cyclomatic_file_extension_list.begin(), skip_cmplx_cyclomatic_file_extension_list.end(), file_ext) != skip_cmplx_cyclomatic_file_extension_list.end()) + process_cyclomatic_complexity = false; + } + } + } + for (fit = fmap->begin(); fit != fmap->end(); fit++) + { + line = fit->line; + + if (CUtil::CheckBlank(line)) + continue; + + line = " " + line; + + // cyclomatic complexity + if (process_cyclomatic_complexity) + { + // search for cyclomatic complexity keywords + CUtil::CountTally(line, cmplx_cyclomatic_list, cyclomatic_cnt, 1, exclude, "", "", 0, casesensitive); + + CUtil::CountTally(line, cmplx_cyclomatic_switch_list, cyclomatic_switch_cnt, 1, exclude, "", "", 0, casesensitive); + + // search for keywords to exclude + if (ignore_cmplx_cyclomatic_list.size() > 0) + CUtil::CountTally(line, ignore_cmplx_cyclomatic_list, ignore_cyclomatic_cnt, 1, exclude, "", "", 0, casesensitive); + + // search for cyclomatic complexity logical keywords + if (cmplx_cyclomatic_logic_list.size() > 0) + CUtil::CountTally(line, cmplx_cyclomatic_logic_list, cyclomatic_logic_cnt, 1, exclude, "", "", 0, casesensitive); + + // search for cyclomatic complexity case keywords + if (cmplx_cyclomatic_case_list.size() > 0) + CUtil::CountTally(line, cmplx_cyclomatic_case_list, cyclomatic_case_cnt, 1, exclude, "", "", 0, casesensitive); + + // search for cyclomatic complexity case default keywords + if (cmplx_cyclomatic_default_list.size() > 0) + CUtil::CountTally(line, cmplx_cyclomatic_default_list, cyclomatic_default_cnt, 1, exclude, "", "", 0, casesensitive); + + if(cyclomatic_default_cnt > 0) + { + cyclomatic_cnt -= cyclomatic_default_cnt; + cyclomatic_case_cnt -= cyclomatic_default_cnt; + cyclomatic_default_cnt = 0; + } + + if (main_cyclomatic_cnt < 1) + main_cyclomatic_cnt = 1; // add 1 for main function here in case no other decision points are found in main + // PL/SQL code doesn't belong to any function. It is treated as a single main function. + main_cyclomatic_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt; + main_cyclomatic_logic_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt + cyclomatic_logic_cnt; + main_cyclomatic_case_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt - cyclomatic_case_cnt + cyclomatic_switch_cnt; + cyclomatic_cnt = ignore_cyclomatic_cnt = cyclomatic_logic_cnt = cyclomatic_case_cnt = cyclomatic_switch_cnt = 0; + + + } + } + + // done with a file + if (main_cyclomatic_cnt > 0) + { + // add "main" code + lineElement element(main_cyclomatic_cnt, "main"); + lineElement n_element(main_cyclomatic_logic_cnt, "main"); + lineElement c_element(main_cyclomatic_case_cnt, "main"); + function_map[0] = element; + logical_map[0] = n_element; + case_map[0] = c_element; + } + else + { + // finish the first function if not closed + while (!function_stack.empty()) + { + function_name = function_stack.back().line; + function_count = function_stack.back().lineNumber; + function_stack.pop_back(); + + if (!function_stack.empty()) + { + // grab previous function from stack to continue + if (!cyclomatic_stack.empty()) + { + cyclomatic_cnt = cyclomatic_stack.top(); + cyclomatic_stack.pop(); + } + } + else + { + // capture count at end of function + lineElement element(cyclomatic_cnt - ignore_cyclomatic_cnt + 1, function_name); + lineElement n_element(cyclomatic_cnt - ignore_cyclomatic_cnt + cyclomatic_logic_cnt + 1, function_name); + lineElement c_element(cyclomatic_cnt - ignore_cyclomatic_cnt - cyclomatic_case_cnt + cyclomatic_switch_cnt + 1, function_name); + function_map[function_count] = element; + logical_map[function_count] = n_element; + case_map[function_count] = c_element; + } + } + } + + // process ordered functions + for (map::iterator it = function_map.begin(); it != function_map.end(); ++it) + result->cmplx_cycfunct_count.push_back(it->second); + if(cmplx_cyclomatic_logic_list.size() > 0) + { + for (map::iterator it = logical_map.begin(); it != logical_map.end(); ++it) + result->cmplx_cycfunct_CC2_count.push_back(it->second); + } + if(cmplx_cyclomatic_case_list.size() > 0) + { + for (map::iterator it = case_map.begin(); it != case_map.end(); ++it) + result->cmplx_cycfunct_CC3_count.push_back(it->second); + } + return 1; +} + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results +* \param line processed physical line of code +* \param lineBak original physical line of code +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param data_continue continuation of a data declaration line +*/ +void CSqlCounter::LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, bool &data_continue) +{ + size_t i, ind, nextInd, startInd, endInd, strSize; + ptrdiff_t j; + bool trunc_flag = false, found; + string exec_keyword, data_keyword, cmplx_assign_keyword; + list slocIndices, eslocIndices, dslocIndices, gslocIndices; + + /* Modification Fall 2016 + 1> PreProcess the line + 2> Handles the case where "var1:=1" is modified to "var1 := 1" + 3> The change is required so that LSLOC Count is as per UCC documentation + */ + char *cs = (char *)malloc(sizeof(char *) * (line.length() + 1)); + memset(cs, 0, sizeof(char *) * (line.length() + 1)); + for (i=0;i cmplx_assign_positions; + for(std::vector::iterator it = cmplx_assign_list.begin(); it != cmplx_assign_list.end(); it ++) { + i = 0; + while ((i=line.find(*it, i)) != std::string::npos) { + memset(&cs[i], 0, (*it).length()); + cmplx_assign_positions[i] = *it; + i += (*it).length(); + } + } + i = 0; + while (i < len) { + if (cs[i] != 0) { + ss << &cs[i]; + i += strlen(&cs[i]); + } else { + if (cmplx_assign_positions.find(i) != cmplx_assign_positions.end()) + ss << " "<< cmplx_assign_positions[i] <<" "; + i++; + } + } + free(cs); + line = ss.str(); + /* + Modification Fall 2016 Ends + */ + + // find locations of executable keywords + for (i = 0; i < exec_name_list.size(); i++) + { + ind = 0; + nextInd = 0; + exec_keyword = exec_name_list.at(i); + while (ind != string::npos) + { + ind = CUtil::FindKeyword(line, exec_keyword, nextInd, TO_END_OF_STRING, false); + if (ind != string::npos) + { + // check for grant, revoke, deny + if (exec_keyword == "grant" || exec_keyword == "revoke" || exec_keyword == "deny") + { + // ignore GRANT OPTION + if (line.length() <= ind + 12 || CUtil::ToLower(line.substr(ind, 12)) != "grant option") + gslocIndices.push_back(ind); + } + + nextInd = ind + 1; + + //For PL/SQL, when it's "end if", ignore and not count it + if (exec_keyword == "if") + { + if (ind >= 4) + { + string ifStr = CUtil::ToLower(line.substr(ind - 4, 6)); + if (ifStr == "end if") { + continue; + } + } + } + + //For PL/SQL, when it's "end loop", ignore and not count it + if (exec_keyword == "loop") + { + if (ind >= 4) + { + string ifStr = CUtil::ToLower(line.substr(ind - 4, 8)); + if (ifStr == "end loop") { + continue; + } + } + } + + //For PL/SQL, when it's "create or replace", count only once + if (exec_keyword == "create") + { + if (line.length() >= ind + 17) { + string crtStr = CUtil::ToLower(line.substr(ind, 17)); + if (crtStr == "create or replace") { + continue; + } + } + } + + //For PL/SQL, when it's return + data name, ignore and not count it + if (exec_keyword == "return") + { + bool flag = false; + int startIndex = 0; + int endIndex = 0; + for(unsigned int i = ind + 6; i 0) + { + slocIndices.sort(); + slocIndices.unique(); + } + while (gslocIndices.size() > 0) + { + ind = gslocIndices.front() + 1; + gslocIndices.pop_front(); + + // search for ON, TO, FROM until first non-exec keyword found and clear slocIndices in between + // (ideally this check would span multiple lines, but we avoid this for now) + if (ind < line.length()) + { + nextInd = CUtil::FindKeyword(line, "on", ind, TO_END_OF_STRING, false); + if (nextInd == string::npos) + { + nextInd = CUtil::FindKeyword(line, "to", ind, TO_END_OF_STRING, false); + if (nextInd == string::npos) + { + nextInd = CUtil::FindKeyword(line, "from", ind, TO_END_OF_STRING, false); + if (nextInd == string::npos) + nextInd = line.length(); + } + } + // clear any slocIndices between these values + list::iterator it, it2; + for (it = eslocIndices.begin(), it2 = slocIndices.begin(); it != eslocIndices.end(); ++it, ++it2) + { + if (*it >= ind && *it < nextInd) + { + *it = INVALID_POSITION; + *it2 = INVALID_POSITION; + } + } + eslocIndices.remove(INVALID_POSITION); + slocIndices.remove(INVALID_POSITION); + } + } + + // find locations of data keywords + + if (CUtil::FindKeyword(line, ":=", 0, TO_END_OF_STRING, false) == string::npos && CUtil::FindKeyword(line, "RETURN", 0, TO_END_OF_STRING, false) == string::npos) { + for (i = 0; i < data_name_list.size(); i++) + { + ind = 0; + nextInd = 0; + data_keyword = data_name_list.at(i); + while (ind != string::npos) + { + ind = CUtil::FindKeyword(line, data_keyword, nextInd, TO_END_OF_STRING, false); + if (ind != string::npos) + { + // try to get variable name (var name is listed before type in SQL) + nextInd = ind + 1; + found = false; + for ( j = (ptrdiff_t)ind - 1; j >= 0; j-- ) + { + if (line[(unsigned)j] != ' ' && line[(unsigned)j] != '\t') + found = true; + else if (found && (line[(unsigned)j] == ' ' || line[(unsigned)j] == '\t' || line[(unsigned)j] == ',')) + { + ind = (size_t)j + 1; + break; + } + } + if (found && j <= 0) + ind = 1; + slocIndices.push_back(ind); + dslocIndices.push_back(ind); + } + } + } + + } + + dslocIndices.sort(); + dslocIndices.unique(); + + // process total set of keywords + slocIndices.sort(); + slocIndices.unique(); + startInd = 0; + while (slocIndices.size() > 0) + { + // get current keyword index + startInd = slocIndices.front(); + slocIndices.pop_front(); + if (slocIndices.size() > 0) + endInd = slocIndices.front(); + else + endInd = string::npos; + + // process continuation + if (strLSLOCBak.length() > 0) + { + strSize = CUtil::TruncateLine(startInd, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(0, strSize); + strLSLOCBak += lineBak.substr(0, strSize); + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + { + if (data_continue) + result->data_lines[LOG]++; + else + result->exec_lines[LOG]++; + } + strLSLOC = strLSLOCBak = ""; + } + data_continue = false; + + // determine keyword type + if (eslocIndices.size() > 0 && eslocIndices.front() == startInd) + eslocIndices.pop_front(); + else + { + dslocIndices.pop_front(); + data_continue = true; + } + + // process LSLOC + if (endInd != string::npos) + { + strSize = CUtil::TruncateLine(endInd - startInd, 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC = line.substr(startInd, strSize); + strLSLOCBak = lineBak.substr(startInd, strSize); + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + { + if (data_continue) + result->data_lines[LOG]++; + else + result->exec_lines[LOG]++; + } + strLSLOC = strLSLOCBak = ""; + startInd = endInd; + } + } + + // capture continuing LSLOC + if (startInd < line.length()) + { + strSize = CUtil::TruncateLine(line.length() - startInd, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(startInd, strSize); + strLSLOCBak += lineBak.substr(startInd, strSize); + } + } +} + +/*! +* Constructs a CSqlColdFusionCounter object. +*/ +CSqlColdFusionCounter::CSqlColdFusionCounter() +{ + classtype = SQL_CFM; + language_name = "SQL/ColdFusion"; + + file_extension.clear(); + file_extension.push_back(".*sqlcfm"); +} diff --git a/src/CSqlCounter.h b/src/CSqlCounter.h new file mode 100644 index 0000000..8e5fe11 --- /dev/null +++ b/src/CSqlCounter.h @@ -0,0 +1,67 @@ +//! Code counter class definition for the SQL language. +/*! +* \file CSqlCounter.h +* +* This file contains the code counter class definition for the SQL language. +*/ + +#ifndef CSqlCounter_h +#define CSqlCounter_h + +#include "CCodeCounter.h" + +//! SQL code counter class. +/*! +* \class CSqlCounter +* +* Defines the SQL code counter class. +*/ +class CSqlCounter : public CCodeCounter +{ +public: + CSqlCounter(); + +protected: + virtual int ReplaceQuote(string &strline, size_t &idx_start, bool &contd, char &CurrentQuoteEnd); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak = NULL); + void LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + bool &data_continue); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CSqlCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CSqlCounter(const CSqlCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CSqlCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CSqlCounter operator=(const CSqlCounter); // Declare without implementation +}; + +//! SQL in ColdFusion code counter class. +/*! +* \class CSqlColdFusionCounter +* +* Defines the SQL in ColdFusion code counter class. +*/ +class CSqlColdFusionCounter : public CSqlCounter +{ +public: + CSqlColdFusionCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CSqlColdFusionCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CSqlColdFusionCounter(const CSqlColdFusionCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CSqlColdFusionCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CSqlColdFusionCounter operator=(const CSqlColdFusionCounter); // Declare without implementation +}; + +#endif diff --git a/src/CTagCounter.cpp b/src/CTagCounter.cpp new file mode 100644 index 0000000..32c56eb --- /dev/null +++ b/src/CTagCounter.cpp @@ -0,0 +1,351 @@ +//! Code counter class methods for tag languages including HTML, XML, and ColdFusion. +/*! +* \file CTagCounter.cpp +* +* This file contains the code counter class methods for tag languages including HTML, XML, and ColdFusion. +*/ + +#include "CTagCounter.h" + +/*! +* Constructs a CTagCounter object. +*/ +CTagCounter::CTagCounter() +{ + casesensitive = false; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CTagCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + unsigned int cnt = 0; + filemap::iterator fit, fitBak; + string line, lineBak; + size_t lineNumber = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string strLSLOC, strLSLOCBak; + char prev_char = 0; + bool ok, data_continue = false; + unsigned int phys_exec_lines = 0; + unsigned int phys_data_lines = 0; + unsigned int temp_lines = 0; + + size_t idx_start, quote_idx_start; + char CurrentQuoteEnd = 0; + bool quote_contd = false; + QuoteStart = ">"; + QuoteEnd = "<"; + + for (fit = fmap->begin(), fitBak = fmapBak->begin(); fit != fmap->end(); fit++, fitBak++) + { + if (!CUtil::CheckBlank(fit->line)) + { + // replace "quotes" - string between close and open tags + // must be processed after comments since comments start/end with same character as tag + quote_idx_start = 0; + idx_start = 0; + if (quote_contd) + { + // replace quote until next character + ReplaceQuote(fit->line, quote_idx_start, quote_contd, CurrentQuoteEnd); + } + if (!quote_contd) + { + while (idx_start < fit->line.length()) + { + quote_idx_start = FindQuote(fit->line, QuoteStart, quote_idx_start, QuoteEscapeFront); + + if (quote_idx_start == string::npos) + break; + + ReplaceQuote(fit->line, quote_idx_start, quote_contd, CurrentQuoteEnd); + if (quote_idx_start > idx_start) + { + // comment delimiter inside quote + idx_start = quote_idx_start; + continue; + } + } + } + + line = fit->line; + lineBak = fitBak->line; + lineNumber = fit->lineNumber; + + LSLOC(result, line, lineNumber, lineBak, strLSLOC, strLSLOCBak, prev_char, + data_continue, temp_lines, phys_exec_lines, phys_data_lines); + + if (print_cmplx) + { + cnt = 0; + CountTagTally(line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count, false); + } + + result->exec_lines[PHY] += phys_exec_lines; + phys_exec_lines = 0; + + result->data_lines[PHY] += phys_data_lines; + phys_data_lines = 0; + } + } + QuoteStart = ""; + QuoteEnd = ""; + + // capture closing tag + if (strLSLOC.length() > 0) + { + bool trunc_flag = false; + if (strLSLOCBak.length() == this->lsloc_truncate) + trunc_flag = true; + ok = result->addSLOC(strLSLOCBak, lineNumber, trunc_flag); + + cnt = 0; + if (data_name_list.size() > 0) + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", &result->data_name_count); + + if (data_continue || cnt > 0) + { + if (ok) + result->data_lines[LOG]++; + result->data_lines[PHY]++; + } + else + { + if (ok) + result->exec_lines[LOG]++; + + // since physical data lines are recorded at next LSLOC, check if first line was a data line + if (data_name_list.size() > 0) + { + fit = fmap->begin(); + cnt = 0; + CUtil::CountTally(fit->line, data_name_list, cnt, 1, exclude, "", "", NULL); + if (cnt > 0) + result->exec_lines[PHY]++; + } + } + } + return 0; +} + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results +* \param line processed physical line of code +* \param lineBak original physical line of code +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param prev_char previous character +* \param data_continue continuation of a data declaration line +* \param temp_lines tracks physical line count +* \param phys_exec_lines number of physical executable lines +* \param phys_data_lines number of physical data lines +*/ +void CTagCounter::LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, char &prev_char, + bool &data_continue, unsigned int &temp_lines, unsigned int &phys_exec_lines, unsigned int &phys_data_lines) +{ + size_t start = 0; + size_t i = 0, strSize; + unsigned int cnt = 0; + string tmp; + bool trunc_flag = false; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + while (i < line.length()) + { + if (line[i] == '<') + { + /* + * Modification Fall 2016 + * Line below changed from: if (line.length() - 1 > i && (line[i+1] == '/' || line[i+1] == '?' || line[i+1] == '!')) + * to: if (line.length() - 1 > i && (line[i+1] == '/' )) --> removed condition for ? and ! + * in order to count multiple lines beginning with i && (line[i+1] == '/' )) + { + i++; + continue; + } + + strSize = CUtil::TruncateLine(i - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + if (strLSLOC.length() > 0 && strLSLOC[strLSLOC.length()-1] != '>' && line[0] != '<') + { + strLSLOC += " "; + strLSLOCBak += " "; + } + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + if (strLSLOC.length() > 0) + { + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + { + cnt = 0; + if (data_name_list.size() > 0) + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", &result->data_name_count); + + if (data_continue || cnt > 0) + { + result->data_lines[LOG]++; + phys_data_lines = temp_lines; + } + else + { + temp_lines++; + result->exec_lines[LOG]++; + phys_exec_lines = temp_lines; + } + } + else if (data_continue == true) + phys_data_lines = temp_lines; + else + phys_exec_lines = temp_lines; + data_continue = false; + temp_lines = 0; + strLSLOC = strLSLOCBak = ""; + start = i; + } + } + else if (line[i] == '>') + { + // also, <> is also skipped, empty block is not counted + if (prev_char == '/' || prev_char == '<') + start = i + 1; + else if (exclude_keywords.size() > 0) + { + // skip excluded keywords + cnt = 0; + CUtil::CountTally(line.substr(start, i - start), exclude_keywords, cnt, 1, exclude, "", "", NULL); + if (cnt > 0) + start = i + 1; + } + } + i++; + } + + tmp = CUtil::TrimString(line.substr(start, i - start)); + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + if (strLSLOC.length() > 0 && strLSLOC[strLSLOC.length()-1] != '>' && line[0] != '<') + { + strLSLOC += " "; + strLSLOCBak += " "; + } + strLSLOC += tmp.substr(0, strSize); + tmp = CUtil::TrimString(lineBak.substr(start, i - start)); + strLSLOCBak += tmp.substr(0, strSize); + } + + // verify not beginning to process a new data line + cnt = 0; + if (!data_continue && data_name_list.size() > 0) + { + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", NULL); + if (cnt > 0) + data_continue = true; + } + if (data_continue == true) + temp_lines++; + if (temp_lines == 0 && phys_data_lines == 0 && phys_exec_lines == 0) + phys_exec_lines = 1; +} + +/*! +* Counts HTML keywords in string. +* +* \param base string to search +* \param container set of keywords +* \param count number of keywords found +* \param mode allowable leading/trailing character mode (1=exclude keywords surrounded by exclude characters, 2=include only keywords preceded by include1 characters and followed by include2 characters) +* \param exclude list of characters that may not surround keywords found (if mode=1) +* \param include1 list of characters that must precede keywords found (if mode=2) +* \param include2 list of characters that must follow keywords found (if mode=2) +* \param counter_container stores list of individual keyword counts (if specified) +* \param case_sensitive language is case sensitive? +*/ +void CTagCounter::CountTagTally(string base, StringVector& container, unsigned int &count, int mode, string exclude, + string include1, string include2, UIntVector* counter_container, bool case_sensitive) +{ + base = " " + base + " "; + string::size_type idx; + string base1, temp; + StringVector::iterator vit; + UIntVector::iterator cit; + unsigned int single_count = 0; + + if (counter_container) + cit = counter_container->begin(); + + if (case_sensitive == false) + { + base1 = CUtil::ToLower(base); + for (vit = container.begin(); vit != container.end(); vit++) + (*vit) = CUtil::ToLower((*vit)); + } + else + base1 = base; + + if (mode == 1) + { + // exclude mode + for (vit = container.begin(); vit != container.end(); vit++) + { + temp = "<" + *vit; + idx = base1.find(temp); + while (idx != string::npos) + { + if ((exclude.find(base1[idx+(temp.size())] ) == string::npos) && + (exclude.find(base1[idx-1]) == string::npos)) + { + count++; + single_count++; + } + idx = base1.find(temp, idx + (temp.size())); + } + if (counter_container) + { + (*cit) += single_count; + single_count = 0; + cit++; + } + } + } + else if (mode == 2) + { + // include mode + for (vit = container.begin(); vit != container.end(); vit++) + { + temp = "<" + *vit; + idx = base1.find(temp); + while (idx != string::npos) + { + if ((include1.find(base1[idx-1]) != string::npos) && + (include2.find(base1[idx+(temp.size())]) != string::npos)) + count++; + idx = base1.find(temp, idx + (temp.size())); + } + } + } +} diff --git a/src/CTagCounter.h b/src/CTagCounter.h new file mode 100644 index 0000000..3cd6bf5 --- /dev/null +++ b/src/CTagCounter.h @@ -0,0 +1,46 @@ +//! Code counter class definition for tag languages including HTML, XML, and ColdFusion. +/*! +* \file CHtmlCounter.h +* +* This file contains the code counter class definition for for tag languages including HTML, XML, and ColdFusion. +*/ + +#ifndef CTagCounter_h +#define CTagCounter_h + +#include "CCodeCounter.h" + +//! Tag language code counter class. +/*! +* \class CTagCounter +* +* Defines the tag language code counter class. +*/ +class CTagCounter : public CCodeCounter +{ +public: + CTagCounter(); + +protected: + virtual int PreCountProcess(filemap* /*fmap*/) { return 0; } + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak = NULL); + void LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + char &prev_char, bool &data_continue, unsigned int &temp_lines, unsigned int &phys_exec_lines, + unsigned int &phys_data_lines); + void CountTagTally(string base, StringVector& container, unsigned int &count, int mode, string exclude, + string include1, string include2, UIntVector* counter_container = 0, bool case_sensitive = true); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CTagCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CTagCounter(const CTagCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CTagCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CTagCounter operator=(const CTagCounter); // Declare without implementation +}; + +#endif diff --git a/src/CUtil.cpp b/src/CUtil.cpp new file mode 100644 index 0000000..d10b26e --- /dev/null +++ b/src/CUtil.cpp @@ -0,0 +1,2784 @@ +//! Utility class static methods. +/*! +* \file CUtil.cpp +* +* This file contains the utility class static methods. +*/ + +/* Modification: 2016.01; USC +* Fixed mingw 4.9.1 compilation errors */ +#include +#include "UCCBeforeLibraryIncludes.h" // Modification: 2015.12 +#include +#include +#include // Modification: 2015.12 +#include // Modification: 2009.01 +#include // Modification: 2015.12 + +using namespace std; + +#ifdef UNIX // Modification: 2009.01 + #include + #include + #include //Modification: 2018.01 +#else + #include + #include +#endif + +/****************** NOT USED +#ifndef NO32BIT // Modification: 2015.12 + + // ensure unsigned 32-bit integer is available for SHA-256 algorithm + // if compile fails, no standard 32-bit integer exists + #include + #if UINT_MAX == 0xffffffff + typedef unsigned int uint32_t; + #elif ULONG_MAX == 0xffffffff + typedef unsigned long uint32_t; + #else + // only exists if C++11 standard (ISO/IEC 14882:2011) + #include + #endif + + // macros to shift and rotate 32-bit word the specified number of bits to the right + #define ShiftRight32Bit(word, bits) (((bits) >= 32) ? 0 : ((word) >> (bits))) + #define RotateRight32Bit(word, bits) (((word) >> (bits)) | ((word) << (32 - (bits)))) + +#endif // Modification: 2009.01 +********* END OF NOT USED ******/ + +#include "UCCAfterLibraryIncludes.h" // Modification: 2015.12 + +#include "UCCGlobals.h" // Modification: 2015.12 +#include "UCCFilesOut.h" // Modification: 2015.12 +#include "CUtil.h" + + +// Convert an int to a std::string +void IntToStr( const int val, string & result ) +{ + char buf[64]; + +// Use more Secure C library APIs if available +#ifdef _MSC_VER + // Microsoft C++ compiler. This MAY not be available on very old versions + // _itoa_s( val, buf, 10 ); // itoa is not standard C so not available on Mac for example + sprintf_s( buf, sizeof( buf )/sizeof( buf[0] ), "%d", val ); +#else + // Use less Secure original C library API + sprintf( buf, "%d", val ); +#endif + + result = buf; +} + +// Convert a long to a std::string +void LongToStr( const long val, string & result ) +{ + char buf[64]; + +// Use more Secure C library APIs if available +#ifdef _MSC_VER + // Microsoft C++ compiler. This MAY not be available on very old versions + // _ltoa_s( val, buf, 10 ); // ltoa is not standard C + sprintf_s( buf, sizeof( buf )/sizeof( buf[0] ), "%ld", val ); +#else + // Use less Secure original C library API + sprintf( buf, "%ld", val ); +#endif + + result = buf; +} + +// Convert a long long to a std::string +void LongLongToStr( const long long val, string & result ) +{ + char buf[128]; + +// Use more Secure C library APIs if available +#ifdef _MSC_VER + // Microsoft C++ compiler. This MAY not be available on very old versions + // _ltoa_s( val, buf, 10 ); // ltoa is not standard C + sprintf_s( buf, sizeof( buf )/sizeof( buf[0] ), "%lld", val ); +#else + // Use less Secure original C library API + sprintf( buf, "%lld", val ); +#endif + + result = buf; +} + +// Convert a float to a std::string with a number of decimal places +void FloatToStr( const float val, string & result, const unsigned int dec_precision ) +{ + char buf[128]; + + if ( 1 == dec_precision ) + { + // Use more Secure C library APIs if available +#ifdef _MSC_VER + // Microsoft C++ compiler. This MAY not be available on very old versions + // _ltoa_s( val, buf, 10 ); // ltoa is not standard C + sprintf_s( buf, sizeof( buf )/sizeof( buf[0] ), "%.1f", val ); +#else + // Use less Secure original C library API + sprintf( buf, "%.1f", val ); +#endif + result = buf; + } + else + result = ""; +} + + +// Count only Physical files and do NOT count embedded "extension files" +void CountPhysicalFiles( SourceFileList & fileList, unsigned long & phyFileCount ) +{ + unsigned long foundCount = 0L; + + SourceFileList::iterator itEnd = fileList.end(); + for ( SourceFileList::iterator it = fileList.begin(); it != itEnd; it++ ) + { + if ( (*it).second.file_name_isEmbedded ) + continue; + + foundCount++; + } + + phyFileCount = foundCount; +} + + +// Helper called to clear totals of various message types +void ClearSummaryMsgCounts() +{ + stack_dump_count = 0; + errors_count = 0; + warnings_count = 0; + information_count = 0; + uncounted_files_count = 0L; +} + +// Initialize Time values of various processing steps. +void InitTimes() +{ + time( &timeStart ); + +// Set all the time values to at least the start time. + // Removed time_end_match_baselines_web = from below Randy + timeEnd = time_end_files_analysis = time_end_files_read = time_end_print_resultsB \ + = time_end_find_duplicatesB = time_end_print_results = time_end_find_duplicates \ + = time_end_process_pairs = time_end_match_baselines \ + = time_end_list_built = timeStart; +} + + +/*! +* 1. Function Description: +* Helper to format a message string +* +* 2. Parameters: +* \Local define STR_BUF_SIZE IN gives size of passed in character buffer +* \param pBuf IN/OUT pointer to a character buffer to use when formatting +* \param time_str IN/OUT string to append newly formatted character buffer +* \param before IN string to show before the number +* \param count IN number to display if >= 0 +* \param after IN string to show after the number +* \param fixed_width IN use fixed width for number or not +* +* 3.Creation Time And Owner: +* Version: 2015.12 +*/ +void TimeMsg( char *pBuf, string & time_str, const char * before, + const long count, const char * after, const bool fixed_width ) +{ + // Use more Secure C library APIs if available +#ifdef _MSC_VER + strcpy_s( pBuf, MSG_STR_BUF_SIZE * sizeof(char), "" ); + if ( count >= 0 ) + { + if ( fixed_width ) + sprintf_s( pBuf, MSG_STR_BUF_SIZE * sizeof(char), "%s%6ld%s", before, count, after ); + else + sprintf_s( pBuf, MSG_STR_BUF_SIZE * sizeof(char), "%s%ld%s", before, count, after ); + } + else + sprintf_s( pBuf, MSG_STR_BUF_SIZE * sizeof(char), "%s%s", before, after ); +#else + // Use less Secure original C library APIs + strcpy( pBuf, "" ); + if ( count >= 0 ) + { + if ( fixed_width ) + sprintf( pBuf, "%s%6ld%s", before, count, after ); + else + sprintf( pBuf, "%s%ld%s", before, count, after ); + } + else + sprintf( pBuf, "%s%s", before, after ); +#endif + + time_str += pBuf; +} + + +/*! +* 1. Function Description: +* Show Times to perform various steps +* Don't call this if you do not want any Times shown +* +* 2. Parameters: +* \Global g_process_after_read IN Decision variable for exact sequence of steps +* \Globals time_end_xxx IN xxx represents names given above +* \Local define STR_BUF_SIZE IN gives size of character buffer to use +* \param time_str IN/OUT string to append newly formatted character buffer +* \param timeStart IN Time when this activity started +* \param timeEnd IN Time when this activity ended +* \param show_total_only IN Decision variable for amount of Time details +* \param doDiff IN Decision variable to tell if Differencing was done +* \param doDups IN Decision variable to tell if Duplicate checks were done +* +* 3.Creation Time And Owner: +* Version: 2015.12 +* Randy Maxwell +*/ +void ShowTiming( string & time_str, + const time_t timeStart, const time_t timeEnd, + const bool show_total_only, + const bool doDiff, const bool doDups ) +{ + long diffSeconds; + char buf[MSG_STR_BUF_SIZE]; + + if ( false == show_total_only ) + { + TimeMsg( buf, time_str, "\n Processing Step : seconds\n", -1, "" ); + TimeMsg( buf, time_str, "------------------------- : --------\n", -1, "" ); + + diffSeconds = (long)( difftime( time_end_list_built, timeStart ) + 0.5 ); // round to nearest second + TimeMsg( buf, time_str, " Build file list : ", diffSeconds, "\n" ); + + if ( doDiff ) + { + diffSeconds = (long)( difftime( time_end_match_baselines, time_end_list_built ) + 0.5 ); + TimeMsg( buf, time_str, " Match baselines : ", diffSeconds, "\n" ); + + /* NOT USED but don't want to delete from code yet. Randy + diffSeconds = (long)( difftime( time_end_match_baselines_web, time_end_match_baselines ) + 0.5 ); + TimeMsg( buf, time_str, " Match Web baselines : ", diffSeconds, "\n" ); + */ + // Was time_end_process_pairs, time_end_match_baselines_web + diffSeconds = (long)( difftime( time_end_process_pairs, time_end_match_baselines ) + 0.5 ); + TimeMsg( buf, time_str, " Files comparison : ", diffSeconds, "\n" ); + + if ( doDups ) + { + diffSeconds = (long)( difftime( time_end_find_duplicates, time_end_process_pairs ) + 0.5 ); + TimeMsg( buf, time_str, " Find A duplicates : ", diffSeconds, "\n" ); + } + else + time_end_find_duplicates = time_end_process_pairs; + + diffSeconds = (long)( difftime( time_end_print_results, time_end_find_duplicates ) + 0.5 ); + TimeMsg( buf, time_str, " Generate results A files : ", diffSeconds, "\n" ); + + if ( doDups ) + { + diffSeconds = (long)( difftime( time_end_find_duplicatesB, time_end_print_results ) + 0.5 ); + TimeMsg( buf, time_str, " Find B duplicates : ", diffSeconds, "\n" ); + } + else + time_end_find_duplicatesB = time_end_print_results; + + diffSeconds = (long)( difftime( time_end_print_resultsB, time_end_find_duplicatesB ) + 0.5 ); + TimeMsg( buf, time_str, " Generate results B files : ", diffSeconds, "\n" ); + } + else + { + // NOT Diff + // Show the Read and Analyze times + if ( ! g_process_after_read ) + { + diffSeconds = (long)( difftime( time_end_files_read, time_end_list_built ) + 0.5 ); + TimeMsg( buf, time_str, " Reading files : ", diffSeconds, "\n" ); + + diffSeconds = (int)( difftime( time_end_files_analysis, time_end_files_read ) + 0.5 ); + TimeMsg( buf, time_str, " Analyze files : ", diffSeconds, "\n" ); + } + else + { // Reading with Analysis following immediately on per file basis + // instead of Reading going through list then start Analyze through list. + diffSeconds = (long)( difftime( time_end_files_analysis, time_end_list_built ) + 0.5 ); + TimeMsg( buf, time_str, "Read, Analyze & Count : ", diffSeconds, "\n" ); + } + + if ( doDups ) + { + diffSeconds = (long)( difftime( time_end_find_duplicates, time_end_files_analysis ) + 0.5 ); + TimeMsg( buf, time_str, " Find duplicates : ", diffSeconds, "\n" ); + } + else + time_end_find_duplicates = time_end_files_analysis; + + diffSeconds = (long)( difftime( time_end_print_results, time_end_find_duplicates ) + 0.5 ); + TimeMsg( buf, time_str, " Generate results files : ", diffSeconds, "\n" ); + + } // END else NOT Diff + } // END ! show_total_only + + // Show the elapsed time + diffSeconds = (long)( difftime( timeEnd, timeStart ) + 0.5 ); + if ( diffSeconds > 60 ) + { + if ( diffSeconds >= 120 ) + { + // Variable minutes + TimeMsg( buf, time_str, " Total time : ", (diffSeconds / 60), " minutes", false ); + // seconds + TimeMsg( buf, time_str, " ", (diffSeconds % 60), " seconds\n", false ); + } + else + { + TimeMsg( buf, time_str, " Total time : 1 minute ", (diffSeconds % 60), " seconds\n", false ); + } + } + else + TimeMsg( buf, time_str, " Total time : ", diffSeconds, " seconds\n" ); + + return; +} + + +void SavePerformanceStats( const string time_str ) +{ + time_t myTime; + struct tm *myLocalTime; + time(&myTime); +#if defined UNIX || defined MINGW + myLocalTime = localtime(&myTime); +#else + struct tm myLT; + localtime_s(&myLT, &myTime); + myLocalTime = &myLT; +#endif + char s[64]; + strftime( s, sizeof(s), "/UCC_Performance__%m%d%Y_%I%M%S.txt", myLocalTime ); + + string path_name; + if ( outDir.length() ) + path_name = outDir + s; + else + { + // use current directory + path_name = s; + path_name = "." + path_name; + } + + FILE* pFile = NULL; + + // Use more Secure C library APIs if available + #ifdef _MSC_VER + fopen_s( &pFile, path_name.c_str(), "a" ); + #else + pFile = fopen( path_name.c_str(), "a" ); + #endif + + if ( NULL != pFile ) + { + fprintf( pFile, "%s\n", time_str.c_str() ); + fprintf( pFile, "\n%s\n", cmdLine.c_str() ); + fclose( pFile ); + } +} + +// +// ALL BELOW ARE TIGHTLY BOUND AS CUtil CLASS Member Functions. Not really needed to be like this. +// +// There is a form of protection via a class type namespace. Because all these are static; see .h file +// there is no protection via implementation hiding as all these are also public. +// + +/*/*Modification: 11.2016 Ext-4 Starts*/ +void CUtil::allocate() { + + std::vector ada_vec; + ada_vec.push_back(".ada"); ada_vec.push_back(".a"); ada_vec.push_back(".adb"); ada_vec.push_back(".ads"); + dict["Ada"] = ada_vec; + dictCopy["Ada"] = ada_vec; + + std::vector assembly_vec; + assembly_vec.push_back(".asm"); assembly_vec.push_back(".s"); assembly_vec.push_back(".asm.ppc"); + dict["Assembly"] = assembly_vec; + dictCopy["Assembly"] = assembly_vec; + + std::vector dos_vec; + dos_vec.push_back(".bat"); + dict["DOS_Batch"] = dos_vec; + dictCopy["DOS_Batch"] = dos_vec; + + std::vector bash_vec; + bash_vec.push_back(".sh"); bash_vec.push_back(".ksh"); + dict["Bash"] = bash_vec; + dictCopy["Bash"] = bash_vec; + + std::vector cobol_vec; + cobol_vec.push_back(".cbl"); cobol_vec.push_back(".cob"); cobol_vec.push_back(".cpy"); + dict["COBOL"] = cobol_vec; + dictCopy["COBOL"] = cobol_vec; + + std::vector coldfu_vec; + coldfu_vec.push_back(".*cfm"); + dict["ColdFusion"] = coldfu_vec; + dictCopy["ColdFusion"] = coldfu_vec; + + std::vector cfs_vec; + cfs_vec.push_back(".cfs"); + dict["CFScript"] = cfs_vec; + dictCopy["CFScript"] = cfs_vec; + + std::vector cshell_vec; + cshell_vec.push_back(".csh"); cshell_vec.push_back(".tcsh"); + dict["C-Shell"] = cshell_vec; + dictCopy["C-Shell"] = cshell_vec; + + std::vector ccpp_vec; + ccpp_vec.push_back(".cpp"); ccpp_vec.push_back(".c"); ccpp_vec.push_back(".cc"); ccpp_vec.push_back(".cxx"); ccpp_vec.push_back(".inl"); + ccpp_vec.push_back(".h"); ccpp_vec.push_back(".hh"); ccpp_vec.push_back(".hpp"); ccpp_vec.push_back(".hxx"); ccpp_vec.push_back(".inc"); + dict["C_CPP"] = ccpp_vec; + dictCopy["C_CPP"] = ccpp_vec; + + std::vector csharp_vec; + csharp_vec.push_back(".cs"); + dict["C#"] = csharp_vec; + dictCopy["C#"] = csharp_vec; + + std::vector css_vec; + css_vec.push_back(".css"); + dict["CSS"] = css_vec; + dictCopy["CSS"] = css_vec; + + std::vector fortran_vec; + fortran_vec.push_back(".f"); fortran_vec.push_back(".for"); fortran_vec.push_back(".f77"); fortran_vec.push_back(".f90"); fortran_vec.push_back(".f95"); + fortran_vec.push_back(".f03"); fortran_vec.push_back(".hpf"); + dict["Fortran"] = fortran_vec; + dictCopy["Fortran"] = fortran_vec; + + std::vector html_vec; + html_vec.push_back(".*htm"); + dict["HTML"] = html_vec; + dictCopy["HTML"] = html_vec; + + std::vector idl_vec; + idl_vec.push_back(".pro"); idl_vec.push_back(".sav"); + dict["IDL"] = idl_vec; + dictCopy["IDL"] = idl_vec; + + std::vector java_vec; + java_vec.push_back(".java"); + dict["Java"] = java_vec; + dictCopy["Java"] = java_vec; + + std::vector javascript_vec; + javascript_vec.push_back(".js"); + dict["JavaScript"] = javascript_vec; + dictCopy["JavaScript"] = javascript_vec; + + std::vector makefile_vec; + makefile_vec.push_back(".make"); makefile_vec.push_back(".makefile"); + dict["Makefile"] = makefile_vec; + dictCopy["Makefile"] = makefile_vec; + + std::vector matlab_vec; + matlab_vec.push_back(".m"); + dict["MATLAB"] = matlab_vec; + dictCopy["MATLAB"] = matlab_vec; + + std::vector nextmidas_vec; + nextmidas_vec.push_back(".mm"); + dict["NeXtMidas"] = nextmidas_vec; + dictCopy["NeXtMidas"] = nextmidas_vec; + + std::vector xmidas_vec; + xmidas_vec.push_back(".txt"); + dict["X-Midas"] = xmidas_vec; + dictCopy["X-Midas"] = xmidas_vec; + + std::vector pascal_vec; + pascal_vec.push_back(".pas"); pascal_vec.push_back(".p"); pascal_vec.push_back(".pp"); pascal_vec.push_back(".pa3"); pascal_vec.push_back(".pa4"); pascal_vec.push_back(".pa5"); + dict["Pascal"] = pascal_vec; + dictCopy["Pascal"] = pascal_vec; + + std::vector perl_vec; + perl_vec.push_back(".pl"); perl_vec.push_back(".pm"); + dict["Perl"] = perl_vec; + dictCopy["Perl"] = perl_vec; + + std::vector php_vec; + php_vec.push_back(".*php"); + dict["PHP"] = php_vec; + dictCopy["PHP"] = php_vec; + + + std::vector python_vec; + python_vec.push_back(".py"); + dict["Python"] = python_vec; + dictCopy["Python"] = python_vec; + + + std::vector ruby_vec; + ruby_vec.push_back(".rb"); + dict["Ruby"] = ruby_vec; + dictCopy["Ruby"] = ruby_vec; + + std::vector scala_vec; + scala_vec.push_back(".scala"); + dict["Scala"] = scala_vec; + dictCopy["Scala"] = scala_vec; + + + std::vector sql_vec; + sql_vec.push_back(".sql"); + dict["SQL"] = sql_vec; + dictCopy["SQL"] = sql_vec; + + std::vector vb_vec; + vb_vec.push_back(".vb"); vb_vec.push_back(".frm"); vb_vec.push_back(".mod"); vb_vec.push_back(".cls"); vb_vec.push_back(".bas"); + dict["Visual_Basic"] = vb_vec; + dictCopy["Visual_Basic"] = vb_vec; + + std::vector vbscript_vec; + vbscript_vec.push_back(".vbs"); + dict["VBScript"] = vbscript_vec; + dictCopy["VBScript"] = vbscript_vec; + + std::vector verilog_vec; + verilog_vec.push_back(".v"); verilog_vec.push_back(".sv"); verilog_vec.push_back(".svi"); verilog_vec.push_back(".vlib"); verilog_vec.push_back(".svlib"); verilog_vec.push_back(".vh"); verilog_vec.push_back(".svh"); + dict["Verilog"] = verilog_vec; + dictCopy["Verilog"] = verilog_vec; + + std::vector vhdl_vec; + vhdl_vec.push_back(".vhd"); vhdl_vec.push_back(".vhdl"); + dict["VHDL"] = vhdl_vec; + dictCopy["VHDL"] = vhdl_vec; + + std::vector xml_vec; + xml_vec.push_back(".*xml"); + dict["XML"] = xml_vec; + dictCopy["XML"] = xml_vec; + +} +StringVector CUtil::getExtensionsToLanguage(string lang, StringVector fileExtension) +{ + std::vector myvector = dict[lang]; + vector::iterator it; + //StringVector fileExtension; + for (it = myvector.begin(); it < myvector.end(); it++) + { + fileExtension.push_back(*it); + } + return fileExtension; +} +/*Modification: 11.2016 Ext-4 ends*/ + +/*! +* 1. Function Description: +* Returns a string without leading/trailing spaces or tabs. +* Return trimmed string +* +* 2. Parameters: +* str:original string +* mode:trim mode (-1=left, 0=both, 1=right) +* +* 3. Creation Time and Owner: +* Version 2009.01 +*/ +string CUtil::TrimString(const string &str, int mode) // Modification: 2011.05 +{ + size_t idx; + string str1 = str; // Modification: 2011.10 + bool done = false; // Modification: 2015.12 + if (mode <= 0) // Modification: 2011.05 + { + idx = str1.find_first_not_of(" \t\n\r\f"); + if (idx != string::npos) + str1 = str1.substr(idx); + else + { + // Nothing but whitespace in the string + str1 = ""; + done = true; // no need to check other direction Modification: 2015.12 + } + } + if ( ( false == done ) // Modification: 2015.12 + && ( mode >= 0 ) ) + { + idx = str1.find_last_not_of(" \t\n\r\f"); + if (idx != string::npos) + str1 = str1.substr(0, idx + 1); + else + str1 = ""; + } + return str1; // Modification: 2011.10 +} + +/*! +* 1. Function Description: +* Erases all strings specified by erasedstr in srcstr. +* Return modified string +* +* 2. Parameters: +* srcstr: original string +* erasedstr: substring to be erased +* +* 3. Creation Time and Owner: +* Version 2011.05 +*/ +string CUtil::EraseString(const string &srcstr, const string &erasedstr) +{ + size_t idx = 0; + string srcstr1 = srcstr; // Modification: 2011.10 + while ((idx = srcstr1.find(erasedstr, idx)) != string::npos) // Modification: 2011.05 + { + srcstr1.erase(idx, erasedstr.length()); + } + return srcstr1; +} + +/*! +* 1. Function Description: Initialize an array of lower case chars used by ToLower. +* Performance improvement (significant, some tests show ~7% less time use by entire UCC). +* Valid for ASCII style characters only (values from 0 to 255). +* NOT for UTF or Unicode character sets. +* +* 2. Parameters: +* \Globals lowerChars char array is initialized +* none, fills in Global lowerChars array +* +* 3. Creation Time and Owner: +* Version 2015.10.02 +*/ +unsigned char lowerChars[256]; +static bool useLibraryToLower = false; +void CUtil::InitToLower() +{ + // Call expensive tolower library call just to fill in array + // Modification: Spring 2016 - 'j' can be unsinged int to avoid warning + for ( unsigned int j = 0; j < ( sizeof( lowerChars ) / sizeof( lowerChars[0] ) ); j++ ) + { + lowerChars[ j ] = (unsigned char)( (unsigned int)( tolower( j ) ) ); + } + + string test = "A"; + size_t temp = 1; + if ( sizeof( test[ 0 ] ) != temp ) + useLibraryToLower = true; +} + +/*! +* 1. Function Description: Returns a string in lower case. +* Return lower case string +* +* 2. Parameters: +* string_to_lower:original string +* \Globals lowerChars char array is used to greatly improve performance +* +* 3. Creation Time and Owner: +* Version 2009.01 +* Revised 2015.12 Use array lookup instead of calling C library API +*/ +string CUtil::ToLower(const string &string_to_lower) +{ + string string_to_lower2 = string_to_lower; // Modification: 2011.10 + size_t size = string_to_lower2.size(); // avoid calling size() as part of loop + if ( false == useLibraryToLower ) + { + for ( size_t i = 0; i < size; i++ ) // Modification: 2009.01 + { + // Use array instead of much slower tolower library call + string_to_lower2[ i ] = (char)lowerChars[ TL_ARR( string_to_lower2[ i ] ) ]; // Modification: 2015.10.02 + } + } + else + { + // SOME characters are greater than 255 so do the lower 255 quickly and others as library call + for ( size_t i = 0; i < size; i++ ) // Modification: 2009.01 + { + if ( (unsigned int)( string_to_lower2[ i ] ) <= 255 ) + { + // Use array instead of much slower tolower library call + string_to_lower2[ i ] = (char)lowerChars[ TL_ARR( string_to_lower2[ i ] ) ]; // Modification: 2015.10.02 + } + else + { + // Use C library for this character + string_to_lower2[ i ] = (char)tolower( (int)string_to_lower2[ i ] ); + } + } + } + return string_to_lower2; +} + +/*! +* 1. Function Description: +* Checks whether the string passed is blank. +* Return true if it's a blank string +* +* 2. Parameters: +* str:original string +* +* 3. Creation Time and Owner: +* Version 2009.01 +*/ +bool CUtil::CheckBlank(const string &str) +{ + string::size_type idx; + idx = str.find_first_not_of("\n\t\r\f "); // searches for chars other than specified + if (idx != string::npos) + return false; + else + return true; +} + +/*! +* 1. Function Description: +* Checks whether the string passed is an integer. +* Return true if it's an integer string +* +* 2. Parameters: +* str:original string +* +* 3. Creation Time and Owner: +* Version 2009.01 +*/ +bool CUtil::IsInteger(const string &str) +{ + string str1 = TrimString(str); // Modification: 2011.10 + if (str1.find_first_not_of("0123456789") != string::npos) // Modification: 2009.01 + return false; + else + return true; +} + +/*! +* 1. Function Description: +* Finds the first appearance of each element of table's first value. +* Return second value of the map +* +* 2. Parameters: +* target:string +* table:map table +* pos:position of string +* preLang:previous language (for web languages) +* +* 3. Creation time and Owner: +* Version 2009.01 +*/ +size_t CUtil::FindStringsCaseInsensitive(const string &target, map &table, size_t &pos, size_t preLang) +{ + string target1 = ToLower(target); + size_t tmp_pos; + pos = string::npos; + size_t ret = INVALID_POSITION; // Modification: 2011.05 + for (map::iterator iter = table.begin(); iter != table.end(); iter++) // Modification: 2009.01 + { + tmp_pos = target1.find(iter->first); + if (tmp_pos != string::npos && (pos == string::npos || pos > tmp_pos)) + { + // handle special case "width=100%>" + ret = static_cast( iter->second ); + if ((ret == WEB_ASP_JSP_END && preLang != WEB_ASP_JSP_START) || + (ret == WEB_PHP_END && (preLang != WEB_PHP_START && preLang != WEB_PHP_START2))) + ret = INVALID_POSITION; // Modification: 2011.05 + pos = tmp_pos; // Modification: 2009.01 + } + } + return ret; +} + +/*! +* 1. Function Description: +* Finds the position of the "target" char in "source" string. +* Starting from "start_idx" but ignoring escape chars, +* for example, '\n' is different from 'n' in "this is not nothing" +* Return position of target character +* +* 2. Parameters: +* source:source string +* target:target character +* start_idx:index to start search +* escape:escape character to ignore +* +* 3. Creation time and Owner: +* Version 2009.01 +*/ +size_t CUtil::FindCharAvoidEscape(const string &source, char target, size_t start_idx, char escape) +{ + size_t idx, i; + if (start_idx >= source.length()) + return string::npos; + idx = start_idx; + while (idx < source.length()) + { + idx = source.find(target, idx); + if (idx == string::npos) + return idx; + + for (i = 1; i <= idx; i++)// Modification: 2015.12 + { + // trace back to the previous char ex. "adfd\\\\" + if (source[idx - i] != escape) break; + } + if (i % 2 != 0) + { + // case \\", avoid case something\" + break; + } + idx++; + } + if (idx >= source.length()) // Modification: 2011.10 + return string::npos; + return idx; // Modification: 2009.01 +} + +/*! +* 1. Function Description: +* Finds the (possibly) beginning part of 2 strings that are the same. +* Removes the common beginning part from each string if wanted. +* Return the string holding the common (to each given string) prefix. +* +* 2. Parameters: +* str: string IN/OUT reference to string that may share a prefix with str2 +* str2: string IN/OUT reference to string that may share a prefix with str +* prefix: string IN/OUT reference to string that is the found common prefix +* remove: bool IN true will remove the prefix characters from both strings +* +* 3. Creation time and Owner: +* Version 2015.12 +*/ +void CUtil::FindCommonPrefix( string & str, string & str2, string & prefix, const bool remove ) +{ + string prefixNew; // Prevent changing prefix until known + + unsigned int prefixSize = 0; + unsigned int count = str.size(); + if ( count > str2.size() ) + count = str2.size(); + + for ( unsigned int i = 0; i < count; i++ ) + { + // This does Exact (case sensitive) match + if ( str[i] == str2[i] ) + { + prefixNew += str[i]; + prefixSize++; + } + else + break; + } + + if ( remove && prefixSize ) + { + str = str.substr( prefixSize ); + str2 = str2.substr( prefixSize ); + } + prefix = prefixNew; +} + +/*! +* 1. Function Description: +* Finds the (possibly) beginning part of 2 strings that are the same. +* Removes the common beginning part from each string if wanted. +* Return the string holding the common (to each given string) prefix. +* +* 2. Parameters: +* str: string IN/OUT reference to string that may share a prefix with str2 +* str2: string IN/OUT reference to string that may share a prefix with str +* prefix: string IN/OUT reference to string that is the found common prefix +* remove: bool IN true will remove the prefix characters from both strings +* +* 3. Creation time and Owner: +* Version 2015.12 +*/ +void CUtil::FindCommonSuffix( string & str, string & str2, string & suffix, const bool remove ) +{ + string suffixNew; // Prevent changing suffix until known + + unsigned int size = str.size(); + unsigned int size2 = str2.size(); + unsigned int count = size; + if ( count > size2 ) + count = size2; + + if ( 0 == count ) + { + suffix = ""; + return; + } + + unsigned int suffixSize = 0; + unsigned int index = size - 1; + unsigned int index2 = size2 - 1; + + for ( unsigned int i = 0; i < count; i++ ) + { + // Go from END of strings + if ( str[index] == str2[index2] ) + { + suffixNew = str[index] + suffixNew; + suffixSize++; + index--; + index2--; + } + else + break; + } + + if ( remove && suffixSize ) + { + str = str.substr( 0, size - suffixSize ); + str2 = str2.substr( 0, size2 - suffixSize ); + } + suffix = suffixNew; +} + +/*! +* 1. Function Description: +* Finds the keyword in the string starting from start to end. +* Return index of keyword in string +* +* 2. Parameters: +* str:string +* keyword:keyword to find +* start:starting index for search +* end:ending index for search +* case_sensitive: is case sensitive? +* +* 3. Creation time and Owner: +* Version 2009.01 +*/ +size_t CUtil::FindKeyword(const string &str, const string &keyword, size_t start, size_t end, bool case_sensitive)// Modification: 2013.04 +{ +// Modification: 2009.01 +#define SPECIAL_CHARS " \t;[]()+/-*<>=,&~!^?:%{}|" + size_t kw_length = keyword.length(); + size_t idx, i = start; + string str1 = str; + string keyword1 = keyword; // Modification: 2014.08 + if (end == TO_END_OF_STRING) // Modification: 2009.01 + end = str1.length() - 1; // inclusive + + if (!case_sensitive) + { + str1 = CUtil::ToLower(str1); + keyword1 = CUtil::ToLower(keyword1); + } + + while (i <= end) + { + idx = str1.find(keyword1, i); + if (idx != string::npos && idx + kw_length - 1 <= end) + { + if ((idx == 0 || strchr(SPECIAL_CHARS, str1[idx - 1]) != NULL) && + (idx + kw_length >= str1.length() || + strchr(SPECIAL_CHARS, str1[idx + kw_length]) != NULL)) + { + // the keyword stands alone or surrounded by special chars + return idx; + } + } + else + { + // cannot find the keyword in str + break; + } + i = idx + 1; // keyword found but not stands alone nor surrounded by special chars + } + + return string::npos; //not found +#undef SPECIAL_CHARS +} + +/*! +* 1. Function Description: +* Counts keywords in string. +* +* 2. Parameters: +* base: string to search +* container: set of keywords +* count: is incremented by the number of keywords found and not changed if none found +* mode: allowable leading/trailing character mode (1=exclude keywords surrounded by exclude characters, 2=include only keywords preceded by include1 characters and followed by include2 characters) +* exclude: list of characters that may not surround keywords found (if mode=1) +* include1: list of characters that must precede keywords found (if mode=2) +* include2: list of characters that must follow keywords found (if mode=2) +* counter_container: stores list of individual keyword counts (if specified) +* case_sensitive: language is case sensitive? +* +* 3. Creation time and Owner: +* Version 2009.01 +*/ +void CUtil::CountTally(const string &base, StringVector &container, unsigned int &count, int mode, const string &exclude, + const string &include1, const string &include2, UIntVector* counter_container, bool case_sensitive) // Modification: 2015.12 +{ + // Precondition: Must have at least 1 keyword + if ( container.size() == 0 ) + return; // Nothing to look for. Modification 2015.12 + + // Modification: 2009.01 + string::size_type idx; + string base1; + StringVector::iterator vit; + UIntVector::iterator cit; + unsigned int single_count = 0; // Modification: 2011.05 + base1 = " " + base + " "; + + if (counter_container) // Modification: 2009.01 + cit = counter_container->begin(); + + if (case_sensitive == false) + { + base1 = CUtil::ToLower(base1); + for (vit = container.begin(); vit != container.end(); vit++) + (*vit) = CUtil::ToLower((*vit)); + } + + if (mode == 1) + { + // exclude mode + for (vit = container.begin(); vit != container.end(); vit++) + { + idx = base1.find((*vit)); + while (idx != string::npos) + { + if ((exclude.find(base1[idx + ((*vit).size())]) == string::npos) && // Modification: 2015.12 + (exclude.find(base1[idx - 1]) == string::npos)) + { + count++; + single_count++; + } + idx = base1.find((*vit), idx + ((*vit).size())); // Modification: 2009.01 + } + if (counter_container) + { + (*cit) += single_count; + single_count = 0; + cit++; + } + } + } + else if (mode == 2) + { + // include mode + for (vit = container.begin(); vit != container.end(); vit++) + { + idx = base1.find((*vit)); + while (idx != string::npos) + { + if ((include1.find(base1[idx - 1]) != string::npos) && // Modification: 2015.12 + (include2.find(base1[idx + ((*vit).size())]) != string::npos)) + count++; + idx = base1.find((*vit), idx + ((*vit).size())); // Modification: 2009.01 + } + } + } +} + +/*! +* 1. Function Description: +* Extracts the filename (without path) from the filepath. +* ex. abc\xyz.cpp --> xyz.cpp +* Return file name +* +* 2. Parameters: +* filepath: file path +* +* 3. Creation time and Owner: +* Version 2009.01 +*/ +string CUtil::ExtractFilename(const string &filepath) +{ + string filename = filepath; // Modification: 2013.04 + // Modification: 2015.12 + size_t idx = string::npos; +#ifdef UNIX + size_t idxW = string::npos; // Windows +#else + size_t idxW = filename.find_last_of("\\"); // Windows +#endif + size_t idxU = filename.find_last_of("/"); // Unix + if (idxW != string::npos && idxU != string::npos) + { + if (idxW > idxU) + idx = idxW; + else + idx = idxU; + } + else if (idxW != string::npos) + idx = idxW; + else if (idxU != string::npos) + idx = idxU; + if (idx != string::npos) + return filename.substr(idx + 1); + else + return filename; +} + +/*! +* 1. Function Description: +* Extracts the path (without name) from the filepath. +* ex. abc\xyz.cpp --> abc +* Return path +* +* 2. Parameters: +* filepath: file path +* +* 3. Creation time and Owner: +* Version 2015.12 +*/ +string CUtil::ExtractFilepath(const string &filepath) +{ + string path = filepath; + size_t idx = string::npos; +#ifdef UNIX + size_t idxW = string::npos; // Windows +#else + size_t idxW = path.find_last_of("\\"); // Windows +#endif + size_t idxU = path.find_last_of("/"); // Unix + if (idxW != string::npos && idxU != string::npos) + { + if (idxW > idxU) + idx = idxW; + else + idx = idxU; + } + else if (idxW != string::npos) + idx = idxW; + else if (idxU != string::npos) + idx = idxU; + if (idx != string::npos) + { + if (idx < 1) + return path.substr(0, 1); + else + return path.substr(0, idx); + } + else + return ""; +} + + +// Helper used for smarter Estimates to help with RAM usage. +/*! +* 1. Function Description: +* Helper used for smarter Estimates to help with RAM usage. +* Do NOT use the size returned for buffer allocation ! +* Returns size of the file whose path/name is given. + +* Precondition: +* It is expected but not required that the file exists. +* +* 2. Parameters: +* file: path/name of file whose Size is wanted +* +* 3. Creation time and Owner: +* Version 2015.12 +*/ +unsigned long long CUtil::GetFileSizeInBytes( const string file ) +{ + unsigned long long file_size_bytes = 0L; + + // Research Note: + // Below is not guarenteed to give to the Byte accurate sizes. + // This is not a problem as we just want to uses sizes for Estimates. + // Some security scanning programs may flag this as INSECURE. + // This is only INSECURE if we use the return value directly + // for buffer allocation (malloc for example). This is not the case. + + FILE * fp = NULL; + +#ifdef _MSC_VER + // Use more Secure C library API + errno_t ret = fopen_s( &fp, file.c_str(), "r" ); + if ( 0 != ret ) + fp = NULL; +#else + // Use older less Secure C library API + fp = fopen( file.c_str(), "r" ); +#endif + + if ( fp != NULL ) + { + if ( 0 == fseek( fp, 0, SEEK_END ) ) + file_size_bytes = (unsigned long long)ftell( fp ); + fclose( fp ); + } + + return file_size_bytes; +} + +/*! +* 1. Function Description: +* For a given directory name, extract all the files from that directory as well as +* from all its sub-directories and store the filenames in the fileList vector. +* Returns: +* 1 if path exists and is a directory +* 0 if not a directory +* -1 if User cancelled Caller MUST check all the return values! +* +* This may be called more than 1 time to accumulate information. +* This is support for building a List of Files for a given Baseline file set. +* +* Pre Conditions: +* Before the first call here ALL of OUT parameters were initialized. +* +* Post Conditions: +* Caller MUST check for User Cancel before normal processing +* +* 2. Parameters: +* folder: IN/OUT folder to list (may have whitespace trimmed) +* fileExtList: IN list of file extensions to search +* fileList: IN/OUT list of files in folder +* symLinks: IN follow Unix links? +* totalFileSizes: IN/OUT sum of all file sizes in Bytes +* largestFileSize: IN/OUT largest found file size in Bytes +* numFiles: IN/OUT number of files in the list +* commonPathPrefix: IN/OUT beginning part of full path that is the same among all files +* +* 3. Creation time and Owner: +* Version 2009.01 +* Revised 2015.12 Get number of files, total file sizes (Bytes) and size of largest file and common Path prefix. +*/ +int CUtil::ListAllFiles(string & folder, + const StringVector &fileExtList, + StringVector &fileList, // Appended to here + const bool symLinks, + unsigned long long & totalFileSizes, // Added to here + unsigned long long & largestFileSize, // Updated here + unsigned long & numFiles, // Incremented here + string & commonPathPrefix ) // Updated here +{ + StringVector tmpList; // Modification: 2011.10 + string file; + size_t i, n; + + folder = CUtil::TrimString(folder); // Modification: 2011.05 + +#ifdef UNIX // Modification: 2011.10 + // skip links if user specified + struct stat inodeData; + if (!symLinks && (lstat(folder.c_str(), &inodeData) < 0 || S_ISLNK(inodeData.st_mode))) + return 0; +#endif + + // process folder + if (!GetFileList(tmpList, folder, symLinks)) + return 0; + unsigned long long fileSizeBytes; + bool extensionFiltering = true; + if ( fileExtList.at(0) == "*.*" || fileExtList.at(0) == "*" ) + extensionFiltering = false; + +#define UI_GET_FILE_LIST_COUNT 200 + unsigned long prev_numFiles = numFiles; + string cntStr; + +#ifndef QTGUI + cout << ".........."; // Set an area to be overwritten +#endif + + int retVal = 1; + + // read through tmpList and get the names of all the files in the directory mentioned + for (n = 0; n < tmpList.size(); n++) + { + file = tmpList.at(n); + + // if no-extension filtering, each file is pushed into the fileList + if ( false == extensionFiltering ) + { + fileList.push_back(file); + fileSizeBytes = GetFileSizeInBytes( file ); + if ( largestFileSize < fileSizeBytes ) + largestFileSize = fileSizeBytes; + totalFileSizes += fileSizeBytes; + if ( 0L == numFiles ) + commonPathPrefix = file; + numFiles++; + FindCommonPrefix( commonPathPrefix, file, commonPathPrefix, false ); + } + else + { + // for each extension, if file extension matches with the extension, the file is pushed into the fileList + for (i = 0; i < fileExtList.size(); i++) + { + if (MatchFilename(ExtractFilename(file), fileExtList.at(i))) + { + fileList.push_back(file); + numFiles++; + fileSizeBytes = GetFileSizeInBytes( file ); + if ( largestFileSize < fileSizeBytes ) + largestFileSize = fileSizeBytes; + totalFileSizes += fileSizeBytes; + if ( 0L == numFiles ) + commonPathPrefix = file; + numFiles++; + FindCommonPrefix( commonPathPrefix, file, commonPathPrefix, false ); + } + } + } + + if ( prev_numFiles + UI_GET_FILE_LIST_COUNT <= numFiles ) + { + #ifndef QTGUI + LongToStr( (long)numFiles, cntStr ); + cout << "\b\b\b\b\b\b\b\b\b\b" << cntStr; // 10 backspace + new count + for ( int i = 0 ; i < 10 - (int)cntStr.size(); i++ ) + cout << " "; // fill at right to be easier next time + cout << flush; + #endif + prev_numFiles = numFiles; + + #ifdef QTGUI + // Check to see if User has cancelled only for Qt + extern bool execCanceled; + if ( true == execCanceled ) + { + retVal = -1; // Caller MUST check for Cancel from this value + printf( "\a" ); // Make a noise + break; + } + #endif + } + } + + // Just erase the last 10 characters + cout << "\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b"; + + tmpList.resize( 0 ); // Modification: 2009.01 + return retVal; +} + +/*! +* 1. Function Description: +* For a given path, this method lists all files, directories and +* sub-directories it contains and stores the filenames in the fileList vector. +* Return path exists and is a directory +* +* 2. Parameters: +* fileList: list of files in folder +* path: folder's path to list +* symLinks: follow Unix links? +* +* 3. Creation time and Owner: +* Version 2011.05 +*/ +bool CUtil::GetFileList(StringVector &fileList, const string &path, bool symLinks) // Modification: 2011.10 +{ + string fullPath; +#ifdef UNIX // Modification: 2011.05 + DIR *dir; + struct dirent *fileRead; + struct stat inodeData; + + // opening the given path + dir = opendir(path.c_str()); + + // If the dir doesn't exist + if (dir == NULL) + return(false); // Modification: 2011.10 + + // each file is processed until the last one + while ((fileRead = readdir(dir)) != NULL) // Modification: 2011.05 + { + // '.' & '..' are omitted + if ((strcmp(fileRead->d_name, ".") != 0) && (strcmp(fileRead->d_name, "..") != 0)) + { + // fullPath contains the path + the file name. + fullPath = path + '/' + fileRead->d_name; + if (symLinks) + { + if (stat(fullPath.c_str(), &inodeData) >= 0) + { + // for each file, store the fullPath into the ofstream + if (!S_ISDIR(inodeData.st_mode)) // Modification: 2011.10 + fileList.push_back(fullPath); + else + { + // for each directory, its file list is obtained + GetFileList(fileList, fullPath, symLinks); + } + } // Modification: 2011.05 + } + else + { + if (lstat(fullPath.c_str(), &inodeData) >= 0) + { + // for each file, store the fullPath into the ofstream + if (!S_ISLNK(inodeData.st_mode)) + { + if (!S_ISDIR(inodeData.st_mode)) + fileList.push_back(fullPath); + else + { + // for each directory, its file list is obtained + GetFileList(fileList, fullPath, symLinks); + } + } + } + } + } + } + // close the directory + closedir(dir); +#else + struct _finddata_t c_file; + ptrdiff_t hFile; + string findPath = path + "\\*.*"; + + // the first file is obtained + hFile = _findfirst(findPath.c_str(), &c_file); + + // If the dir doesn't exist + if (hFile == -1) + return(false); + + // each file is processed until the last one + while (_findnext(hFile, &c_file) == 0) + { + // for each file (not a directory (_A_SUBDIR), store its name into the fileList + fullPath = path + "\\" + c_file.name; + if (!(c_file.attrib & _A_SUBDIR)) // Modification: 2011.10 + fileList.push_back(fullPath); + else if ((strcmp(".", c_file.name) != 0) && (strcmp("..", c_file.name) != 0)) // Modification: 2011.05 + { + // for each directory, except '.' and '..', its file list is obtained + GetFileList(fileList, fullPath, symLinks); + } + } + // close the directory + _findclose(hFile); +#endif + return(true); +} + +/*! +* 1. Function Description: +* For a given filename, this method checks whether the file matches +* a given match string containing wildcards (*) and placeholders (?). +* Return filename matches pattern +* +* 2. Parameters: +* filename: filename to be checked +* matchstr: string pattern to match +* +* 3. Creation time and Owner: +* Version 2011.10 +*/ +bool CUtil::MatchFilename(const string &filename, const string &matchstr) +{ + unsigned int i, j, k, f, m, fl, ml, s, e, sl, lim; + + fl = filename.length(); // Modification: 2013.04 + ml = matchstr.length(); + if (ml == 0) // Modification: 2011.10 + return(fl == 0); + if (fl == 0) + return(false); + + f = 0; + for (m = 0; m < ml; m++) + { + if (matchstr[m] == '?') + { + f++; + if (f > fl) + return(false); + continue; + } + else if (matchstr[m] == '*') + { + // search for next non-wild card character + s = m + 1; + while (s < ml && matchstr[s] == '*') + s++; + if (s >= ml) + break; + e = s + 1; + while (e < ml && matchstr[e] != '*') + e++; + sl = e - s; + lim = fl - sl - f; + if (e >= ml) + { + // check the end of the filename + if (fl - f < sl) + return(false); + for (j = fl - sl, k = s; j < fl; j++, k++) + { + if (matchstr[k] != '?') + { +#ifdef UNIX + // case-sensitive match + if (matchstr[k] != filename[j]) + break; +#else + // TODO: Windows server installs (and perhaps workstations) + // allow Case Sensitive file systems. + // While processing file paths each file system + // can be queried if Case Sensitive. For NOW, do as below + // + // case-insensitive match + if ( lowerChars[ TL_ARR( matchstr[k] ) ] != lowerChars[ TL_ARR( filename[j] ) ] ) + break; +#endif + } + } + return(j >= fl); + } + for (i = 0; i <= lim; i++) + { + for (j = f + i, k = s; j < f + i + sl; j++, k++) + { + if (matchstr[k] != '?') + { +#ifdef UNIX + // case-sensitive match + if (matchstr[k] != filename[j]) + break; +#else + // case-insensitive match + if ( lowerChars[ TL_ARR( matchstr[k] ) ] != lowerChars[ TL_ARR( filename[j] ) ] ) + break; +#endif + } + } + if (j >= f + i + sl) + { + f += i + sl; + break; + } + } + if (i > lim) + return(false); + m = e - 1; + continue; + } +#ifdef UNIX + // case-sensitive match + if (matchstr[m] != filename[f]) + return(false); +#else + // case-insensitive match + if ( lowerChars[ TL_ARR( matchstr[m] ) ] != lowerChars[ TL_ARR( filename[f] ) ] ) + return(false); +#endif + f++; + } + if (f < fl && matchstr[ml - 1] != '*') + return(false); + return(true); +} + +/*! +*1. Function Description: +* For a given path string, whis method replace the path string with +* current file folder path. +* Return file status +* +*2. Parameters: +* path: path string what will be replaced with current path +* +*3. Creation time and Owner: +* Version 2017.02 +* Version 2018.01 +*/ +int CUtil::GetPath(string &path) +{ + char buffer[MAX_PATH_SIZE]; +#ifdef UNIX + if(getcwd(buffer, MAX_PATH_SIZE) == NULL) + { + return 0; + } +#else + if(_getcwd(buffer, MAX_PATH_SIZE) == NULL) + { + return 0; + } +#endif + path.assign(buffer); + return 1; +} + + +/*! +* 1. Function Description: +* For a given path, this method creates the specified directory path +* including all required sub-directories. +* Return file status +* +* 2. Parameters: +* path: path to create +* +* 3. Creation time and Owner: +* Version 2011.05 +*/ +int CUtil::MkPath(const string &path) +{ + size_t i = 1; + string tpath; +#ifdef UNIX + struct stat st; + if (stat(path.c_str(), &st) != 0) + { + while (i < path.size()) + { + if (path[i] == '/') + { + tpath = path.substr(0, i); + if (stat(tpath.c_str(), &st) != 0) + { + if (mkdir(tpath.c_str(), 0777) != 0) + return 0; + } + } + i++; + } + if (mkdir(path.c_str(), 0777) != 0) + return 0; + } + string tempfile = path + "/___temp.dat"; +#else + if (_access(path.c_str(), 0) != 0) + { + while (i < path.size()) + { + if ((path[i] == '\\' || path[i] == '/') && path[i - 1] != ':') // Modification: 2015.12 + { + tpath = path.substr(0, i); + if (_access(tpath.c_str(), 0) != 0) + { + if (_mkdir(tpath.c_str()) != 0) + return 0; + } + } + i++; + } + if (_mkdir(path.c_str()) != 0) + return 0; + } + string tempfile = path + "\\___temp.dat"; +#endif + + // attempt to write a temporary file to the directory + ofstream outfile; + outfile.open(tempfile.c_str(), ofstream::out); + outfile.close(); + if (outfile.fail()) + { + // file could not be opened + return 0; + } + else + { + // delete the temporary file + remove(tempfile.c_str()); + } + return 1; +} + +/*! +* 1. Function Description: +* For a given path, this method delete all directory and its content. +* Return file status +* +* 2. Parameters: +* path: path to delete +* +* 3. Creation time and Owner: +* Version 2017.02 +*/ +int CUtil::RmPath(const string &path) +{ + string tpath; +#ifdef UNIX + DIR *d = opendir(path.c_str()); + size_t path_len = strlen(path.c_str()); + int r = -1; + if (d) + { + struct dirent *p; + r = 0; + while (!r && (p=readdir(d))) + { + int r2 = -1; + char *buf; + size_t len; + /* Skip the names "." and ".." as we don't want to recurse on them. */ + if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..")) + continue; + len = path_len + strlen(p->d_name) + 2; + buf = (char *) malloc(len); + snprintf(buf, len, "%s/%s", path.c_str(), p->d_name); + if (buf) + { + r2 = unlink(buf); + free(buf); + } + r = r2; + } + closedir(d); + } + if (!r) + r = rmdir(path.c_str()); + if (r!=0) + return 0; +#else + struct _finddata_t c_file; + ptrdiff_t hFile; + string findPath = path + "\\*.*"; + + // the first file is obtained + hFile = _findfirst(findPath.c_str(), &c_file); + + // If the dir doesn't exist + if (hFile == -1) + return(false); + + int r = 0; + string fullPath; + // each file is processed until the last one + while (!r && _findnext(hFile, &c_file) == 0) + { + int r2 = -1; + // Skip the names "." and ".." as we don't want to recurse on them + if (!(strcmp(".", c_file.name)) || !(strcmp("..", c_file.name))) + continue; + fullPath = path + "\\" + c_file.name; + + r = _unlink(fullPath.c_str()); + } + // close the directory + _findclose(hFile); + + if (!r) + r = _rmdir(path.c_str()); + if (r!=0) + return 0; +#endif + return 1; +} + +/*! +* 1. Function Description: +* Removes extra text added after @@ by ClearCase. +* The original implementation was provided by NGC. +* Return modified file name +* Set trailer to what is removed to later make original file name +* +* 2. Parameters: +* fileName: file name +* clearCaseTrailer: part of given file name removed here +* +* 3. Creation time and Owner: +* Version 2011.05 +* Revised 2015.12 Sets trailer added by ClearCase to make original name +*/ +string CUtil::ConvertClearCaseFile( const string &fileName, string & clearCaseTrailer ) +{ + clearCaseTrailer = ""; // Modification: 2015.12 + + // remove @@ and anything after + string fileName1 = fileName; // Modification: 2011.10 + size_t loc = fileName1.rfind("@@"); // Modification: 2011.05 + if (loc != string::npos) + { + clearCaseTrailer = fileName1.substr( loc ); // Modification: 2015.12 + return fileName1.erase(loc); + } + + return fileName1; +} + +/*! +* 1. Function Description: +* Checks for line truncation. +* Return size of string to keep after truncation +* +* 2. Parameters: +* length: length of current line +* totalLength: length of current SLOC +* truncate: allowable number of characters per SLOC +* trunc_flag: line truncated? +* +* 3. Creation time and Owner: +* Version 2011.05 +*/ +size_t CUtil::TruncateLine(size_t length, size_t totalLength, size_t truncate, bool &trunc_flag) +{ + if (truncate == 0) + { + trunc_flag = false; + return(length); + } + else if (totalLength >= truncate) + { + trunc_flag = true; + return(0); + } + else if (totalLength + length <= truncate) + { + trunc_flag = false; + return(length); + } + else + { + trunc_flag = true; + return(truncate - totalLength); + } +} + +/*! +* 1. Function Description: +* Clear redundant/unnecessary white spaces in a string. +* Return new string +* +* 2. Parameters: +* str: string to be processed +* +* 3. Creation time and Owner: +* Version 2011.05 +*/ +string CUtil::ClearRedundantSpaces(const string &str) +{ +#define SPECIAL_CHARS " \t;[]()+/-*<>=,&~!^?:%{}|" + size_t len = str.length(); + size_t idx = 0; + size_t idx_new = 0; + string str_new(len, '\0'); + + for (idx = 0; idx < len; idx++) + { + if (str[idx] == ' ' || str[idx] == '\t') // Modification: 2011.10 + { + if (idx == 0 || idx + 1 == len + || strchr(SPECIAL_CHARS, str[idx - 1]) != NULL + || strchr(SPECIAL_CHARS, str[idx + 1]) != NULL) + { + continue; + } + } + if (str[idx] == '\t') // Modification: 2013.04 + str_new[idx_new++] = ' '; + else + str_new[idx_new++] = str[idx]; + } + str_new.resize(idx_new); // Modification: 2011.05 + + return str_new; +#undef SPECIAL_CHARS +} + +/*! +* 1. Function Description: +* Returns a string without smart quotes. +* Return updated string +* +* 2. Parameters: +* str: original string +* +* 3. Creation time and Owner: +* Version 2011.10 +* Revised 2015.12 Compiles clean with MS Visual C++ -Wall +*/ + +// MAke the change to do the below in-place +string CUtil::ReplaceSmartQuotes( const string &str1) +{ + string str = str1; + std::replace( str.begin(), str.end(), static_cast(static_cast(145)), '\'' ); + std::replace( str.begin(), str.end(), static_cast(static_cast(146)), '\'' ); + std::replace( str.begin(), str.end(), static_cast(static_cast(147)), '\"' ); + std::replace( str.begin(), str.end(), static_cast(static_cast(148)), '\"' ); + return str; +} + + +/*! +* 1. Function Description: +* Checks whether the string starts with a specified string. +* Return true if the character sequence represented by startsWith is a prefix of the substring of str +* +* 2. Parameters: +* str: original string +* startsWith: check string +* case_sensitive: is case sensitive? +* +* 3. Creation time and Owner: +* Version 2015.12 +*/ +bool CUtil::StartsWith(const string &str, const string &startsWith, bool case_sensitive) +{ + size_t strlen = str.length(); + size_t startslen = startsWith.length(); + + if (strlen < 1 || strlen < startslen) + return false; + else if (startslen < 1) + return true; + + string str1 = str; + string startsWith1 = startsWith; + if (!case_sensitive) + { + str1 = CUtil::ToLower(str1); + startsWith1 = CUtil::ToLower(startsWith1); + } + return (str1.substr(0, startslen) == startsWith1) ? true : false; +} + +/*! +* 1. Function Description: +* Checks whether the string ends with a specified string. +* Return true if the character sequence represented by endsWith is a suffix of the substring of str +* +* 2. Parameters: +* str: original string +* startsWith: check string +* case_sensitive: is case sensitive? +* +* 3. Creation time and Owner: +* Version 2015.12 +*/ +bool CUtil::EndsWith(const string &str, const string &endsWith, bool case_sensitive) +{ + size_t strlen = str.length(); + size_t endslen = endsWith.length(); + + if (strlen < 1 || strlen < endslen) + return false; + else if (endslen < 1) + return true; + + string str1 = str; + string endsWith1 = endsWith; + if (!case_sensitive) + { + str1 = CUtil::ToLower(str1); + endsWith1 = CUtil::ToLower(endsWith1); + } + return (str1.substr(strlen - endslen) == endsWith1) ? true : false; +} + +/*! +* 1. Function Description: +* Splits a string by a specified delimiter. +* Return string vector of tokens or if there is nothing to split or delimiter is empty, the original string is returned as a token and is the only token +* +* 2. Parameters: +* stringToSplit: string to split +* delimiter: string delimiter +* +* 3. Creation time and Owner: +* Version 2015.12 +*/ +StringVector CUtil::Split(const string &stringToSplit, const string &delimiter) +{ + StringVector tokens; // tokens contain 1 or more strings that have been split + size_t start = 0, end = 0; + + if (delimiter.empty()) + { + tokens.push_back(stringToSplit); + return tokens; + } + + while (end != string::npos) + { + end = stringToSplit.find(delimiter, start); + + // if at end, use length=maxLength, else use length=end-start + string token = stringToSplit.substr(start, (end == string::npos) ? string::npos : end - start); + if (token.length() > 0) + tokens.push_back(token); + + // if at end, use start=maxSize, else use start=end+delimiter + start = (end > (string::npos - delimiter.size())) ? string::npos : end + delimiter.size(); + } + if (tokens.size() < 1) + tokens.push_back(stringToSplit); // if delimiter not found, return stringToSplit + + return tokens; +} + +/*! +* 1. Function Description: +* Replaces all instances of string within a string. +* Return new string +* +* 2. Parameters: +* originalStr: original string +* toBeReplaced: string to be replaced +* replaceWith: string to replace with +* +* 3. Creation time and Owner: +* Version 2015.12 +*/ +string CUtil::ReplaceWith(const string &originalStr, const string &toBeReplaced, const string &replaceWith) +{ + string str = originalStr; + size_t i = str.find(toBeReplaced); + while (i != string::npos) + { + str.replace(i, toBeReplaced.length(), replaceWith); + i = str.find(toBeReplaced); + } + return str; +} + +//#ifndef NO32BIT // NOT USED +/*! +* 1. Function Description: +* Generates an almost-unique 256-bit (32-byte) hash signature for a text string (SHA-256). +* Return 256-bit hash +* +* 2. Parameters: +* fileBuffer: buffer containing file text (512-bit chunks) +* +* 3. Creation time and Owner: +* Version 2015.12 +*/ +/************ NOT USED +string CHash::CalcSHA256(vector > fileBuffer) +{ +// SHA-256 +// 1) All variables are 32-bit unsigned integers and addition is calculated modulo 2^32 +// 2) For each round, there is one round constant k[i] and one entry in the message +// schedule array w[i], 0 <= i <= 63 +// 3) The compression function uses 8 working variables, a through h +// 4) Big-endian convention is used when expressing the constants, +// and when parsing message block data from bytes to words, for example, +// the first word of the input message "abc" after padding is 0x61626380 + +// Based on the pseudocode for SHA-256 found at +// http://en.wikipedia.org/wiki/SHA-2 + +// Information on secure hash standards found at +// http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf + +// More information on SHA-256 found at +// http://www.movable-type.co.uk/scripts/sha256.html +// + + // initialize hash values + // (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19): + uint32_t h0 = 0x6a09e667; + uint32_t h1 = 0xbb67ae85; + uint32_t h2 = 0x3c6ef372; + uint32_t h3 = 0xa54ff53a; + uint32_t h4 = 0x510e527f; + uint32_t h5 = 0x9b05688c; + uint32_t h6 = 0x1f83d9ab; + uint32_t h7 = 0x5be0cd19; + + // initialize array of round constants + // (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311): + const uint32_t k[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; + + uint32_t a, b, c, d, e, f, g, h; + uint32_t s0, s1, temp1, temp2, ch, maj; + uint32_t w[64]; + size_t L, K, TL; + + int si; + size_t i, j, l; + unsigned char c0 = 0x00; + unsigned char c1 = 0x80; + unsigned char cb; + stringstream st_h0, st_h1, st_h2, st_h3, st_h4, st_h5, st_h6, st_h7; + string hash; + + if (fileBuffer.size() < 1) + fileBuffer.push_back(vector()); + + // preprocessing + L = fileBuffer[fileBuffer.size() - 1].size() * 8; + TL = L + ((fileBuffer.size() - 1) * 512); + if (L >= 448) + { + // append the bit '1' to the message (8-bit char with padded '0' = 0x80) + if (L < 512) + fileBuffer[fileBuffer.size() - 1].push_back(c1); + + // append '0' bytes to the message until the end + if (L < 511) + { + for (i = L + 8; i < 512; i += 8) + fileBuffer[fileBuffer.size() - 1].push_back(c0); + } + + // append another 512-bit buffer to hold the message length + fileBuffer.push_back(vector()); + } + + // append the bit '1' to the message (8-bit char with padded '0' = 0x80) if not done already + if (L < 448 || L == 512) + fileBuffer[fileBuffer.size() - 1].push_back(c1); + + // append '0' bytes to the message until the end + L = fileBuffer[fileBuffer.size() - 1].size() * 8; // includes byte 0x80 + K = 448 - L; + i = 0; + while (i < K) + { + fileBuffer[fileBuffer.size() - 1].push_back(c0); + i += 8; + } + + // append length of message (without the '1' bit or padding), in bits, as 64-bit big-endian integer + int sizeT_size = sizeof( size_t ); // declaring this variable avoids constant expression warning + if ( sizeT_size >= 8 ) + { + for (si = 7; si >= 0; si--) + { + cb = (TL >> si * 8) & 0xff; + fileBuffer[fileBuffer.size() - 1].push_back(cb); + } + } + else + { + for (si = 3; si >= 0; si--) + { + cb = TL & 0x00; + fileBuffer[fileBuffer.size() - 1].push_back(cb); + } + for (si = 3; si >= 0; si--) + { + cb = (TL >> si * 8) & 0xff; + fileBuffer[fileBuffer.size() - 1].push_back(cb); + } + } + + // process the message in successive 512-bit chunks + for (i = 0; i < fileBuffer.size(); i++) + { + // the initial values in w[0..63] don't matter, so zero them here + for (j = 0; j < 64; j++) + w[j] = 0; + + // break chunk into sixteen 32-bit big-endian words w[0..15] + for (j = 0, l = 0; j < 64; j += 4, l++) + w[l] = (uint32_t)(fileBuffer[i][j] << 24 | fileBuffer[i][j + 1] << 16 | fileBuffer[i][j + 2] << 8 | fileBuffer[i][j + 3]); + + // extend the sixteen 32-bit words into sixty-four 32-bit words + for (j = 16; j < 64; j++) + { + s0 = RotateRight32Bit(w[j - 15], 7) ^ RotateRight32Bit(w[j - 15], 18) ^ ShiftRight32Bit(w[j - 15], 3); + s1 = RotateRight32Bit(w[j - 2], 17) ^ RotateRight32Bit(w[j - 2], 19) ^ ShiftRight32Bit(w[j - 2], 10); + w[j] = (w[j - 16] + s0 + w[j - 7] + s1) & 0xffffffff; + } + + // initialize hash value for this chunk + a = h0; + b = h1; + c = h2; + d = h3; + e = h4; + f = h5; + g = h6; + h = h7; + + // main loop + for (j = 0; j < 64; j++) + { + s1 = RotateRight32Bit(e, 6) ^ RotateRight32Bit(e, 11) ^ RotateRight32Bit(e, 25); + ch = (e & f) ^ ((~e) & g); + temp1 = h + s1 + ch + k[j] + w[j]; + s0 = RotateRight32Bit(a, 2) ^ RotateRight32Bit(a, 13) ^ RotateRight32Bit(a, 22); + maj = (a & b) ^ (a & c) ^ (b & c); + temp2 = s0 + maj; + + h = g; + g = f; + f = e; + e = (d + temp1) & 0xffffffff; + d = c; + c = b; + b = a; + a = (temp1 + temp2) & 0xffffffff; + } + + // add this chunk's hash to result so far + h0 = (h0 + a) & 0xffffffff; + h1 = (h1 + b) & 0xffffffff; + h2 = (h2 + c) & 0xffffffff; + h3 = (h3 + d) & 0xffffffff; + h4 = (h4 + e) & 0xffffffff; + h5 = (h5 + f) & 0xffffffff; + h6 = (h6 + g) & 0xffffffff; + h7 = (h7 + h) & 0xffffffff; + } + + // produce the final hash value (big-endian) + st_h0 << hex << h0; + st_h1 << hex << h1; + st_h2 << hex << h2; + st_h3 << hex << h3; + st_h4 << hex << h4; + st_h5 << hex << h5; + st_h6 << hex << h6; + st_h7 << hex << h7; + hash = st_h0.str() + st_h1.str() + st_h2.str() + st_h3.str() + st_h4.str() + st_h5.str() + st_h6.str() + st_h7.str(); + + return hash; +} +********* END OF NOT USED *******/ +//#endif // NOT USED + +int CUtil::CountNestedNum(string &combine){ + //"a==1&&b==1 return 2" + int res=0; + for( unsigned int i = 0; i < combine.size(); i++ ) + { + if(combine[i]=='&') + res++; + } + return res/2+1; +} + +vector CUtil::SplitByConcat(string &statement, string concat){ + vector result; + string temp_statement = statement; + unsigned int idx; + string left; + string right; + while(temp_statement.find(concat) != string::npos){ + idx = temp_statement.find(concat); + left = temp_statement.substr(0, idx); + right = temp_statement.substr(idx + concat.length(), string::npos); + result.push_back(left); + temp_statement = temp_statement.substr(idx + concat.length(), string::npos); + } + result.push_back(right); + return result; +} + +size_t CUtil::ConcatAndDup(string &cc4_valid_if, set &nested_set){ + set::iterator it; + string temp; + if(nested_set.count(cc4_valid_if) == 1){ + return (size_t)CountNestedNum( cc4_valid_if ); + } + + for(it=nested_set.begin(); it!=nested_set.end(); it++){ + temp= *it; + if(temp.find(cc4_valid_if)!= string::npos) + return (size_t)CountNestedNum( cc4_valid_if ); + if(cc4_valid_if.find(temp)!=string::npos) + return (size_t)CountNestedNum( temp ); + } + + return 0; +} + +void CUtil::ConcatOrReorder(set &string_set, string &statement){ + + if(statement.find("||")==string::npos) + return; + vector original = SplitByConcat(statement, "||"); + //make unique + sort( original.begin(), original.end() ); + original.erase( unique( original.begin(), original.end() ), original.end() ); + + vector permute; + string output; + if( original.size() <= 1 ) //no || in statement + return; + + set visited; + ConcatOrReorderDFS( original, 0, visited, output, permute ); + for( unsigned int i = 0; i < permute.size(); i++ ) + { + if ( string_set.count( permute[i] ) == 1 ) + { + statement = permute[i]; + return; + } + } + statement=permute[0]; + return; +} + +/*! +* 1. Function Description: +* This takes a DFS (whatever that is) and does a concat(enation) or Reorder +* Strongly suggest someone review these comments and improve. +* +* 2. Parameters: +* original IN/OUT reference to a string holding some DFS stuff ? +* step IN integer representing some aspect of some DFS Reorder or Concat ? +* visited IN/OUT integers representing the visited states ? +* output IN/OUT result of ? +* permute IN/OUT some change to do or that was done ? +* +* 3. Creation time and Owner: +* Version ????.?? +* Revused 2015.12 Removed recursion to decrease use of RAM memory and run faster! +*/ + +struct concatOrReorderDFSCallStruct +{ + int step; // - parameter input + unsigned int i; // - local variable that will be used + // after returning from the function call + int place; // - Since there is process needed to be done + // after recursive call. (Sixth rule) +}; + +void CUtil::ConcatOrReorderDFS( vector &original, const int step, set &visited, string &output, vector &permute ) +{ +// NEW Approach without Recursion. NEEDS more work! + + concatOrReorderDFSCallStruct currentCall; + currentCall.step = step; // Start with given value + currentCall.i = 0; + currentCall.place = 0; // Start with the code before the first simulated return + + // Stack to handle int step value that changes. Other args are references and not needed in stack. + stack noRecursionStk; + noRecursionStk.push( currentCall ); // Save initial value + + while ( !noRecursionStk.empty() ) + { + // Entry point from either outside initial call + // or inside simulated recursive calls + currentCall = noRecursionStk.top(); + noRecursionStk.pop(); + switch( currentCall.place ) + { + default: + case 0: + if ( currentCall.step == (int)original.size() ) + { + permute.push_back( output.substr( 2, string::npos ) ); + + // Simulate a return + // return; + continue; + } + // Allow to fall through + case 1: + loopTop: + for ( unsigned int i = currentCall.i; i < original.size(); i++ ) + { + if( visited.find( (int)i ) == visited.end() ) + { + visited.insert( i ); + output = output + "||" + original[i]; + + // Simulate a recursive call + // ConcatOrReorderDFS( original, step + 1, visited, output, permute ); + // + // currentCall needed after returning from the recursive call + currentCall.place = 2; + currentCall.i = i; + noRecursionStk.push( currentCall ); + + // Create a new struct for calling itself + concatOrReorderDFSCallStruct newCallStruct; + + // Set parameters as when calling itself + newCallStruct.step = currentCall.step + 1; + + // Set values as initial values since it will start from the + // beginning of the function (while loop) + newCallStruct.i = 0; + newCallStruct.place = 0; // give the initial place + + noRecursionStk.push( newCallStruct ); + continue; // to while loop to simulate the recursive call + + // These are done below as case 2 + // output.resize( output.size() - original[i].size() - 2 ); + // visited.erase( (int)i ); + } + } + break; + case 2: + // Do code after simulated recursive call + + output.resize( output.size() - original[ currentCall.i ].size() - 2 ); + visited.erase( (int)currentCall.i ); + + // Must increment currentCall.i as coming from top of loop, not inside anymore + currentCall.i++; + goto loopTop; + break; + } + } + + return; // Really done here... + +/* + // RECURSIVE and Hostile to RAM and SLOWER version + if( step == (int)(original.size()) ) + { + permute.push_back( output.substr( 2, string::npos ) ); + return; + } + for( unsigned int i = 0; i < original.size(); i++ ) + { + if( visited.find( (int)i ) == visited.end() ) + { + visited.insert( i ); + output = output + "||" + original[i]; + + ConcatOrReorderDFS( original, step + 1, visited, output, permute ); + + output.resize( output.size() - original[i].size() - 2 ); + visited.erase( (int)i ); + } + } + return; +*/ +} + +size_t CUtil::NestedIfDup(string &cc4_valid_if, stack &cc4_parent_stack, stack > &cyclomatic_distinct_cond_stack, set &nested_set){ + + if(cyclomatic_distinct_cond_stack.size()==0 || cc4_parent_stack.size()==0){ //fix a bug that sometimes the stack may be empty (CC4 Integration 2015 Summer) + return 0; + } + + //cc4_valid_if && cc4_parent_stack.top() + set temp_set = cyclomatic_distinct_cond_stack.top(); + set::iterator it; + string parent = cc4_parent_stack.top(); + size_t dup_counter=0; + + if(cyclomatic_distinct_cond_stack.size()==1){ + string combine = parent + "&&" + cc4_valid_if; + + if(nested_set.find(combine)!=nested_set.end()){ + AddTraceLine(__LINE__, UCC_FUNC_NAME, "CC4: already counted the dup"); + return 0; + + } + nested_set.insert(combine); + std::ostringstream ss; + ss << "CC4: insert " << combine << " to nested_set"; + AddTraceLine(__LINE__, UCC_FUNC_NAME, ss.str()); + + if(temp_set.find(combine)!=temp_set.end()){ + //can find a && dup, counter && number + return (size_t)CountNestedNum( combine ); + } + return 0; + + + } + + cyclomatic_distinct_cond_stack.pop(); + cc4_parent_stack.pop(); + string new_cc4_valid_if = parent+"&&"+cc4_valid_if; + + // RECURSIVE CALL... What were you THINKING ! ! ! + dup_counter = NestedIfDup( new_cc4_valid_if, cc4_parent_stack, cyclomatic_distinct_cond_stack, nested_set ); + + cyclomatic_distinct_cond_stack.push(temp_set); + cc4_parent_stack.push(parent); + + return dup_counter; +} + + +// Below is an offensively RECURSIVE implementation +// +// Of couse no meaningful comments to start with (my code is so great you will enjoy reading EVERY line) +void CUtil::SemanticFormat(string &statement) +{ + size_t idx; + string left; + string right; + string eq = "==", ne = "!=", eqT = "==1", eqF = "==0", concat_op_and = "&&", concat_op_or="||"; + + // Modification: Spring 2016 - Below variables are changed from 'unsigned int' to 'size_t' to + // avoid warningi(comparisonof constant and unsigned int is always true) on clang++ compiler. + size_t eq_len = eq.length(); + size_t ne_len = ne.length(); + size_t eq_pos = (size_t)(-1), ne_pos = (size_t)(-1); + size_t tnum_len = string("1").length(); + size_t tstr_len = string("true").length(); + size_t fnum_len = string("0").length(); + size_t fstr_len = string("false").length(); + + statement.erase(remove_if(statement.begin(), statement.end(), ::isspace), statement.end()); + + // For loop handling: Currently "for" keyword is searched and upon encounter, is stripped from the statement + // and the rest of the statement is recorded as a condition. We need to record specific condition within a + // for loop, not the entire statement. + if(statement.find(";", 0) != string::npos) { + unsigned int firstSC = statement.find(";", 0); + if(statement.find(";", firstSC + 1) != string::npos) { + unsigned int secondSC = statement.find(";", firstSC + 1); + if(firstSC != secondSC + 1) { + string condFOR = statement.substr(firstSC + 1, secondSC - firstSC - 1); + //distinct_cond_set.erase(it); + //it = distinct_cond_set.find(temp); + statement = condFOR; + //distinct_cond_set.insert(condFOR); + } + } + } + + if ( statement.size() == 0 ) // Modified: 2015.12 + return; + + if(statement.find(concat_op_and) == string::npos && statement.find(concat_op_or) == string::npos){ + + if(statement[0]=='!'){ + eq_pos = statement.find(eq); + ne_pos = statement.find(ne); + if( eq_pos != (string::npos) ) // Modification: Spring 2016 - npos is of type size_t + { + if(statement.substr(eq_pos + eq_len, tnum_len) == "1" || statement.substr(eq_pos + eq_len, tstr_len) == "true") { + statement = statement.substr(1, string::npos) += eqF; + } else if(statement.substr(eq_pos + eq_len, fnum_len) == "0" || statement.substr(eq_pos + eq_len, fstr_len) == "false") { + statement = statement.substr(1, string::npos) += eqT; + } + } + else if( ne_pos != (string::npos) ) // Modification: Spring 2016 - npos is of type size_t + { + if(statement.substr(ne_pos + ne_len, tnum_len) == "1" || statement.substr(ne_pos + ne_len, tstr_len) == "true") { + statement = statement.substr(1, string::npos) += eqT; + } else if(statement.substr(ne_pos + ne_len, fnum_len) == "0" || statement.substr(ne_pos + ne_len, fstr_len) == "false") { + statement = statement.substr(1, string::npos) += eqF; + } + } else { + statement = statement.substr(1, string::npos) += eqF; + } + return; + } + + if (statement.find(eq) == string::npos && statement.find(ne) == string::npos){ + statement = statement + eqT; + return ; + } + + idx = statement.find(eq); + if (idx != string::npos){ + + left = statement.substr(0, idx); + right = statement.substr(idx + eq_len, string::npos); + + if(left=="true" || left=="1") { + statement = right + eqT; + } else if(right=="true" || right=="1") { + statement = left + eqT; + } + if(left=="false" || left=="0") { + statement = right + eqF; + } else if(right=="false" || right=="0"){ + statement = left + eqF; + } + return; + } + + idx = statement.find(ne); + if (idx != string::npos){ + + left = statement.substr(0, idx); + right = statement.substr(idx + ne_len, string::npos); + + if(left=="true" || left=="1") { + statement = right + eqF; + } else if (right=="true" || right=="1") { + statement = left + eqF; + } + if(left=="false" || left=="0") { + statement = right + eqT; + } else if (right=="false" || right=="0"){ + statement = left + eqT; + } + return; + } + + }else{ + // for && + if(statement.find(concat_op_and) != string::npos){ + string temp_statement = statement; + statement = ""; + while(temp_statement.find(concat_op_and) != string::npos){ + idx = temp_statement.find(concat_op_and); + left = temp_statement.substr(0, idx); + right = temp_statement.substr(idx + concat_op_and.length(), string::npos); + + + SemanticFormat(left); + + statement= statement + left + concat_op_and; + temp_statement = temp_statement.substr(idx + concat_op_and.length(), string::npos); + } + + + SemanticFormat(right); + + statement = statement + right; + } + + // for || + if(statement.find(concat_op_or) != string::npos){ + string temp_statement = statement; + statement = ""; + while(temp_statement.find(concat_op_or) != string::npos){ + idx = temp_statement.find(concat_op_or); + left = temp_statement.substr(0, idx); + right = temp_statement.substr(idx + concat_op_or.length(), string::npos); + + + SemanticFormat(left); + + statement= statement + left + concat_op_or; + temp_statement = temp_statement.substr(idx + concat_op_or.length(), string::npos); + } + + + SemanticFormat(right); + + statement = statement + right; + } + + return; + } +} + +size_t CUtil::SemanticDeduplicate(string &cc4_valid_if, stack &cc4_parent_stack, stack > &cyclomatic_distinct_cond_stack, set &nested_set) +{ + if(cc4_valid_if!=""){ + std::ostringstream ss; + + ss << "CC4: cc4_valid_if " << cc4_valid_if; + AddTraceLine(__LINE__, UCC_FUNC_NAME, ss.str()); + ss.str(""); + ss.clear(); + if(cc4_valid_if.find("&&") == string::npos){ + if(cc4_parent_stack.size()!=0){ + // && comes first and nested if comes later. + return CUtil::NestedIfDup(cc4_valid_if, cc4_parent_stack, cyclomatic_distinct_cond_stack, nested_set); + } + }else{ + if(cc4_valid_if.find("&&") != string::npos){ + ss << "CC4: get&&: " << cc4_valid_if; + AddTraceLine(__LINE__, UCC_FUNC_NAME, ss.str()); + // this is the case that nested comes first and && comes later + return CUtil::ConcatAndDup(cc4_valid_if, nested_set); + } + } + } + return 0; +} + +void CUtil::CountDistinctCond(string &valid_statement, const string &base, StringVector &container, unsigned int &count, int mode, const string &exclude, + const string &include1, const string &include2, set &distinct_cond_set, UIntVector* counter_container, bool case_sensitive) +{ + string::size_type idx, left_bracket_idx; + string base1; + StringVector::iterator vit; + UIntVector::iterator cit; + unsigned int single_count = 0; + base1 = " " + base + " "; + valid_statement=""; + + + if (counter_container) + cit = counter_container->begin(); + + if (case_sensitive == false) + { + base1 = CUtil::ToLower(base1); + for (vit = container.begin(); vit != container.end(); vit++) + (*vit) = CUtil::ToLower((*vit)); + } + + unsigned int index = (unsigned int)(-1); // keep unsigned to match length and ... + + if (mode == 1) + { + // exclude mode + for (vit = container.begin(); vit != container.end(); vit++) + { + idx = base1.find((*vit)); + while (idx != string::npos) + { + if ((exclude.find(base1[idx+((*vit).size())]) == string::npos) && + (exclude.find(base1[idx-1]) == string::npos)) + { + count++; + single_count++; + left_bracket_idx = base1.find("(", idx); + if (left_bracket_idx != string::npos) + { + index = left_bracket_idx + 1; + int level = 1; + while(index < base1.length() && level > 0) + { + if (base1[index] == '(') ++level; + if (base1[index] == ')') --level; + index++; + } + if (index < base1.length()){ + + string temp = base1.substr(left_bracket_idx+1, index - left_bracket_idx-2); + if ( temp.size() > 0 ) + { + CUtil::SemanticFormat( temp ); + + CUtil::ConcatOrReorder( distinct_cond_set, temp ); // only check order + distinct_cond_set.insert( temp ); + valid_statement = temp; + } + } + } + else + { + index = idx + (*vit).size(); + } + } + else + { + index = idx + (*vit).size(); + } + idx = base1.find((*vit), index); + } + if (counter_container) + { + (*cit) += single_count; + single_count = 0; + cit++; + } + } + } + else if (mode == 2) + { + // include mode + for (vit = container.begin(); vit != container.end(); vit++) + { + idx = base1.find((*vit)); + while (idx != string::npos) + { + if ((include1.find(base1[idx-1]) != string::npos) && + (include2.find(base1[idx+((*vit).size())]) != string::npos)) + count++; + idx = base1.find((*vit), idx + ((*vit).size())); + } + } + } +} diff --git a/src/CUtil.h b/src/CUtil.h new file mode 100644 index 0000000..8bfd1b3 --- /dev/null +++ b/src/CUtil.h @@ -0,0 +1,176 @@ +//! Utility class definition for static methods. +/*! +* \file CUtil.h +* +* This file contains the utility class definition for static methods. +*/ + +#ifndef CUTIL_H +#define CUTIL_H + +#include "cc_main.h" + +using namespace std; +#define INVALID_POSITION ((unsigned int)-1) +#define TO_END_OF_STRING INVALID_POSITION + +//Modified: 2017.02 Max path size of a directory +const int MAX_PATH_SIZE = 100000; + +/* Modification: 2015.09.27 +* by Randy Maxwell +* Fix for Compile error(s) on older compilers when __func__ is not declared. */ +// Set up a cross compiler define to support getting local function (procedure) name. +// First check for old versions of Microsoft compilers +#ifdef _MSC_VER +// See Microsoft site: Predefined Macros and then see when __func__ becomes supported. +// See StackOverflow: How to Detect if I'm Compiling Code With Visual Studio 2008? + #if _MSC_VER < 1800 + // Old Microsoft compiler + #define UCC_FUNC_NAME (__FUNCTION__) + #else + // Newer compiler + #define UCC_FUNC_NAME (__func__) + #endif +#endif + +#ifndef UCC_FUNC_NAME + // Check for old versions of GCC + // TODO: Somebody add this if needed + + // Use __func__ This assumes compiler supports C++ 2011 or later? + #define UCC_FUNC_NAME (__func__) +#endif + +//! Vector containing a list of strings. +/*! +* \typedef vectorString +* +* Defines a vector containing a list of strings. +*/ +typedef vector vectorString; +/*Modification: 11.2016 Ext-4 Starts*/ +static std::map > dict; +static std::map > dictCopy; +/*Modification: 11.2016 Ext-4 End*/ + +// +// These are utilities that must NOT be inside the CUtil class +// +void IntToStr( const int val, string & result ); +void LongToStr( const long val, string & result ); +void LongLongToStr( const long long val, string & result ); + +// Convert a float to a std::string with a number of decimal places +void FloatToStr( const float val, string & result, const unsigned int dec_precision = 1 ); + +void CountPhysicalFiles( SourceFileList & fileList, unsigned long & phyFileCount ); + +void ClearSummaryMsgCounts(); + +// Time message helpers +// +// Initialize Time values of various processing steps. + + + +void InitTimes(); +#define MSG_STR_BUF_SIZE 512 +void SavePerformanceStats( const string time_str ); +void TimeMsg( char *pBuf, string & time_str, const char * before, + const long count, const char * after, const bool fixed_width = true ); +void ShowTiming( string & time_str, + const time_t timeStart, const time_t timeEnd, + const bool show_total_only, + const bool doDiff, const bool doDups ); + +// Helper to make lowerChars array access reliable +#define TL_ARR(x) ((unsigned int)((unsigned char)(x))) + +//! Utility class. +/*! +* \class CUtil +* +* Defines a utility class. +*/ +class CUtil +{ +public: + /*Modification: 11.2016 Ext-4 Starts*/ + static void allocate(); + static StringVector getExtensionsToLanguage(string lang, StringVector fileExtension); + /*Modification: 11.2016 Ext-4 ends*/ + + static string TrimString(const string &str, int mode = 0); + static string EraseString(const string &srcstr, const string &erasedstr); + + // InitToLower MUST be called before calling ToLower + static void InitToLower(); // Modification: 2015.12 + static string ToLower(const string &string_to_lower); + + static bool CheckBlank(const string &str); + static bool IsInteger(const string &str); + static size_t FindStringsCaseInsensitive(const string &target, map &table, size_t &pos, size_t preLang = INVALID_POSITION); + static size_t FindCharAvoidEscape(const string &source, char target, size_t start_idx, char escape); + static void FindCommonPrefix( string & str, string & str2, string & prefix, const bool remove = false ); + static void FindCommonSuffix( string & str, string & str2, string & suffix, const bool remove = false ); + static size_t FindKeyword(const string &str, const string &keyword, size_t start = 0, size_t end = TO_END_OF_STRING, bool case_sensitive = true); + static void CountTally(const string &base, StringVector &container, unsigned int &count, int mode, const string &exclude, + const string &include1, const string &include2, UIntVector *counter_container = 0, bool case_sensitive = true); + + static vector SplitByConcat(string &statement, string concat); + static int CountNestedNum(string &combine); + static size_t SemanticDeduplicate(string &cc4_valid_if, stack &cc4_parent_stack, stack > &cyclomatic_distinct_cond_stack, set &nested_set); + static void SemanticFormat(string &statement); + static size_t NestedIfDup(string &cc4_valid_if, stack &cc4_parent_stack, stack > &cyclomatic_distinct_cond_stack, set &nested_set); + static size_t ConcatAndDup(string &cc4_valid_if, set &nested_set); + static void ConcatOrReorderDFS(vector &original, int step, set &visited, string &output, vector &permute); + static void ConcatOrReorder(set &string_set, string &statement); + static void CountDistinctCond(string &valid_statement, const string &base, StringVector &constainer, unsigned int &count, int mode, const string &exclude, const string &include1, const string &include2, set &set, UIntVector *counter_container = 0, bool case_sensitive = true); + + static string ExtractFilename(const string &filepath); + static string ExtractFilepath(const string &filepath); + + // Helper used for smarter Estimates to help with RAM usage. + static unsigned long long GetFileSizeInBytes( const string file ); + + // Build a List of Files; of given file Extension(s) starting from a Directory; maybe follow symbolic links. + // Update number of files, largest file size, total files size, common Path prefix shared by the files. + static int ListAllFiles(string & folder, const StringVector &fileExtList, StringVector &fileList, const bool symLinks, + unsigned long long & totalFileSizes, unsigned long long & largestFileSize, + unsigned long & numFiles, string & commonPathPrefix ); + static bool GetFileList(StringVector &fileList, const string &path, bool symLinks); + static bool MatchFilename(const string &filename, const string &matchStr); + //Modification: 2017.02 + static int GetPath(string &path); + static int MkPath(const string &path); + // Modification: 2017.02 + static int RmPath(const string &path); + static string ConvertClearCaseFile( const string &fileName, string & clearCaseTrailer ); + static size_t TruncateLine(size_t length, size_t totalLength, size_t truncate, bool &trunc_flag); + static string ClearRedundantSpaces(const string &str); + static string ReplaceSmartQuotes(const string &str); + static bool StartsWith(const string &str, const string &startsWith, bool case_sensitive = true); + static bool EndsWith(const string &str, const string &endsWith, bool case_sensitive = true); + static StringVector Split(const string &stringToSplit, const string &delimiter); + static string ReplaceWith(const string &originalStr, const string &toBeReplaced, const string &replaceWith); +}; + +#ifndef NO32BIT +//! Hash generation class. +/*! +* \class CHash +* +* Defines a hash generation class. +*/ +class CHash +{ +public: + static string CalcSHA256(vector > fileBuffer); +}; +#endif + +#endif +#ifdef _MSC_VER +#define __func__ __FUNCTION__ +#endif diff --git a/src/CVHDLCounter.cpp b/src/CVHDLCounter.cpp new file mode 100644 index 0000000..607eea2 --- /dev/null +++ b/src/CVHDLCounter.cpp @@ -0,0 +1,1776 @@ +//! Code counter class methods for the VHDL language. +/*! +* \file CVHDLCounter.cpp +* +* This file contains the code counter class methods for the VHDL hardware definition language (used in FPGA programming). +*/ + +#include "CVHDLCounter.h" +#include + +/*! +* Constructs a CVHDLCounter object. +*/ +CVHDLCounter::CVHDLCounter() +{ + classtype = VHDL; + language_name = "VHDL"; + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("VHDL", file_extension); + /*file_extension.push_back(".vhd"); + file_extension.push_back(".vhdl");*/ + + LineCommentStart.push_back("--"); + QuoteStart = "\""; + QuoteEnd = "\""; + QuoteEscapeFront = '\"'; + + directive.push_back("pragma"); + + data_name_list.push_back("access"); + data_name_list.push_back("alias"); + data_name_list.push_back("attribute"); + data_name_list.push_back("buffer"); + data_name_list.push_back("bus"); + data_name_list.push_back("constant"); + data_name_list.push_back("file"); + data_name_list.push_back("generic"); + data_name_list.push_back("group"); + data_name_list.push_back("label"); + data_name_list.push_back("linkage"); + data_name_list.push_back("literal"); + data_name_list.push_back("new"); + data_name_list.push_back("range"); + data_name_list.push_back("record"); + data_name_list.push_back("register"); + data_name_list.push_back("shared"); + data_name_list.push_back("signal"); + data_name_list.push_back("subtype"); + data_name_list.push_back("type"); + data_name_list.push_back("units"); + data_name_list.push_back("variable"); + + exec_name_list.push_back("after"); + exec_name_list.push_back("architecture"); + exec_name_list.push_back("assert"); + exec_name_list.push_back("begin"); + exec_name_list.push_back("block"); + exec_name_list.push_back("body"); + exec_name_list.push_back("case"); + exec_name_list.push_back("component"); + exec_name_list.push_back("configuration"); + exec_name_list.push_back("disconnect"); + exec_name_list.push_back("else"); + exec_name_list.push_back("elsif"); + exec_name_list.push_back("end"); + exec_name_list.push_back("entity"); + exec_name_list.push_back("exit"); + exec_name_list.push_back("for"); + exec_name_list.push_back("function"); + exec_name_list.push_back("generate"); + exec_name_list.push_back("if"); + exec_name_list.push_back("inertial"); + exec_name_list.push_back("library"); + exec_name_list.push_back("loop"); + exec_name_list.push_back("map"); + exec_name_list.push_back("next"); + exec_name_list.push_back("null"); + exec_name_list.push_back("on"); + exec_name_list.push_back("others"); + exec_name_list.push_back("package"); + exec_name_list.push_back("port"); + exec_name_list.push_back("procedure"); + exec_name_list.push_back("process"); + exec_name_list.push_back("reject"); + exec_name_list.push_back("report"); + exec_name_list.push_back("return"); + exec_name_list.push_back("select"); + exec_name_list.push_back("then"); + exec_name_list.push_back("transport"); + exec_name_list.push_back("use"); + exec_name_list.push_back("wait"); + exec_name_list.push_back("while"); + exec_name_list.push_back("with"); + + cmplx_preproc_list.push_back("pragma"); + + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("**"); + cmplx_calc_list.push_back("/"); + cmplx_calc_list.push_back("mod"); + cmplx_calc_list.push_back("abs"); + cmplx_calc_list.push_back("rem"); + + + + cmplx_logic_list.push_back("="); + cmplx_logic_list.push_back("<"); + cmplx_logic_list.push_back(">"); + cmplx_logic_list.push_back("/="); + cmplx_logic_list.push_back(">="); + cmplx_logic_list.push_back("&"); + cmplx_logic_list.push_back("not"); + cmplx_logic_list.push_back("and"); + cmplx_logic_list.push_back("or"); + cmplx_logic_list.push_back("nand"); + cmplx_logic_list.push_back("nor"); + cmplx_logic_list.push_back("xor"); + cmplx_logic_list.push_back("xnor"); + cmplx_logic_list.push_back("sll"); + cmplx_logic_list.push_back("srl"); + cmplx_logic_list.push_back("sla"); + cmplx_logic_list.push_back("sra"); + cmplx_logic_list.push_back("rol"); + cmplx_logic_list.push_back("ror"); + cmplx_logic_list.push_back("<="); + + + cmplx_cond_list.push_back("case"); + cmplx_cond_list.push_back("for"); + cmplx_cond_list.push_back("if"); + cmplx_cond_list.push_back("else"); + cmplx_cond_list.push_back("elsif"); + cmplx_cond_list.push_back("loop"); + cmplx_cond_list.push_back("next"); + cmplx_cond_list.push_back("when"); + cmplx_cond_list.push_back("while"); + + cmplx_assign_list.push_back("=>"); + cmplx_assign_list.push_back(":="); + cmplx_assign_list.push_back("<="); + + /* COMPLEXITY MODULE STRUCTURES: SPRING 2014 */ + + math_func_list.push_back("SIGN"); + math_func_list.push_back("CEIL"); + math_func_list.push_back("exp"); + math_func_list.push_back("FLOOR"); + math_func_list.push_back("ROUND"); + math_func_list.push_back("FMAX"); + math_func_list.push_back("FMIN"); + math_func_list.push_back("UNIFORM"); + math_func_list.push_back("SRAND"); + math_func_list.push_back("RAND"); + math_func_list.push_back("GET_RAND_MAX"); + math_func_list.push_back("SQRT"); + math_func_list.push_back("CBRT"); + + math_func_list.push_back("FMAX"); + math_func_list.push_back("FMIN"); + math_func_list.push_back("UNIFORM"); + + trig_func_list.push_back("SIN"); + trig_func_list.push_back("COS"); + trig_func_list.push_back("TAN"); + trig_func_list.push_back("ASIN"); + trig_func_list.push_back("ACOS"); + trig_func_list.push_back("ATAN"); + trig_func_list.push_back("ATAN2"); + trig_func_list.push_back("SINH"); + trig_func_list.push_back("COSH"); + trig_func_list.push_back("TANH"); + trig_func_list.push_back("ASINH"); + trig_func_list.push_back("ACOSH"); + trig_func_list.push_back("ATANH"); + + log_func_list.push_back("LOG"); + log_func_list.push_back("EXP"); + + /* COMPLEXITY MODULE STRUCTURES: SPRING 2014 */ + + /* COMPLEXITY MODULE STRUCTURES: SPRING 2014 */ + + cmplx_cyclomatic_list.push_back("if"); + cmplx_cyclomatic_list.push_back("elsif"); + cmplx_cyclomatic_list.push_back("when"); //Max condition evaluation done for a "case" statement is the number of "when" statements under it. + cmplx_cyclomatic_list.push_back("assert"); + cmplx_cyclomatic_list.push_back("wait"); //considers all "wait" statements, including "wait until", so no separate "until" statement is used here + cmplx_cyclomatic_list.push_back("loop"); + //cmplx_cyclomatic_list.push_back("while"); //Already considered with "loop" keyword + //cmplx_cyclomatic_list.push_back("for"); //Already considered with "loop" keyword + + ignore_cmplx_cyclomatic_list.push_back("end if"); + + /* COMPLEXITY MODULE STRUCTURES: SPRING 2014 */ + + cmplx_cyclomatic_logic_list.push_back("and"); + cmplx_cyclomatic_logic_list.push_back("or"); + //cmplx_cyclomatic_logic_list.push_back("nand"); + //cmplx_cyclomatic_logic_list.push_back("nor"); + //cmplx_cyclomatic_logic_list.push_back("xor"); + //cmplx_cyclomatic_logic_list.push_back("xnor"); + + cmplx_cyclomatic_case_list.push_back("when"); + cmplx_cyclomatic_switch_list.push_back("case"); + cmplx_cyclomatic_default_list.push_back("others"); + +} +/*! +* Replaces quoted strings inside a string starting at idx_start with '$'. +* Handles special cases for VHDL literal strings. +* +* \param strline string to be processed +* \param idx_start index of line character to start search +* \param contd specifies the quote string is continued from the previous line +* \param CurrentQuoteEnd end quote character of the current status +* +* \return method status +*/ +int CVHDLCounter::ReplaceQuote(string &strline, size_t &idx_start, bool &contd, char &CurrentQuoteEnd) +{ + size_t idx = 0; + bool done = false; + while ( ! done ) + { + idx = strline.find("\"", idx); // replace all '"' by '$' + if (idx != string::npos) + strline.replace(idx, 1, 1, '$'); + else + break; + } + return CCodeCounter::ReplaceQuote(strline, idx_start, contd, CurrentQuoteEnd); +} + +/*! +* Counts the number of comment lines, removes comments, and +* replaces quoted strings by special chars, e.g., $ +* All arguments are modified by the method. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CVHDLCounter::CountCommentsSLOC(filemap* fmap, results* result, filemap *fmapBak) +{ + if (BlockCommentStart.empty() && LineCommentStart.empty()) + return 0; + if (classtype == UNKNOWN || classtype == DATAFILE) + return 0; + + bool contd = false; + bool contd_nextline; + int comment_type = 0; + /* + comment_type: + 0 : not a comment + 1 : line comment, whole line + 2 : line comment, embedded + 3 : block comment, undecided + 4 : block comment, embedded + */ + + size_t idx_start, idx_end, comment_start; + size_t quote_idx_start; + string curBlckCmtStart, curBlckCmtEnd; + char CurrentQuoteEnd = 0; + bool quote_contd = false, directiveComment =false; + filemap::iterator itfmBak = fmapBak->begin(); + + quote_idx_start = 0; + + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++, itfmBak++) + { + directiveComment = false; + contd_nextline = false; + + quote_idx_start = 0; + idx_start = 0; + + if (CUtil::CheckBlank(iter->line)) + continue; + + if (quote_contd) + { + // Replace quote until next character + ReplaceQuote(iter->line, quote_idx_start, quote_contd, CurrentQuoteEnd); + if (quote_contd) + continue; + } + + if (contd) + comment_type = 3; + + while (!contd_nextline && idx_start < iter->line.length()) + { + // need to handle multiple quote chars in some languages, both " and ' may be accepted + quote_idx_start = FindQuote(iter->line, QuoteStart, quote_idx_start, QuoteEscapeFront); + comment_start = idx_start; + if (!contd) + FindCommentStart(iter->line, comment_start, comment_type, curBlckCmtStart, curBlckCmtEnd); + + if (comment_start == string::npos && quote_idx_start == string::npos) + break; + + if (comment_start != string::npos) + idx_start = comment_start; + + // if found quote before comment, e.g., "this is quote");//comment + if (quote_idx_start != string::npos && (comment_start == string::npos || quote_idx_start < comment_start)) + { + ReplaceQuote(iter->line, quote_idx_start, quote_contd, CurrentQuoteEnd); + if (quote_idx_start > idx_start && quote_idx_start != iter->line.length()) + { + // comment delimiter inside quote + idx_start = quote_idx_start; + continue; + } + } + else if (comment_start != string::npos) + { + // check if next word is pragma or synopsis; this can turn out to be a declaration + StringVector::iterator itDirective = directive.begin(); + for (; itDirective != directive.end(); ++itDirective) + { + if ((CUtil::FindKeyword(iter->line, *itDirective, comment_start+LineCommentStart.size(), TO_END_OF_STRING, false)) != string::npos) + { + directiveComment = true; + iter->line = *itDirective; + break; + } + } + if (directiveComment) + break; + + // comment delimiter starts first + switch (comment_type) + { + case 1: // line comment, definitely whole line + iter->line = ""; + itfmBak->line = ""; + result->comment_lines++; + contd_nextline = true; + break; + case 2: // line comment, possibly embedded + iter->line = iter->line.substr(0, idx_start); + itfmBak->line = itfmBak->line.substr(0, idx_start); + // trim trailing space + iter->line = CUtil::TrimString(iter->line, 1); + itfmBak->line = CUtil::TrimString(itfmBak->line, 1); + if (iter->line.empty()) + result->comment_lines++; // whole line + else + result->e_comm_lines++; // embedded + contd_nextline = true; + break; + case 3: // block comment + case 4: + if (contd) + idx_end = iter->line.find(curBlckCmtEnd); + else + idx_end = iter->line.find(curBlckCmtEnd, idx_start + curBlckCmtStart.length()); + + if (idx_end == string::npos) + { + if (comment_type == 3) + { + iter->line = ""; + itfmBak->line = ""; + result->comment_lines++; + } + else if (comment_type == 4) + { + iter->line = iter->line.substr(0, idx_start); + itfmBak->line = itfmBak->line.substr(0, idx_start); + // trim trailing space + iter->line = CUtil::TrimString(iter->line, 1); + itfmBak->line = CUtil::TrimString(itfmBak->line, 1); + if (iter->line.empty()) + result->comment_lines++; // whole line + else + result->e_comm_lines++; // embedded + } + contd = true; + contd_nextline = true; + break; + } + else + { + contd = false; + iter->line.erase(idx_start, idx_end - idx_start + curBlckCmtEnd.length()); + itfmBak->line.erase(idx_start, idx_end - idx_start + curBlckCmtEnd.length()); + if (iter->line.empty()) + result->comment_lines++; + else + { + // trim trailing space + iter->line = CUtil::TrimString(iter->line, 1); + itfmBak->line = CUtil::TrimString(itfmBak->line, 1); + if (iter->line.empty()) + result->comment_lines++; // whole line + else + result->e_comm_lines++; // embedded + } + + // quote chars found may be erased as it is inside comment + quote_idx_start = idx_start; + } + break; + default: + cout << "Error in CountCommentsSLOC()" << endl; + break; + } + } + } + if (directiveComment) + continue; + } + return 1; +} + +/*! +* Counts file language complexity based on specified language keywords/characters. +* +* \param fmap list of processed file lines +* \param result counter results +* +* \return method status +*/ + +int CVHDLCounter::CountComplexity(filemap* fmap, results* result) +{ + if (classtype == UNKNOWN || classtype == DATAFILE) + return 0; + filemap::iterator fit; + filemap fitBak; + filemap::iterator fitForw, fitBack;//used to check prior an later lines for semicolons + //unsigned int cnt; + //string line, line2; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$><=:"; + tokLocVect conditionalVector; + tokLocVect::reverse_iterator r_tlvIter; + //StringVector::iterator strIter = this->cmplx_cond_list.begin(); warning fix + string buf; // have a buffer string + stringstream ss; // insert the string into a stream + tokenLocation tl; + int count; + bool whenCont; + + size_t idx; + unsigned int cnt, ret, cyclomatic_cnt = 0, ignore_cyclomatic_cnt = 0, main_cyclomatic_cnt = 0, /*function_count = 0,*/ cyclomatic_logic_cnt = 0, main_cyclomatic_logic_cnt = 1, cyclomatic_case_cnt = 0, main_cyclomatic_case_cnt = 1, cyclomatic_default_cnt = 0, cyclomatic_switch_cnt = 0; //warning fix + string line, lastline, file_ext, function_name = ""; + StringVector function_stack; + stack cyclomatic_stack; + stack cyclomatic_logic_stack; + stack cyclomatic_case_stack; + bool process_cyclomatic_complexity = false; + bool first_line_in_main = true; + + + // check whether to process cyclomatic complexity + if (cmplx_cyclomatic_list.size() > 0) + { + process_cyclomatic_complexity = true; + if (skip_cmplx_cyclomatic_file_extension_list.size() > 0) + { + idx = result->file_name.find_last_of("."); + if (idx != string::npos) + { + file_ext = result->file_name.substr(idx); + file_ext = CUtil::ToLower(file_ext); + if (find(skip_cmplx_cyclomatic_file_extension_list.begin(), skip_cmplx_cyclomatic_file_extension_list.end(), file_ext) != skip_cmplx_cyclomatic_file_extension_list.end()) + process_cyclomatic_complexity = false; + } + } + } + + for (fit = fmap->begin(); fit != fmap->end(); fit++) + { + line = fit->line; + + if (CUtil::CheckBlank(line)) + continue; + + line = " " + line; + + // mathematical functions + cnt = 0; + CUtil::CountTally(line, math_func_list, cnt, 1, exclude, "", "", &result->math_func_count, casesensitive); + result->cmplx_math_lines += cnt; + + // trigonometric functions + cnt = 0; + CUtil::CountTally(line, trig_func_list, cnt, 1, exclude, "", "", &result->trig_func_count, casesensitive); + result->cmplx_trig_lines += cnt; + + // logarithmic functions + cnt = 0; + CUtil::CountTally(line, log_func_list, cnt, 1, exclude, "", "", &result->log_func_count, casesensitive); + result->cmplx_logarithm_lines += cnt; + + // calculations + cnt = 0; + CUtil::CountTally(line, cmplx_calc_list, cnt, 1, exclude, "", "", &result->cmplx_calc_count, casesensitive); + result->cmplx_calc_lines += cnt; + + // conditionals + cnt = 0; + CUtil::CountTally(line, cmplx_cond_list, cnt, 1, exclude + ";", "", "", &result->cmplx_cond_count, casesensitive); + result->cmplx_cond_lines += cnt; + + // logical operators + cnt = 0; + StringVector tmpList = cmplx_logic_list;//making a temporary list with the '<=' operator removed from the list; counting it on another pass; + tmpList.pop_back(); + CUtil::CountTally(line, tmpList, cnt, 1, exclude, "", "", &result->cmplx_logic_count, casesensitive); + result->cmplx_logic_lines += cnt; + + // preprocessor directives + cnt = 0; + CUtil::CountTally(line, cmplx_preproc_list, cnt, 1, exclude, "", "", &result->cmplx_preproc_count, casesensitive); + result->cmplx_preproc_lines += cnt; + + // assignments + cnt = 0; + tmpList.clear(); + tmpList = cmplx_assign_list;//making a temporary list with the '<=' operator removed from the list; counting it on another pass; + tmpList.pop_back(); + CUtil::CountTally(line, tmpList, cnt, 1, exclude, "", "", &result->cmplx_assign_count, casesensitive); + result->cmplx_assign_lines += cnt; + + /* No Pointers for VHDL + // pointers + cnt = 0; + // Pointers are embedded syntax so there is NO exclude string or include strings + CUtil::CountTally(line, cmplx_pointer_list, cnt, 1, "", "", "", &result->cmplx_pointer_count, casesensitive); + result->cmplx_pointer_lines += cnt; + */ + + // cyclomatic complexity + if (process_cyclomatic_complexity) + { + // search for cyclomatic complexity keywords + CUtil::CountTally(line, cmplx_cyclomatic_list, cyclomatic_cnt, 1, exclude + ";", "", "", 0, casesensitive); + + CUtil::CountTally(line, cmplx_cyclomatic_switch_list, cyclomatic_switch_cnt, 1, exclude + ";", "", "", 0, casesensitive); + + // search for keywords to exclude + if (ignore_cmplx_cyclomatic_list.size() > 0) + CUtil::CountTally(line, ignore_cmplx_cyclomatic_list, ignore_cyclomatic_cnt, 1, exclude + ";", "", "", 0, casesensitive); + + // search for cyclomatic complexity logical keywords + if (cmplx_cyclomatic_logic_list.size() > 0) + CUtil::CountTally(line, cmplx_cyclomatic_logic_list, cyclomatic_logic_cnt, 1, exclude, "", "", 0, casesensitive); + + // search for cyclomatic complexity case keywords + if (cmplx_cyclomatic_case_list.size() > 0) + CUtil::CountTally(line, cmplx_cyclomatic_case_list, cyclomatic_case_cnt, 1, exclude + ";", "", "", 0, casesensitive); + + // search for cyclomatic complexity case default keywords + if (cmplx_cyclomatic_default_list.size() > 0) + CUtil::CountTally(line, cmplx_cyclomatic_default_list, cyclomatic_default_cnt, 1, exclude + ";", "", "", 0, casesensitive); + + if(cyclomatic_default_cnt > 0) + { + cyclomatic_cnt -= cyclomatic_default_cnt; + cyclomatic_case_cnt -= cyclomatic_default_cnt; + cyclomatic_default_cnt = 0; + } + + // pars + // parse function name if found + ret = (unsigned)ParseFunctionName(line, lastline, function_stack, function_name); + if (ret != 1 && !cyclomatic_stack.empty() && cyclomatic_stack.size() == function_stack.size()) + { + // remove count stack entry for non-function names + cyclomatic_cnt += cyclomatic_stack.top(); + ignore_cyclomatic_cnt = 0; + cyclomatic_stack.pop(); + } + if (ret != 1 && !cyclomatic_logic_stack.empty() && cyclomatic_logic_stack.size() == function_stack.size()-1) + { + // remove count stack entry for non-function names + cyclomatic_logic_cnt += cyclomatic_logic_stack.top(); + cyclomatic_logic_stack.pop(); + } + if (ret != 1 && !cyclomatic_case_stack.empty() && cyclomatic_case_stack.size() == function_stack.size()-1) + { + // remove count stack entry for non-function names + cyclomatic_case_cnt += cyclomatic_case_stack.top(); + cyclomatic_case_stack.pop(); + } + + if (ret == 1) + { + // capture count at end of function + lineElement element(cyclomatic_cnt - ignore_cyclomatic_cnt + 1, function_name); + result->cmplx_cycfunct_count.push_back(element); + + lineElement n_element(cyclomatic_cnt - ignore_cyclomatic_cnt + cyclomatic_logic_cnt + 1, function_name); + result->cmplx_cycfunct_CC2_count.push_back(n_element); + + if (cyclomatic_case_cnt > 0) + { + lineElement c_element(cyclomatic_cnt - ignore_cyclomatic_cnt - cyclomatic_case_cnt + cyclomatic_switch_cnt + 1, function_name); + result->cmplx_cycfunct_CC3_count.push_back(c_element); + } + else + result->cmplx_cycfunct_CC3_count.push_back(element); + + if (!function_stack.empty()) + { + // grab previous function from stack to continue + if (!cyclomatic_stack.empty()) + { + cyclomatic_cnt = cyclomatic_stack.top(); + cyclomatic_stack.pop(); + } + if (!cyclomatic_logic_stack.empty()) + { + cyclomatic_logic_cnt = cyclomatic_logic_stack.top(); + cyclomatic_logic_stack.pop(); + } + if (!cyclomatic_case_stack.empty()) + { + cyclomatic_case_cnt = cyclomatic_case_stack.top(); + cyclomatic_case_stack.pop(); + } + } + else { + cyclomatic_cnt = 0; + cyclomatic_logic_cnt = 0; + cyclomatic_case_cnt = 0; + } + function_name = ""; + ignore_cyclomatic_cnt = 0; + cyclomatic_switch_cnt = 0; + } + else if (ret == 2) + { + if (first_line_in_main) + { + main_cyclomatic_cnt = 1; + first_line_in_main = false; + } + if (main_cyclomatic_cnt < 1) + main_cyclomatic_cnt = 1; // add 1 for main function here in case no other decision points are found in main + // some code doesn't belong to any function + main_cyclomatic_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt; + main_cyclomatic_logic_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt + cyclomatic_logic_cnt; + main_cyclomatic_case_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt - cyclomatic_case_cnt + cyclomatic_switch_cnt; + cyclomatic_cnt = ignore_cyclomatic_cnt = cyclomatic_logic_cnt = cyclomatic_case_cnt = cyclomatic_switch_cnt = 0; + } + else { + if (!function_stack.empty() && (function_stack.size() > cyclomatic_stack.size() + 1 || (cyclomatic_stack.empty() && function_stack.size() > 1))) + { + // capture previous complexity count from open function + cyclomatic_stack.push(cyclomatic_cnt - ignore_cyclomatic_cnt); + cyclomatic_cnt = ignore_cyclomatic_cnt = 0; + } + if (!function_stack.empty() && (function_stack.size() > cyclomatic_logic_stack.size() + 1 || (cyclomatic_logic_stack.empty() && function_stack.size() > 1))) + { + // capture previous complexity count from open function + cyclomatic_logic_stack.push(cyclomatic_logic_cnt); + cyclomatic_logic_cnt = 0; + } + if (!function_stack.empty() && (function_stack.size() > cyclomatic_case_stack.size() + 1 || (cyclomatic_case_stack.empty() && function_stack.size() > 1))) + { + // capture previous complexity count from open function + cyclomatic_case_stack.push(cyclomatic_case_cnt - cyclomatic_switch_cnt); + cyclomatic_case_cnt = 0; + cyclomatic_switch_cnt = 0; + } + } + } + + } + + // do a single pass to mark and replace logical operator lessThan or equal "<=" + // these appear only in conditional statements + // the remaining are signal assignment operators + for (fit = fmap->begin(); fit != fmap->end(); fit++) + { + line = fit->line; + line = CUtil::ToLower(line); + + if (CUtil::CheckBlank(line)) + continue; + ss.clear(); + ss.str(""); + ss << line; + count = -1; + while (ss >> buf) + { + ++count; + if (!buf.compare("if")) + { + tl.lineNumber = fit->lineNumber; + tl.position = count; + tl.token = buf; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + if (!buf.compare("then")) + { + tl.lineNumber = fit->lineNumber; + tl.position = count; + tl.token = buf; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + if (!buf.compare("elsif")) + { + tl.lineNumber = fit->lineNumber; + tl.position = count; + tl.token = buf; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + if (!buf.compare("wait")) + { + tl.lineNumber= fit->lineNumber; + tl.position = count; + tl.token = buf; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + if (!buf.compare("until")) + { + tl.lineNumber= fit->lineNumber; + tl.position = count; + tl.token = buf; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + if (!buf.compare("assert")) + { + tl.lineNumber= fit->lineNumber; + tl.position = count; + tl.token = buf; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + if (!buf.compare("while")) + { + tl.lineNumber= fit->lineNumber; + tl.position = count; + tl.token = buf; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + if (!buf.compare("loop")) + { + tl.lineNumber= fit->lineNumber; + tl.position = count; + tl.token = buf; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + if (!buf.compare("next")) + { + tl.lineNumber= fit->lineNumber; + tl.position = count; + tl.token = buf; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + if (!buf.compare("when")) + { + tl.lineNumber= fit->lineNumber; + tl.position = count; + tl.token = buf; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + if (!buf.compare("exit")) + { + tl.lineNumber= fit->lineNumber; + tl.position = count; + tl.token = buf; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + if (!buf.compare("return")) + { + tl.lineNumber= fit->lineNumber; + tl.position = count; + tl.token = buf; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + if (!buf.compare("case")) + { + tl.lineNumber= fit->lineNumber; + tl.position = count; + tl.token = buf; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + if (buf.find_last_of(";") != string::npos) + { + tl.lineNumber= fit->lineNumber; + tl.position = count; + tl.token = ";"; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + if (buf.find("<=") != string::npos) + { + whenCont = false; + // iterate up the vector an look for the first conditional statement + r_tlvIter = conditionalVector.rbegin(); + while (r_tlvIter != conditionalVector.rend()) + { + if (!r_tlvIter->token.compare(";")) + { + result->cmplx_assign_count.back()++; + result->cmplx_assign_lines++; + tl.token = "assign"; + break; + } + else + { + if ((!r_tlvIter->token.compare("if") || !r_tlvIter->token.compare("elsif") || !r_tlvIter->token.compare("assert") || + !r_tlvIter->token.compare("while") || !r_tlvIter->token.compare("return") || !r_tlvIter->token.compare("until") ) && !whenCont) + { + result->cmplx_logic_count.back()++; + result->cmplx_logic_lines++; + tl.token = "lte"; + break; + } + if (!r_tlvIter->token.compare("when")) + { + whenCont = true; + r_tlvIter++; + continue; + } + if (!r_tlvIter->token.compare("case") || !r_tlvIter->token.compare("next") || !r_tlvIter->token.compare("exit")) + { + result->cmplx_assign_count.back()++; + result->cmplx_assign_lines++; + tl.token = "assign"; + whenCont = false; + break; + } + result->cmplx_assign_count.back()++; + result->cmplx_assign_lines++; + tl.token = "assign"; + break; + } + // r_tlvIter++; MS VC++ warning C4702 unreachable code TODO: Review ! ! ! + } + tl.lineNumber= fit->lineNumber; + tl.position = count; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + } + + } + // done with a file, if has "main" code add it + if (main_cyclomatic_cnt > 0) + { + lineElement element(main_cyclomatic_cnt, "main"); + lineElement n_element(main_cyclomatic_logic_cnt, "main"); + lineElement c_element(main_cyclomatic_case_cnt, "main"); + result->cmplx_cycfunct_count.push_back(element); + result->cmplx_cycfunct_CC2_count.push_back(n_element); + result->cmplx_cycfunct_CC3_count.push_back(c_element); + } + return 1; +} + + +/*! +* Counts directive lines of code. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CVHDLCounter::CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak) +{ + size_t idx; + unsigned int cnt = 0; + string strDirLine = ""; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$><=:"; + + filemap::iterator itfmBak = fmapBak->begin(); + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++, itfmBak++) + { + if (CUtil::CheckBlank(iter->line)) + continue; + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(" " + iter->line, directive, cnt, 1, exclude, "", "", &result->directive_count, false); + } + + // if not a continuation of a previous directive + for (vector::iterator viter = directive.begin(); viter != directive.end(); viter++) + { + // merged bug fix for considering only stand-alone keywords + // e.g. package should not be considered a directive (only 'pack' is) + if (((idx = CUtil::FindKeyword(iter->line, *viter, 0, TO_END_OF_STRING, false)) != string::npos) && idx == 0) + { + iter->line = ""; + result->directive_lines[PHY]++; + break; + } + } + } + return 1; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CVHDLCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + bool blank_flag = false; + bool found_unit = false; + string strLSLOC = ""; + string strLSLOCBak = ""; + unsigned int cnt = 0; + unsigned int loopLevel = 0; + + filemap::iterator fit, fitbak; + string line, lineBak, tmp; + string special = "[]()+/-*<>=,@&~!^?:%{}"; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$><=:"; + + unsigned int l_paren_cnt = 0; + bool l_foundblock, found_forifwhile, found_end, found_type, found_is, processSignatureFound, found_withSelect, found_whenConditional, foundWait, blockSignatureFound, found_record; + l_foundblock = found_forifwhile = found_end = found_is = processSignatureFound = found_withSelect = found_whenConditional = foundWait = blockSignatureFound = found_record = false; + vectorString currentBlock; + + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + // insert blank at the beginning (for searching keywords) + line = ' ' + fit->line; + lineBak = ' ' + fitbak->line; + + if (CUtil::CheckBlank(line)) + { + // the line is either blank/whole line comment/compiler directive + blank_flag = true; + continue; + } + else + blank_flag = false; + + if (!blank_flag) + { + // blank line means blank_line/comment_line/directive + // call SLOC function to detect logical SLOC and add to result + LSLOC(result, line, fit->lineNumber, lineBak, strLSLOC, strLSLOCBak, l_paren_cnt, l_foundblock, + found_forifwhile, found_end, found_type, found_is, found_unit, loopLevel, currentBlock, processSignatureFound, found_withSelect, found_whenConditional, foundWait, + blockSignatureFound, found_record); + + cnt = 0; + CUtil::CountTally(line, data_name_list, cnt, 1, exclude, "", "", NULL, false); //tie breaker in favor of physical data rather than physical exec + + // need to check also if the data line continues + if ((cnt > 0 && currentBlock.size() == 0 ) || (currentBlock.size()>0 && (currentBlock.back().compare("record") == 0 || currentBlock.back().compare("units") == 0))) + result->data_lines[PHY]++; + else + result->exec_lines[PHY]++; + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count, false); + } + } + } + return 1; +} + +/*! +* Processes a logical line of code. +* This method is called after a logical SLOC is determined. +* The method adds LSLOC to the result, increases counts, and resets variables. +* +* \param result counter results +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param found_block found block flag +* \param found_forifwhile found for, if, or while flag +* \param found_end found end flag +* \param found_type found type flag +* \param found_is found is flag +* \param found_unit found accept flag +* \param trunc_flag truncate lines? +* \param currentBlock current block vector +*/ +void CVHDLCounter::FoundSLOC(results* result, size_t lineNumber, string &strLSLOC, string &strLSLOCBak, bool &found_block, + bool &found_forifwhile, bool &found_end, bool &found_type, bool &found_is, bool &found_unit, bool &trunc_flag, + StringVector currentBlock) +{ + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$:"; + + // add to the list for comparison purpose + if (result->addSLOC(CUtil::TrimString(strLSLOCBak), lineNumber, trunc_flag)) + { + // determine logical type, data declaration or executable + unsigned int cnt = 0; + + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", &result->data_name_count, false); + if ((cnt > 0 && currentBlock.size() == 0) || (currentBlock.size()>0 && (currentBlock.back().compare("record") == 0 || currentBlock.back().compare("units") == 0)) ) + result->data_lines[LOG]++; + else + result->exec_lines[LOG]++; + } + + // reset all variables whenever a new statement/logical SLOC is found + strLSLOC = ""; + strLSLOCBak = ""; + found_block = false; + found_forifwhile = false; + found_end = false; + found_type = false; + found_is = false; + found_unit = false; +} + +/*! +* Extracts and stores logical lines of code. +* Determines and extract logical SLOC to place in the result variable +* using addSLOC function. Each time the addSLOC function is called, +* a new logical SLOC is added. This function assumes that the directive +* is handled before it is called. +* +* \param result counter results28186 +* \param line processed physical line of code +* \param lineBak original physical line of code +* \param strLSLOC processed logical string +* \param strLSLOCBak original logical string +* \param paren_cnt count of parenthesis +* \param found_block found block flag +* \param found_forifwhile found for, if, or while flag +* \param found_end found end flag +* \param found_type found type flag +* \param found_is found is flag +* \param found_unit found unit flag +* \param loopLevel nested loop level +* \param currentBlock current block vector +* \param processSignatureStartFound found process signature start flag +* \param found_withSelect found with/select flag +* \param found_whenConditional found when/conditional flag +* \param foundWait found wait flag +* \param blockSignatureStartFound found block signature start flag +* \param found_recort found record flag +*/ +void CVHDLCounter::LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + unsigned int &paren_cnt, bool &found_block, bool &found_forifwhile, bool &found_end, bool &found_type, bool &found_is, + bool &found_unit, unsigned int &loopLevel, vectorString ¤tBlock, bool &processSignatureStartFound, bool &found_withSelect, + bool &found_whenConditional, bool &foundWait, bool &blockSignatureStartFound, bool &found_record) +{ + size_t start = 0, forstart = 0; //starting index of the working string + size_t i = 0, tempi, strSize; + string templine = CUtil::TrimString(line); + string tmp, fortmp; + bool trunc_flag = false; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$:"; + string keywordchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + // there may be more than 1 logical SLOC in a line + for (i = 0; i < line.length(); i++) + { + switch (line[i]) + { + case ';': + if (paren_cnt > 0) + break; + if (currentBlock.size() != 0) + { + if (((string) currentBlock.back()).compare("port") == 0) + { + // paren_cnt should be zero here + currentBlock.pop_back(); + } + if (((string) currentBlock.back()).compare("generic") == 0) + { + // paren_cnt should be zero here + currentBlock.pop_back(); + } + if (found_withSelect && CUtil::FindKeyword(line.substr(start, i + 1 - start), "others", 0, TO_END_OF_STRING, false) != string::npos) + { + // found the end of with ... select statement + found_withSelect = false; + currentBlock.pop_back(); + } + if (found_whenConditional) + { + //found the end of when ... conditional statement + found_whenConditional = false; + currentBlock.pop_back(); + } + } + if (!found_end) + { + if (foundWait) foundWait = false; + + // check for empty statement (=1 LSLOC) + if (CUtil::TrimString(line.substr(start, i + 1 - start)) == ";" && strLSLOC.length() < 1) + { + strLSLOC = ";"; + strLSLOCBak = ";"; + } + else + { + strSize = CUtil::TruncateLine(i - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_unit, trunc_flag, currentBlock); + if (currentBlock.size() != 0 && (currentBlock.front().compare("configuration") == 0) && (currentBlock.back().compare("entity") == 0)) + { + //pop the element in the vector matching the END that was just found + if (currentBlock.size() != 0) + currentBlock.pop_back(); + } + } + else + { + if (currentBlock.size() != 0 && ((string) currentBlock.back()).compare("for") == 0 && ((string) currentBlock.front()).compare("configuration") == 0) + { + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_unit, trunc_flag, currentBlock); + } + found_end = false; + found_block = false; + found_is = false; + found_forifwhile = false; + found_type = false; + found_unit = false; + strLSLOC = ""; + strLSLOCBak = ""; + foundWait = false; + if (found_record) + found_record = false; + if (found_unit) + found_unit = false; + + //pop the element in the vector matching the END that was just found + if (currentBlock.size() != 0) + currentBlock.pop_back(); + } + start = i + 1; + break; + case '(': + if (found_type) + found_type = false; + if (currentBlock.size() != 0) + { + if (((string) currentBlock.back()).compare("process") == 0 && !found_is) + processSignatureStartFound = true; + else if (((string) currentBlock.back()).compare("block") == 0 && !found_is) + blockSignatureStartFound = true; + } + paren_cnt++; + break; + case ')': + if (paren_cnt > 0) + paren_cnt--; + break; + case ',': + if (found_withSelect) + { + strSize = CUtil::TruncateLine(i+1-start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_unit, trunc_flag, currentBlock); + start = i + 1; + continue; + } + break; + default: + if (currentBlock.size() != 0) + { + // check for is here first + tmp = "xxx " + CUtil::TrimString(line.substr(start, i + 1 - start)); + + //try to locate the is at the end of the string if a block has been found + if (found_block) + { + tempi = CUtil::FindKeyword(tmp, "is", 0, TO_END_OF_STRING, false); + if (tempi != string::npos) + { + strSize = CUtil::TruncateLine(tempi+3-start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_unit, trunc_flag, currentBlock); + start = i + 1; + found_is = true; + if (currentBlock.size() > 0 && (currentBlock.back().compare("block") == 0 || currentBlock.back().compare("process") == 0 )) + processSignatureStartFound = true; + continue; + } + else + { + // now see if there is anything else other than a space or an 'i' for the start of the word is + if (((string) currentBlock.back()).compare("process") == 0 && line[i] != ' ' && line[i] != 'i' && paren_cnt == 0 && !found_is) + { + strSize = CUtil::TruncateLine(i - 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_unit, trunc_flag, currentBlock); + start = i; + processSignatureStartFound = false; + continue; + } + else if (((string) currentBlock.back()).compare("block") == 0 && line[i] != ' ' && line[i] != 'i' && paren_cnt == 0 && !found_is) + { + strSize = CUtil::TruncateLine(i - 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_unit, trunc_flag, currentBlock); + start = i; + blockSignatureStartFound = false; + continue; + } + } + } + } + } + + // continue the following processing only if line[i] is not in a middle of a word + if (keywordchars.find(line[i]) != string::npos && i < line.length() - 1) + continue; + + // if it ends in xxx, then it has already been counted, so ignore it + tmp = "xxx " + CUtil::TrimString(line.substr(start, i + 1 - start)); + fortmp = "xxx " + CUtil::TrimString(line.substr(forstart, i + 1 - start)); + + if (found_block) + { + // try to locate the is at the end of the string if a block has been found + tempi = CUtil::FindKeyword(tmp, "is", 0, TO_END_OF_STRING, false); + if (tempi != string::npos) + { + strSize = CUtil::TruncateLine(tempi+3-start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_unit, trunc_flag, currentBlock); + start = i + 1; + found_is = true; + if (currentBlock.size() > 0 && (currentBlock.back().compare("block") == 0 || currentBlock.back().compare("process") == 0 )) + processSignatureStartFound = true; + continue; + } + } + + tempi = CUtil::FindKeyword(tmp, "end", 0, TO_END_OF_STRING, false); + if (tempi != string::npos) + { + found_end = true; + + // record end loop for nested loop processing + if (print_cmplx) + { + tmp = CUtil::TrimString(line.substr(start, i + 5 - start)); + if (CUtil::FindKeyword(tmp, "end loop", 0, TO_END_OF_STRING, false) != string::npos) + { + if (loopLevel > 0){ + loopLevel--; + cout << loopLevel; + } + + } + tmp = CUtil::TrimString(line.substr(start, i + 9 - start)); + if (CUtil::FindKeyword(tmp, "end generate", 0, TO_END_OF_STRING, false) != string::npos) + { + if (loopLevel > 0) + loopLevel--; + } + } + start = i + 1; + } + + if (!found_end) + { + // 'begin' is ignored because it's counted with procedure, function, etc. already + // this may ignore the 'standalone' block that starts with 'declare' or only 'begin' + if (CUtil::FindKeyword(tmp, "begin", 0, TO_END_OF_STRING, false) != string::npos) + { + // found a SLOC + strLSLOC += line.substr(start, i - start + 1); + strLSLOCBak += lineBak.substr(start, i - start + 1); + start = i + 1; + continue; + } + if (!foundWait) + { + if (CUtil::FindKeyword(tmp, "wait", 0, TO_END_OF_STRING, false) != string::npos) + foundWait = true; + } + + if (!found_forifwhile || (currentBlock.size() != 0 && ((string) currentBlock.front()).compare("configuration") == 0)) + { + if (CUtil::FindKeyword(tmp, "for", 0 , TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "while", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "if", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "elsif", 0, TO_END_OF_STRING, false) != string::npos) + { + if (CUtil::FindKeyword(tmp, "if", 0, TO_END_OF_STRING, false) != string::npos) + { + currentBlock.push_back("if"); + found_forifwhile = true; + } + else if (CUtil::FindKeyword(tmp, "elsif", 0, TO_END_OF_STRING, false) != string::npos) + { + found_forifwhile = true; + } + else if (CUtil::FindKeyword(tmp, "while", 0, TO_END_OF_STRING, false) != string::npos) + { + currentBlock.push_back("while"); + found_forifwhile = true; + } + else if (CUtil::FindKeyword(tmp, "for", 0, TO_END_OF_STRING, false) != string::npos && !foundWait) + { + found_forifwhile = true; + + if (currentBlock.size() != 0 && ((string) currentBlock.front()).compare("configuration") == 0) + { + //inside a configuration block + if (CUtil::FindKeyword(fortmp, "for", 0, TO_END_OF_STRING, false) != string::npos) + { + if (((string) currentBlock.back()).compare("for") == 0 ) + { + // add sloc if this for loop is nested + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, + found_forifwhile, found_end, found_type, found_is, + found_unit, trunc_flag, currentBlock); + currentBlock.push_back("for"); + forstart = i + 1; + } + else + { + currentBlock.push_back("for"); + forstart = i + 1; + } + } + } + else + { + // just a regular for + currentBlock.push_back("for"); + } + } + } + + if (currentBlock.size() != 0 && ((string) currentBlock.front()).compare("configuration") == 0) + { + // inside a configuration block + if ((CUtil::FindKeyword(fortmp, "use", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(fortmp, "group", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(fortmp, "attribute", 0, TO_END_OF_STRING, false) != string::npos) && + ((string) currentBlock.back()).compare("for") == 0) + { + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_unit, trunc_flag, currentBlock); + forstart = i + 1; + } + } + + // 'exception' is removed because it is not counted + if (CUtil::FindKeyword(tmp, "loop", 0, TO_END_OF_STRING, false) != string::npos) + { + // found a SLOC + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_unit, trunc_flag, currentBlock); + start = i + 1; + + // record nested loop level + if (print_cmplx) + { + loopLevel++; + if ((unsigned int)result->cmplx_nestloop_count.size() < loopLevel) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopLevel-1]++; + } + continue; + } + } + else if (CUtil::FindKeyword(tmp, "loop", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "then", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "record", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "generate", 0, TO_END_OF_STRING, false) != string::npos ) // for..use..record + { + // found a SLOC + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_unit, trunc_flag, currentBlock); + start = i + 1; + + // record nested loop level + if (print_cmplx) + { + if (CUtil::FindKeyword(tmp, "loop", 0, TO_END_OF_STRING, false) != string::npos || CUtil::FindKeyword(tmp, "generate", 0, TO_END_OF_STRING, false) != string::npos) + { + loopLevel++; + if ((unsigned int)result->cmplx_nestloop_count.size() < loopLevel) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopLevel-1]++; + } + } + continue; + } + + // similarly, check for procedure, task, function - it ends with 'is' keyword + // procedure ... is... + // package ... is ... + if (!found_block) + { + if (CUtil::FindKeyword(tmp, "procedure", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "function", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "package", 0, TO_END_OF_STRING, false) !=string::npos || + CUtil::FindKeyword(tmp, "component", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "case",0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "process", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "entity", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "architecture", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "configuration", 0, TO_END_OF_STRING, false) != string::npos || + CUtil::FindKeyword(tmp, "block", 0, TO_END_OF_STRING, false) != string::npos ) + { + if (CUtil::FindKeyword(tmp, "entity", 0, TO_END_OF_STRING, false) != string::npos) + { + if (((currentBlock.size() != 0) && ! (((string) currentBlock.front()).compare("configuration") == 0))) + { + currentBlock.push_back("entity"); + found_block = true; + } + else if (currentBlock.size() == 0) + { + currentBlock.push_back("entity"); + found_block = true; + } + } + else if (CUtil::FindKeyword(tmp, "architecture", 0, TO_END_OF_STRING, false) != string::npos) + { + currentBlock.push_back("architecture"); + found_block = true; + } + else if (CUtil::FindKeyword(tmp, "case", 0, TO_END_OF_STRING, false) != string::npos) + { + currentBlock.push_back("case"); + found_block = true; + } + else if (CUtil::FindKeyword(tmp, "configuration", 0, TO_END_OF_STRING, false) != string::npos) + { + currentBlock.push_back("configuration"); + found_block = true; + } + else if (CUtil::FindKeyword(tmp, "block", 0, TO_END_OF_STRING, false) != string::npos) + { + currentBlock.push_back("block"); + blockSignatureStartFound = false; + found_block = true; + } + else if (CUtil::FindKeyword(tmp, "component", 0, TO_END_OF_STRING, false) != string::npos) + { + currentBlock.push_back("component"); + found_block = true; + } + else if (CUtil::FindKeyword(tmp, "process", 0, TO_END_OF_STRING, false) != string::npos) + { + currentBlock.push_back("process"); + processSignatureStartFound = false; + found_block = true; + } + else + found_block = true; + } + } + + // check for end of a when statement within a case statement + tempi = CUtil::FindKeyword(templine, "=>", 0, TO_END_OF_STRING, false); + if ((tempi == templine.length() - 2) && currentBlock.size() != 0 && ((string) currentBlock.back()).compare("case") == 0) + { + strSize = CUtil::TruncateLine(tempi + 3 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_unit, trunc_flag, currentBlock); + start = templine.length() + 1; + continue; + } + + if (!found_type) + { + if (CUtil::FindKeyword(tmp, "type", 0, TO_END_OF_STRING, false) != string::npos) + found_type = true; + } + if ((currentBlock.size() != 0) && ! (((string) currentBlock.back()).compare("port") == 0)) + { + if (CUtil::FindKeyword(tmp, "port", 0, TO_END_OF_STRING, false) != string::npos) + { + if (currentBlock.back().compare("component") == 0) + { + strSize = CUtil::TruncateLine(1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_unit, trunc_flag, currentBlock); + } + currentBlock.push_back("port"); + } + } + if ((currentBlock.size() != 0) && ! (((string) currentBlock.back()).compare("generic") == 0)) + { + if (CUtil::FindKeyword(tmp, "generic", 0, TO_END_OF_STRING, false) != string::npos) + { + if (currentBlock.back().compare("component") == 0) + { + strSize = CUtil::TruncateLine(1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_unit, trunc_flag, currentBlock); + } + currentBlock.push_back("generic"); + } + } + + if ((currentBlock.size() != 0) && ! (((string) currentBlock.back()).compare("component") == 0)) + { + if (CUtil::FindKeyword(tmp, "component", 0, TO_END_OF_STRING, false) != string::npos) + currentBlock.push_back("component"); + } + + // process 'select...end select;', 'accept ... end accept;' + // 'record ... end record;' is handled via 'type' + // select ... end select; --> only one word statement 'select' + // accept id... do ... end [id]; --> SLOC starting from 'accept' to 'do' + // find 'do' only already found 'accept' + if (!found_withSelect) + { + if (CUtil::FindKeyword(tmp, "with", 0, TO_END_OF_STRING, false) != string::npos) + { + currentBlock.push_back("with"); + found_withSelect = true; + } + } + else if (CUtil::FindKeyword(tmp, "select", 0, TO_END_OF_STRING, false) != string::npos) + { + // found 'select' statement, one SLOC + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_unit, trunc_flag, currentBlock); + start = i + 1; + continue; + } + if (currentBlock.size() != 0) + { + string currentString = ((string) currentBlock.back()); + if (!(currentString.compare("case") == 0) && !(currentString.compare("with") == 0) && !(currentString.compare("when") == 0 ) && + CUtil::FindKeyword(tmp, "when", 0, TO_END_OF_STRING, false) != string::npos) + { + currentBlock.push_back("when"); + found_whenConditional = true; + } + else if (found_whenConditional) + { + if (CUtil::FindKeyword(tmp, "else", 0, TO_END_OF_STRING, false) != string::npos) + { + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_unit, trunc_flag, currentBlock); + start = i + 1; + continue; + } + } + } + + if (!found_unit) + { + if (CUtil::FindKeyword(tmp, "units", 0, TO_END_OF_STRING, false) != string::npos) + { + found_unit = true; + currentBlock.push_back("units"); + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_unit, trunc_flag, currentBlock); + continue; + } + } + if (!found_record) + { + if (CUtil::FindKeyword(tmp, "record", 0, TO_END_OF_STRING, false) != string::npos) + { + found_record = true; + currentBlock.push_back("record"); + FoundSLOC(result, lineNumber, strLSLOC, strLSLOCBak, found_block, found_forifwhile, + found_end, found_type, found_is, found_unit, trunc_flag,currentBlock); + continue; + } + } + + } + } + + tmp = CUtil::TrimString(line.substr(start, i - start)); + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += tmp.substr(0, strSize); + tmp = CUtil::TrimString(lineBak.substr(start, i - start)); + strLSLOCBak += tmp.substr(0, strSize); + + // drop continuation symbol + if (strLSLOC[strLSLOC.length()-1] == '\\') + { + strLSLOC = strLSLOC.substr(0, strLSLOC.length()-1); + strLSLOCBak = strLSLOCBak.substr(0, strLSLOCBak.length()-1); + } + } + if (tmp == "") + found_forifwhile = false; +} + +int CVHDLCounter::ParseFunctionName(const string &line, string &lastline, StringVector &functionStack, string &functionName){ + string str; + size_t idx; + + + idx = CUtil::FindKeyword(line, "procedure"); + if (idx != string::npos) + { + if (idx + 10 < line.length()) + { + str = line.substr(idx + 10); + str = CUtil::ClearRedundantSpaces(str); + idx = str.find("("); + if(idx != string::npos){ + str = str.substr(0, idx); + } + idx = str.find(" "); + if(idx != string::npos){ + str = str.substr(0, idx); + } + str = CUtil::ClearRedundantSpaces(str); + functionStack.push_back(str); + } + } + + idx = CUtil::FindKeyword(line, "function"); + if (idx != string::npos) + { + if (idx + 9 < line.length()) + { + str = line.substr(idx + 9); + str = CUtil::ClearRedundantSpaces(str); + idx = str.find("("); + if(idx != string::npos){ + str = str.substr(0, idx); + } + idx = str.find(" "); + if(idx != string::npos){ + str = str.substr(0, idx); + } + str = CUtil::ClearRedundantSpaces(str); + functionStack.push_back(str); + } + } + + if (functionStack.empty()) + { + // dealing with some code out of any subroutines, it a "main" code + return 2; + } + + string temp = CUtil::ClearRedundantSpaces(line); + idx = CUtil::FindKeyword(line, "end"); + if(idx != string::npos){ + idx = line.find(functionStack.back()); + if (idx != string::npos) + { + str = functionStack.back(); + functionStack.pop_back(); + functionName = CUtil::ClearRedundantSpaces(str); + lastline=line; // warning fix + return 1; + } + } + + return 0; +} diff --git a/src/CVHDLCounter.h b/src/CVHDLCounter.h new file mode 100644 index 0000000..adb5dd4 --- /dev/null +++ b/src/CVHDLCounter.h @@ -0,0 +1,62 @@ +//! Code counter class definition for the VHDL language. +/*! +* \file CVHDLCounter.h +* +* This file contains the code counter class definition for the VHDL hardware definition language (used in FPGA programming). +*/ + +#ifndef CVHDLCounter_h +#define CVHDLCounter_h + +#include "CCodeCounter.h" + +class tokenLocation +{ +public: + string token; + unsigned int lineNumber; + int position; +}; + +typedef vector tokLocVect; + +//! VHDL code counter class. +/*! +* \class CVHDLCounter +* +* Defines the VHDL code counter class. +*/ +class CVHDLCounter : public CCodeCounter +{ +public: + CVHDLCounter(); + +protected: + virtual int ReplaceQuote(string &strline, size_t &idx_start, bool &contd, char &CurrentQuoteEnd); + virtual int CountCommentsSLOC(filemap* fmap, results* result, filemap *fmapBak); + virtual int CountComplexity(filemap* fmap, results* result); + virtual int CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak = NULL); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak); + void FoundSLOC(results* result, size_t lineNumber, string &strLSLOC, string &strLSLOCBak, bool &found_block, bool &found_forifwhile, + bool &found_end, bool &found_type, bool &found_is, bool &found_unit, bool &trunc_flag, StringVector currentBlock); + void LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + unsigned int &paren_cnt, bool &forflag, bool &found_forifwhile, bool &found_while, bool &found_type, bool &found_is, + bool &found_unit, unsigned int &loopLevel, vectorString ¤tBlock, bool &processSignatureFound, bool &found_withSelect, + bool &found_whenConditional, bool &foundWait, bool &blockSignatureFound, bool &found_record); + using CCodeCounter::ParseFunctionName; // warning fix + int ParseFunctionName(const string &line, string &lastline, StringVector &functionStack, string &functionName); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CVHDLCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CVHDLCounter(const CVHDLCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CVHDLCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CVHDLCounter operator=(const CVHDLCounter); // Declare without implementation +}; + +#endif diff --git a/src/CVbCounter.cpp b/src/CVbCounter.cpp new file mode 100644 index 0000000..35fecf8 --- /dev/null +++ b/src/CVbCounter.cpp @@ -0,0 +1,605 @@ +//! Code counter class methods for the Visual Basic language. +/*! +* \file CVbCounter.cpp +* +* This file contains the code counter class methods for the Visual Basic language. +*/ + +#include "CVbCounter.h" + +#define CONTINUATION_CHAR '_' + +/*! +* Constructs a CVbCounter object. +*/ +CVbCounter::CVbCounter() +{ + classtype = VB; + language_name = "Visual_Basic"; + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("Visual_Basic", file_extension); + + /*file_extension.push_back(".vb"); + file_extension.push_back(".frm"); + file_extension.push_back(".mod"); + file_extension.push_back(".cls"); + file_extension.push_back(".bas");*/ + + QuoteStart = "\x93"; + + QuoteEnd = "\x94"; + QuoteEscapeRear = '\"'; + casesensitive = false; + + exclude_keywords.push_back("Do"); // must be alone, e.g., not Do i = 5 + exclude_keywords.push_back("Else"); + exclude_keywords.push_back("Loop"); + exclude_keywords.push_back("Wend"); + + exclude_start_keywords.push_back("End"); + exclude_start_keywords.push_back("Next"); + + LineCommentStart.push_back("'"); + LineCommentStart.push_back("REM "); + + directive.push_back("#Const"); + directive.push_back("#Else"); + directive.push_back("#ElseIf"); + directive.push_back("#End"); + directive.push_back("#ExternalSource"); + directive.push_back("#If"); + directive.push_back("#Region"); + + data_name_list.push_back("Boolean"); + data_name_list.push_back("Byte"); + data_name_list.push_back("Collection"); + data_name_list.push_back("Const"); + data_name_list.push_back("Currency"); + data_name_list.push_back("Date"); + data_name_list.push_back("Dim"); + data_name_list.push_back("Double"); + data_name_list.push_back("Integer"); + data_name_list.push_back("Item"); + data_name_list.push_back("Long"); + data_name_list.push_back("New"); + data_name_list.push_back("Object"); + data_name_list.push_back("Option"); + data_name_list.push_back("Private"); + data_name_list.push_back("Public"); + data_name_list.push_back("ReDim"); + data_name_list.push_back("Single"); + data_name_list.push_back("Static"); + data_name_list.push_back("String"); + data_name_list.push_back("Time"); + data_name_list.push_back("Variant"); + + exec_name_list.push_back("Add"); + exec_name_list.push_back("AppActivate"); + exec_name_list.push_back("Asc"); + exec_name_list.push_back("Beep"); + exec_name_list.push_back("Call"); + exec_name_list.push_back("CBool"); + exec_name_list.push_back("CByte"); + exec_name_list.push_back("CCur"); + exec_name_list.push_back("CDate"); + exec_name_list.push_back("CDbl"); + exec_name_list.push_back("CDec"); + exec_name_list.push_back("CInt"); + exec_name_list.push_back("CStr"); + exec_name_list.push_back("CVar"); + exec_name_list.push_back("ChDir"); + exec_name_list.push_back("Clear"); + exec_name_list.push_back("Close"); + exec_name_list.push_back("Command"); + exec_name_list.push_back("CreateObject"); + exec_name_list.push_back("CurDir"); + exec_name_list.push_back("Dir"); + exec_name_list.push_back("Do"); + exec_name_list.push_back("DoEvents"); + exec_name_list.push_back("Else"); + exec_name_list.push_back("End"); + exec_name_list.push_back("Environ"); + exec_name_list.push_back("Erase"); + exec_name_list.push_back("Error"); + exec_name_list.push_back("Exit"); + exec_name_list.push_back("FileAttr"); + exec_name_list.push_back("FileCopy"); + exec_name_list.push_back("FileDateTime"); + exec_name_list.push_back("FileLen"); + exec_name_list.push_back("Fix"); + exec_name_list.push_back("For"); + exec_name_list.push_back("Format"); + exec_name_list.push_back("FreeFile"); + exec_name_list.push_back("Function"); + exec_name_list.push_back("Get"); + exec_name_list.push_back("GetAttr"); + exec_name_list.push_back("GetObject"); + exec_name_list.push_back("GoSub"); + exec_name_list.push_back("GoTo"); + exec_name_list.push_back("If"); + exec_name_list.push_back("Input"); + exec_name_list.push_back("InStr"); + exec_name_list.push_back("IsError"); + exec_name_list.push_back("Kill"); + exec_name_list.push_back("Left"); + exec_name_list.push_back("Len"); + exec_name_list.push_back("Line"); + exec_name_list.push_back("Loc"); + exec_name_list.push_back("Lock"); + exec_name_list.push_back("LOF"); + exec_name_list.push_back("Loop"); + exec_name_list.push_back("Mid"); + exec_name_list.push_back("MkDir"); + exec_name_list.push_back("Name"); + exec_name_list.push_back("Next"); + exec_name_list.push_back("Now"); + exec_name_list.push_back("On"); + exec_name_list.push_back("Open"); + exec_name_list.push_back("Print"); + exec_name_list.push_back("Put"); + exec_name_list.push_back("Raise"); + exec_name_list.push_back("Randomize"); + exec_name_list.push_back("Remove"); + exec_name_list.push_back("Reset"); + exec_name_list.push_back("Resume"); + exec_name_list.push_back("Return"); + exec_name_list.push_back("Right"); + exec_name_list.push_back("RmDir"); + exec_name_list.push_back("Rnd"); + exec_name_list.push_back("Seek"); + exec_name_list.push_back("Select"); + exec_name_list.push_back("SendKeys"); + exec_name_list.push_back("Server"); + exec_name_list.push_back("SetAttr"); + exec_name_list.push_back("Shell"); + exec_name_list.push_back("Spc"); + exec_name_list.push_back("Stop"); + exec_name_list.push_back("Str"); + exec_name_list.push_back("Switch"); + exec_name_list.push_back("Sub"); + exec_name_list.push_back("Tab"); + exec_name_list.push_back("Timer"); + exec_name_list.push_back("Unlock"); + exec_name_list.push_back("Val"); + exec_name_list.push_back("Wend"); + exec_name_list.push_back("While"); + exec_name_list.push_back("Width"); + exec_name_list.push_back("With"); + exec_name_list.push_back("Write"); + + math_func_list.push_back("Abs"); + math_func_list.push_back("Exp"); + math_func_list.push_back("Round"); + math_func_list.push_back("Rnd"); + math_func_list.push_back("Randomize"); + math_func_list.push_back("Sign"); + math_func_list.push_back("Sqrt"); + + trig_func_list.push_back("Atan"); + trig_func_list.push_back("Cos"); + trig_func_list.push_back("Sin"); + trig_func_list.push_back("Tan"); + + log_func_list.push_back("Log"); + + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("/"); + cmplx_calc_list.push_back("\\"); + cmplx_calc_list.push_back("^"); + + cmplx_cond_list.push_back("Case"); + cmplx_cond_list.push_back("Do"); + cmplx_cond_list.push_back("Else"); + cmplx_cond_list.push_back("ElseIf"); + cmplx_cond_list.push_back("For"); + cmplx_cond_list.push_back("If"); + cmplx_cond_list.push_back("Select"); + cmplx_cond_list.push_back("While"); + + cmplx_logic_list.push_back("<>"); + cmplx_logic_list.push_back(">"); + cmplx_logic_list.push_back("<"); + cmplx_logic_list.push_back(">="); + cmplx_logic_list.push_back("=<"); + cmplx_logic_list.push_back("And"); + cmplx_logic_list.push_back("Not"); + cmplx_logic_list.push_back("Or"); + cmplx_logic_list.push_back("Xor"); + cmplx_logic_list.push_back("AndAlso"); + cmplx_logic_list.push_back("OrElse"); + cmplx_logic_list.push_back("IsFalse"); + cmplx_logic_list.push_back("IsTrue"); + + cmplx_preproc_list.push_back("#Const"); + cmplx_preproc_list.push_back("#Else"); + cmplx_preproc_list.push_back("#ElseIf"); + cmplx_preproc_list.push_back("#End"); + cmplx_preproc_list.push_back("#ExternalSource"); + cmplx_preproc_list.push_back("#If"); + cmplx_preproc_list.push_back("#Region"); + + cmplx_assign_list.push_back("="); + + cmplx_cyclomatic_list.push_back("If"); + cmplx_cyclomatic_list.push_back("ElseIf"); + //cmplx_cyclomatic_list.push_back("IIf"); + cmplx_cyclomatic_list.push_back("For"); + cmplx_cyclomatic_list.push_back("While"); + cmplx_cyclomatic_list.push_back("Until"); + cmplx_cyclomatic_list.push_back("?"); + cmplx_cyclomatic_list.push_back("Catch"); + cmplx_cyclomatic_list.push_back("When"); + cmplx_cyclomatic_list.push_back("Case"); + + ignore_cmplx_cyclomatic_list.push_back("End If"); + ignore_cmplx_cyclomatic_list.push_back("End While"); + + cmplx_cyclomatic_logic_list.push_back("And"); + //cmplx_cyclomatic_logic_list.push_back("Not"); + cmplx_cyclomatic_logic_list.push_back("Or"); + //cmplx_cyclomatic_logic_list.push_back("Xor"); + cmplx_cyclomatic_logic_list.push_back("AndAlso"); + cmplx_cyclomatic_logic_list.push_back("OrElse"); + + cmplx_cyclomatic_case_list.push_back("Case"); + cmplx_cyclomatic_switch_list.push_back("Select Case"); + cmplx_cyclomatic_default_list.push_back("Select Case"); + cmplx_cyclomatic_default_list.push_back("Case Else"); +} + +/*! +* Counts directive lines of code. +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CVbCounter::CountDirectiveSLOC(filemap* fmap, results* result, filemap* /*fmapBak*/) +{ + bool contd = false, trunc_flag = false; + size_t idx, strSize; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string strDirLine = ""; + + for (filemap::iterator iter = fmap->begin(); iter!=fmap->end(); iter++) + { + if (CUtil::CheckBlank(iter->line)) + continue; + size_t lineNumber = iter->lineNumber; + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(" " + iter->line, directive, cnt, 1, exclude, "", "", &result->directive_count); + } + + if (!contd) + { + // if not a continuation of a previous directive + for(vector::iterator viter = directive.begin(); viter != directive.end(); viter++) + { + if ((idx = iter->line.find((*viter), 0)) != string::npos && idx == 0) + { + contd = true; + break; + } + } + if (contd) + { + result->directive_lines[PHY]++; + strSize = CUtil::TruncateLine(iter->line.length(), 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine = iter->line.substr(0, strSize); + } + } + else + { + // continuation of a previous directive + strSize = CUtil::TruncateLine(iter->line.length(), strDirLine.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine += "\n" + iter->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + + if (contd) + { + // drop continuation symbol + if (strDirLine[strDirLine.length()-1] == '\\') + strDirLine = strDirLine.substr(0, strDirLine.length()-1); + + // if a directive or continuation of a directive (no continuation symbol found) + if (iter->line[iter->line.length()-1] != '_') + { + contd = false; + if (result->addSLOC(strDirLine, lineNumber, trunc_flag)) + result->directive_lines[LOG]++; + } + iter->line = ""; + } + } + return 1; +} + +/*! +* Processes physical and logical lines according to language specific rules. +* NOTE: all the blank lines + +* whole line comments + +* lines with compiler directives +* should have been blanked from filemap by previous processing +* before reaching this function +* +* \param fmap list of processed file lines +* \param result counter results +* \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) +* +* \return method status +*/ +int CVbCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + string strLSLOC = ""; + string strLSLOCBak = ""; + + filemap::iterator fit, fitbak; + string line, lineBak; + size_t i, pos, prev_pos, strSize, tmpLoc; + unsigned int cnt = 0; + StringVector loopEnd; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string special = "[]()+/-*<>=,@&~!^?:%{}"; + + string tmp, tmpstr; + bool isDataLine = false; + bool line_continued = false; + bool line_skipped; + bool trunc_flag = false; + bool new_loop = false; + + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + line = fit->line; + lineBak = fitbak->line; + + if (CUtil::CheckBlank(line)) + continue; + size_t lineNumber = fit->lineNumber; + + // delete the line number + pos = line.find_first_of(":"); + if ((pos == 0) || (pos != string::npos && CUtil::IsInteger(line.substr(0,pos)))) + { + line = line.substr(pos + 1); + lineBak = lineBak.substr(pos + 1); + } + + prev_pos = 0; + + // check for inline If..Then + tmpstr = CUtil::TrimString(line); + tmpLoc = CUtil::FindKeyword(tmpstr, "Then"); + if (tmpLoc != string::npos) + { + if (tmpLoc < tmpstr.length() - 4) + tmpLoc += 3; + else + tmpLoc = string::npos; + } + + // record nested loops + if (print_cmplx) + { + new_loop = false; + if (CUtil::FindKeyword(tmpstr, "Do") == 0) + { + loopEnd.push_back("Loop"); + new_loop = true; + } + else if (CUtil::FindKeyword(tmpstr, "For") == 0) + { + loopEnd.push_back("Next"); + new_loop = true; + } + else if (CUtil::FindKeyword(tmpstr, "While") == 0) + { + loopEnd.push_back("Wend"); + new_loop = true; + } + else if (loopEnd.size() > 0) + { + if (CUtil::FindKeyword(tmpstr, loopEnd.back()) == 0) + loopEnd.pop_back(); + } + if (new_loop) + { + if ((unsigned int)result->cmplx_nestloop_count.size() < loopEnd.size()) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopEnd.size()-1]++; + } + } + + tmp = line; + for (i = 0; i < tmp.size(); i++) + { + if ((tmp[i] == ':') || (i == tmp.size() - 1) || tmpLoc != string::npos) + { + if (tmpLoc != string::npos) + { + i = tmpLoc; + tmpLoc = string::npos; + } + else + tmpstr = CUtil::TrimString(tmp.substr(prev_pos, i - prev_pos + 1)); + + // exclude SLOC defined in the exclude_keywords + line_skipped = false; + for (vector::iterator stri = exclude_keywords.begin(); stri != exclude_keywords.end(); stri++) + { + if (tmpstr.compare(*stri) == 0) + { + line_skipped = true; + break; + } + } + if (line_skipped) + continue; + + // exclude SLOC starting with Next, End + for (StringVector::iterator stri = exclude_start_keywords.begin(); stri != exclude_start_keywords.end(); stri++) + { + if (CUtil::FindKeyword(tmpstr, *stri) == 0) + { + line_skipped = true; + break; + } + } + if (line_skipped) + continue; + + strSize = CUtil::TruncateLine(i + 1 - prev_pos, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(prev_pos, strSize); + strLSLOCBak += lineBak.substr(prev_pos, strSize); + } + + line_continued = ((i == tmp.size() - 1) && tmpstr[tmpstr.length() - 1] == CONTINUATION_CHAR); + if (line_continued) + { + // drop continuation symbol + if (strLSLOC[strLSLOC.length()-1] == '_') + { + strLSLOC = strLSLOC.substr(0, strLSLOC.length()-1); + strLSLOCBak = strLSLOCBak.substr(0, strLSLOCBak.length()-1); + } + continue; + } + + isDataLine = false; + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + { + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", &result->data_name_count); + if (cnt > 0) + { + isDataLine = true; + result->data_lines[LOG]++; + } + else + { + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(strLSLOC, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count); + } + result->exec_lines[LOG]++; + } + } + strLSLOCBak = ""; + strLSLOC = ""; + prev_pos = i + 1; + + if (tmpLoc != string::npos) + { + tmpLoc = string::npos; + prev_pos++; + } + } + + if (special.find_first_of(tmp[i]) != string::npos) + tmp[i] = ' '; + } + + if (isDataLine) + result->data_lines[PHY]++; + else + result->exec_lines[PHY]++; + } + return 1; +} + +/*! +* Parses lines for function/method names. +* +* \param line line to be processed +* \param functionStack stack of functions +* \param functionName function name found +* \param functionCount function count found +* +* \return 1 if function name is found +*/ +int CVbCounter::ParseFunctionName(const string &line, string &/*lastline*/, + filemap &functionStack, string &functionName, unsigned int &functionCount) +{ + string str; + size_t idx; + unsigned int fcnt; + + idx = CUtil::FindKeyword(line, "Sub"); + if (idx != string::npos) + { + if (idx + 4 < line.length()) + { + str = line.substr(idx + 4); + lineElement element(++functionCount, str); + functionStack.push_back(element); + } + } + else + { + idx = CUtil::FindKeyword(line, "Function"); + if (idx != string::npos) + { + if (idx + 9 < line.length()) + { + str = line.substr(idx + 9); + lineElement element(++functionCount, str); + functionStack.push_back(element); + } + } + } + + if (functionStack.empty()) + { + // dealing with some code out of any subroutines, it a "main" code + return 2; + } + + idx = CUtil::FindKeyword(line, "End Sub"); + if (idx != string::npos) + { + str = functionStack.back().line; + fcnt = functionStack.back().lineNumber; + functionStack.pop_back(); + idx = str.find("("); + if (idx != string::npos) + { + functionName = CUtil::ClearRedundantSpaces(str.substr(0, idx)); + functionCount = fcnt; + return 1; + } + } + else + { + idx = CUtil::FindKeyword(line, "End Function"); + if (idx != string::npos) + { + str = functionStack.back().line; + fcnt = functionStack.back().lineNumber; + functionStack.pop_back(); + idx = str.find("("); + if (idx != string::npos) + { + functionName = CUtil::ClearRedundantSpaces(str.substr(0, idx)); + functionCount = fcnt; + return 1; + } + } + } + return 0; +} diff --git a/src/CVbCounter.h b/src/CVbCounter.h new file mode 100644 index 0000000..0d0606d --- /dev/null +++ b/src/CVbCounter.h @@ -0,0 +1,45 @@ +//! Code counter class definition for the Visual Basic language. +/*! +* \file CVbCounter.h +* +* This file contains the code counter class definition for the Visual Basic language. +*/ + +#ifndef CVbCounter_h +#define CVbCounter_h + +#include "CCodeCounter.h" + +//! Visual Basic code counter class. +/*! +* \class CVbCounter +* +* Defines the Visual Basic code counter class. +*/ +class CVbCounter : public CCodeCounter +{ +public: + CVbCounter(); + +protected: + virtual int CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak = NULL); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak = NULL); + int ParseFunctionName(const string &line, string &lastline, + filemap &functionStack, string &functionName, unsigned int &functionCount); + + StringVector exclude_start_keywords; //!< SLOC lines excluded from counts starting with keywords + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CVbCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CVbCounter(const CVbCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CVbCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CVbCounter operator=(const CVbCounter); // Declare without implementation}; +}; + +#endif diff --git a/src/CVbscriptCounter.cpp b/src/CVbscriptCounter.cpp new file mode 100644 index 0000000..ca48ba0 --- /dev/null +++ b/src/CVbscriptCounter.cpp @@ -0,0 +1,150 @@ +//! Code counter class methods for the VBScript language. +/*! +* \file CVbscriptCounter.cpp +* +* This file contains the code counter class methods for the VBScript language. +*/ + +#include "CVbscriptCounter.h" + +#define CONTINUATION_CHAR '_' + +/*! +* Constructs a CVbscriptCounter object. +*/ +CVbscriptCounter::CVbscriptCounter() +{ + classtype = VBSCRIPT; + language_name = "VBScript"; + + file_extension.clear(); + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("VBScript", file_extension); + /* + file_extension.push_back(".vbs");*/ +} + +/*! +* Perform preprocessing of file lines before counting. +* Replace quote stuffing in literals '' or "" to avoid quote matching problems. +* +* \param fmap list of file lines +* +* \return method status +*/ +int CVbscriptCounter::PreCountProcess(filemap* fmap) +{ + size_t i; + filemap::iterator fit; + for (fit = fmap->begin(); fit != fmap->end(); fit++) + { + if (fit->line.empty()) + continue; + //int idx = fit->line.find("

"); + size_t idx = fit->line.find("

"); // warning fix + if (idx != string::npos) + { + //int idx1 = fit->line.find("

"); + size_t idx1 = fit->line.find("

"); + if (idx != string::npos) + { + for (i = idx + 4; i < idx1; i++) + { + fit->line[i] = '$'; + } + } + else { + for (i = idx + 4; i < fit->line.length(); i++) + { + fit->line[i] = '$'; + } + } + } + } + return 0; +} + +/*! +* Constructs a CVbsPhpCounter object. +*/ +CVbsPhpCounter::CVbsPhpCounter() +{ + classtype = VBS_PHP; + language_name = "VBScript/PHP"; + + file_extension.clear(); + file_extension.push_back(".*vbsphp"); +} + +/*! +* Constructs a CVbsHtmlCounter object. +*/ +CVbsHtmlCounter::CVbsHtmlCounter() +{ + classtype = VBS_HTML; + language_name = "VBScript/HTML"; + + file_extension.clear(); + file_extension.push_back(".*vbshtm"); +} + +/*! +* Constructs a CVbsXmlCounter object. +*/ +CVbsXmlCounter::CVbsXmlCounter() +{ + classtype = VBS_XML; + language_name = "VBScript/XML"; + + file_extension.clear(); + file_extension.push_back(".*vbsxml"); +} + +/*! +* Constructs a CVbsJspCounter object. +*/ +CVbsJspCounter::CVbsJspCounter() +{ + classtype = VBS_JSP; + language_name = "VBScript/JSP"; + + file_extension.clear(); + file_extension.push_back(".*vbsjsp"); +} + +/*! +* Constructs a CVbsAspServerCounter object. +*/ +CVbsAspServerCounter::CVbsAspServerCounter() +{ + classtype = VBS_ASP_S; + language_name = "VBScript/ASP Server"; + + file_extension.clear(); + file_extension.push_back(".*vbsasps"); +} + +/*! +* Constructs a CVbsAspClientCounter object. +*/ +CVbsAspClientCounter::CVbsAspClientCounter() +{ + classtype = VBS_ASP_C; + language_name = "VBScript/ASP Client"; + + file_extension.clear(); + file_extension.push_back(".*vbsaspc"); +} + +/*! +* Constructs a CVbsColdFusionCounter object. +*/ +CVbsColdFusionCounter::CVbsColdFusionCounter() +{ + classtype = VBS_CFM; + language_name = "VBScript/ColdFusion"; + + file_extension.clear(); + file_extension.push_back(".*vbscfm"); +} diff --git a/src/CVbscriptCounter.h b/src/CVbscriptCounter.h new file mode 100644 index 0000000..6146b6c --- /dev/null +++ b/src/CVbscriptCounter.h @@ -0,0 +1,206 @@ +//! Code counter class definition for the VBScript language. +/*! +* \file CVbscriptCounter.h +* +* This file contains the code counter class definition for the VBScript language. +*/ + +#ifndef CVbscriptCounter_h +#define CVbscriptCounter_h + +#include "CVbCounter.h" + +//! Visual Basic code counter class. +/*! +* \class CVbscriptCounter +* +* Defines the Visual Basic code counter class. +*/ +class CVbscriptCounter : public CVbCounter +{ +public: + CVbscriptCounter(); + int PreCountProcess(filemap* fmap); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CVbscriptCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CVbscriptCounter(const CVbscriptCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CVbscriptCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CVbscriptCounter operator=(const CVbscriptCounter); // Declare without implementation +}; + +//! VBScript in PHP code counter class. +/*! +* \class CVbsPhpCounter +* +* Defines the VBScript in PHP code counter class. +*/ +class CVbsPhpCounter : public CVbscriptCounter +{ +public: + CVbsPhpCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CVbsPhpCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CVbsPhpCounter(const CVbsPhpCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CVbsPhpCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CVbsPhpCounter operator=(const CVbsPhpCounter); // Declare without implementation +}; + +//! VBScript in HTML code counter class. +/*! +* \class CVbsHtmlCounter +* +* Defines the VBScript in HTML code counter class. +*/ +class CVbsHtmlCounter : public CVbscriptCounter +{ +public: + CVbsHtmlCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CVbsHtmlCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CVbsHtmlCounter(const CVbsHtmlCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CVbsHtmlCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CVbsHtmlCounter operator=(const CVbsHtmlCounter); // Declare without implementation +}; + +//! VBScript in XML code counter class. +/*! +* \class CVbsXmlCounter +* +* Defines the VBScript in XML code counter class. +*/ +class CVbsXmlCounter : public CVbscriptCounter +{ +public: + CVbsXmlCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CVbsXmlCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CVbsXmlCounter(const CVbsXmlCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CVbsXmlCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CVbsXmlCounter operator=(const CVbsXmlCounter); // Declare without implementation +}; + +//! VBScript in JSP code counter class. +/*! +* \class CVbsJspCounter +* +* Defines the VBScript in JSP code counter class. +*/ +class CVbsJspCounter : public CVbscriptCounter +{ +public: + CVbsJspCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CVbsJspCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CVbsJspCounter(const CVbsJspCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CVbsJspCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CVbsJspCounter operator=(const CVbsJspCounter); // Declare without implementation +}; + +//! VBScript in ASP server code counter class. +/*! +* \class CVbsAspServerCounter +* +* Defines the VBScript in ASP server code counter class. +*/ +class CVbsAspServerCounter : public CVbscriptCounter +{ +public: + CVbsAspServerCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CVbsAspServerCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CVbsAspServerCounter(const CVbsAspServerCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CVbsAspServerCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CVbsAspServerCounter operator=(const CVbsAspServerCounter); // Declare without implementation +}; + +//! VBScript in ASP client code counter class. +/*! +* \class CVbsAspClientCounter +* +* Defines the VBScript in ASP client code counter class. +*/ +class CVbsAspClientCounter : public CVbscriptCounter +{ +public: + CVbsAspClientCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CVbsAspClientCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CVbsAspClientCounter(const CVbsAspClientCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CVbsAspClientCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CVbsAspClientCounter operator=(const CVbsAspClientCounter); // Declare without implementation +}; + +//! VBScript in ColdFusion code counter class. +/*! +* \class CVbsColdFusionCounter +* +* Defines the VBScript in ColdFusion code counter class. +*/ +class CVbsColdFusionCounter : public CVbscriptCounter +{ +public: + CVbsColdFusionCounter(); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CVbsColdFusionCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CVbsColdFusionCounter(const CVbsColdFusionCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CVbsColdFusionCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CVbsColdFusionCounter operator=(const CVbsColdFusionCounter); // Declare without implementation +}; + +#endif diff --git a/src/CVerilogCounter.cpp b/src/CVerilogCounter.cpp new file mode 100644 index 0000000..21ae336 --- /dev/null +++ b/src/CVerilogCounter.cpp @@ -0,0 +1,1307 @@ +//! Code counter class methods for the Verilog language. +/*! + * \file CVerilogCounter.cpp + * + * This file contains the code counter class methods for the Verilog hardware definition language (used in FPGA programming). + */ + +#include "CVerilogCounter.h" +#include + +/*! + * Constructs a CCCounter object. + */ +CVerilogCounter::CVerilogCounter() +{ + classtype = VERILOG; + language_name = "Verilog"; + + //Modification: 11.2016 Ext-4 + file_extension = CUtil::getExtensionsToLanguage("Verilog", file_extension); + /*file_extension.push_back(".v"); + file_extension.push_back(".sv"); + file_extension.push_back(".svi"); + file_extension.push_back(".vlib"); + file_extension.push_back(".svlib"); + file_extension.push_back(".vh"); + file_extension.push_back(".svh");*/ + + LineCommentStart.push_back("//"); + BlockCommentStart.push_back("/*"); + BlockCommentEnd.push_back("*/"); + QuoteStart = "\""; + QuoteEnd = "\""; + QuoteEscapeFront = '\"'; + + directive.push_back("`define"); + directive.push_back("`include"); + directive.push_back("`ifdef"); + directive.push_back("`else"); + directive.push_back("`endif"); + directive.push_back("`timescale"); + + data_name_list.push_back("endfunction"); + data_name_list.push_back("endmodule"); + data_name_list.push_back("endtask"); + data_name_list.push_back("event"); + data_name_list.push_back("function"); + data_name_list.push_back("genvar"); + data_name_list.push_back("inout"); + data_name_list.push_back("input"); + data_name_list.push_back("integer"); + data_name_list.push_back("localparam"); + data_name_list.push_back("module"); + data_name_list.push_back("output"); + data_name_list.push_back("parameter"); + data_name_list.push_back("reg"); + data_name_list.push_back("specparam"); + data_name_list.push_back("supply0"); + data_name_list.push_back("supply1"); + data_name_list.push_back("task"); + data_name_list.push_back("time"); + data_name_list.push_back("tri"); + data_name_list.push_back("tri0"); + data_name_list.push_back("tri1"); + data_name_list.push_back("triand"); + data_name_list.push_back("trior"); + data_name_list.push_back("trireg"); + data_name_list.push_back("wand"); + data_name_list.push_back("wire"); + data_name_list.push_back("wor"); + + exec_name_list.push_back("always"); + exec_name_list.push_back("assign"); + exec_name_list.push_back("begin"); + exec_name_list.push_back("case"); + exec_name_list.push_back("casex"); + exec_name_list.push_back("casez"); + exec_name_list.push_back("deassign"); + exec_name_list.push_back("defparam"); + exec_name_list.push_back("disable"); + exec_name_list.push_back("end"); + exec_name_list.push_back("endcase"); + exec_name_list.push_back("for"); + exec_name_list.push_back("forever"); + exec_name_list.push_back("fork"); + exec_name_list.push_back("generate"); + exec_name_list.push_back("if"); + exec_name_list.push_back("else if"); + exec_name_list.push_back("else"); + exec_name_list.push_back("initial"); + exec_name_list.push_back("join"); + exec_name_list.push_back("posedge"); + exec_name_list.push_back("repeat"); + exec_name_list.push_back("wait"); + exec_name_list.push_back("while"); + + exec_name_list.push_back("$bitstoreal"); + exec_name_list.push_back("$display"); + exec_name_list.push_back("$dumpall"); + exec_name_list.push_back("$dumpfile"); + exec_name_list.push_back("$dumpflush"); + exec_name_list.push_back("$dumplimit"); + exec_name_list.push_back("$dumpoff"); + exec_name_list.push_back("$dumpon"); + exec_name_list.push_back("$dumpvar"); + exec_name_list.push_back("$dumpvars"); + exec_name_list.push_back("$fclose"); + exec_name_list.push_back("$fdisplay"); + exec_name_list.push_back("$finish"); + exec_name_list.push_back("$fmonitor"); + exec_name_list.push_back("$fopen"); + exec_name_list.push_back("$fstrobe"); + exec_name_list.push_back("$fwrite"); + exec_name_list.push_back("$itor"); + exec_name_list.push_back("$monitor"); + exec_name_list.push_back("$monitoroff"); + exec_name_list.push_back("$monitoron"); + exec_name_list.push_back("$printtimescale"); + exec_name_list.push_back("$random"); + exec_name_list.push_back("$readmemb"); + exec_name_list.push_back("$readmemh"); + exec_name_list.push_back("$realtime"); + exec_name_list.push_back("$realtobits"); + exec_name_list.push_back("$rtoi"); + exec_name_list.push_back("$scale"); + exec_name_list.push_back("$shm_open"); + exec_name_list.push_back("$shm_probe"); + exec_name_list.push_back("$stime"); + exec_name_list.push_back("$stop"); + exec_name_list.push_back("$strobe"); + exec_name_list.push_back("$time"); + exec_name_list.push_back("$timeformat"); + exec_name_list.push_back("$write"); + exec_name_list.push_back("@"); + + exec_name_list.push_back("buf"); + exec_name_list.push_back("not"); + exec_name_list.push_back("and"); + exec_name_list.push_back("or"); + exec_name_list.push_back("nand"); + exec_name_list.push_back("nor"); + exec_name_list.push_back("xor"); + exec_name_list.push_back("xnor"); + exec_name_list.push_back("bufif0"); + exec_name_list.push_back("bufif1"); + exec_name_list.push_back("notif0"); + exec_name_list.push_back("notif1"); + + cmplx_calc_list.push_back("+"); + cmplx_calc_list.push_back("-"); + cmplx_calc_list.push_back("*"); + cmplx_calc_list.push_back("/"); + cmplx_calc_list.push_back("%"); + + cmplx_cond_list.push_back("?"); + cmplx_cond_list.push_back("case"); + cmplx_cond_list.push_back("if"); + cmplx_cond_list.push_back("else"); + cmplx_cond_list.push_back("else if"); + cmplx_cond_list.push_back("for"); + cmplx_cond_list.push_back("forever"); + cmplx_cond_list.push_back("repeat"); + cmplx_cond_list.push_back("while"); + + cmplx_logic_list.push_back("!"); + cmplx_logic_list.push_back("~"); + cmplx_logic_list.push_back("&"); + cmplx_logic_list.push_back("|"); + cmplx_logic_list.push_back("^"); + cmplx_logic_list.push_back("~&"); + cmplx_logic_list.push_back("~|"); + cmplx_logic_list.push_back("~^"); + cmplx_logic_list.push_back("<<"); + cmplx_logic_list.push_back(">>"); + cmplx_logic_list.push_back("<"); + cmplx_logic_list.push_back(">"); + cmplx_logic_list.push_back(">="); + cmplx_logic_list.push_back("=="); + cmplx_logic_list.push_back("!="); + cmplx_logic_list.push_back("==="); + cmplx_logic_list.push_back("!=="); + cmplx_logic_list.push_back("^~"); + cmplx_logic_list.push_back("&&"); + cmplx_logic_list.push_back("||"); + cmplx_logic_list.push_back("<="); + + cmplx_preproc_list.push_back("`define"); + cmplx_preproc_list.push_back("`include"); + cmplx_preproc_list.push_back("`ifdef"); + cmplx_preproc_list.push_back("`else"); + cmplx_preproc_list.push_back("`endif"); + cmplx_preproc_list.push_back("`timescale"); + + cmplx_assign_list.push_back("="); + cmplx_assign_list.push_back("<="); + + /* COMPLEXITY MODULE STRUCTURES: SPRING 2014 */ + + math_func_list.push_back("ln"); + math_func_list.push_back("log"); + math_func_list.push_back("exp"); + math_func_list.push_back("sqrt"); + math_func_list.push_back("min"); + math_func_list.push_back("max"); + math_func_list.push_back("abs"); + math_func_list.push_back("pow"); + + trig_func_list.push_back("sin"); + trig_func_list.push_back("cos"); + trig_func_list.push_back("tan"); + trig_func_list.push_back("asin"); + trig_func_list.push_back("acos"); + trig_func_list.push_back("atan"); + trig_func_list.push_back("atan2"); + trig_func_list.push_back("hypot"); + trig_func_list.push_back("sinh"); + trig_func_list.push_back("cosh"); + trig_func_list.push_back("tanh"); + trig_func_list.push_back("asinh"); + trig_func_list.push_back("acosh"); + trig_func_list.push_back("atanh"); + + /* log_func_list.push_back("math.log"); */ + + /* COMPLEXITY MODULE STRUCTURES: SPRING 2014 */ + + + /* COMPLEXITY MODULE STRUCTURES: SPRING 2014 */ + cmplx_cyclomatic_list.push_back("?"); + cmplx_cyclomatic_list.push_back("if"); //Would take care of nested if-else statements, as here, it is of the form: "else if" + cmplx_cyclomatic_list.push_back("for"); + //cmplx_cyclomatic_list.push_back("case"); //need special process + //cmplx_cyclomatic_list.push_back("casex"); //need special process + //cmplx_cyclomatic_list.push_back("casez"); //need special process + //cmplx_cyclomatic_list.push_back(":"); //Checks for case, casex, casez statements, the choices possible + cmplx_cyclomatic_list.push_back("wait"); + cmplx_cyclomatic_list.push_back("while"); + cmplx_cyclomatic_list.push_back("foreach"); + + /* COMPLEXITY MODULE STRUCTURES: SPRING 2014 */ + + cmplx_cyclomatic_logic_list.push_back("&&"); + cmplx_cyclomatic_logic_list.push_back("||"); + + cmplx_cyclomatic_case_list.push_back("case"); + cmplx_cyclomatic_case_list.push_back("casex"); + cmplx_cyclomatic_case_list.push_back("casez"); + +} + + +/*! + * Counts directive lines of code. + * + * \param fmap list of processed file lines + * \param result counter results + * \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) + * + * \return method status + */ +int CVerilogCounter::CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak) +{ + bool contd = false, trunc_flag = false; + size_t idx, strSize; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + string strDirLine = ""; + + filemap::iterator itfmBak = fmapBak->begin(); + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++, itfmBak++) + { + if (CUtil::CheckBlank(iter->line)) + continue; + size_t lineNumber = iter->lineNumber; + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(" " + iter->line, directive, cnt, 1, exclude, "", "", &result->directive_count); + } + + if (!contd) + { + // if not a continuation of a previous directive + for (vector::iterator viter = directive.begin(); viter != directive.end(); viter++) + { + // ensures the keyword stands alone, avoid, e.g., #ifabc + if (((idx = CUtil::FindKeyword(iter->line, *viter)) != string::npos) && idx == 0) + { + contd = true; + break; + } + } + if (contd) + { + strSize = CUtil::TruncateLine(itfmBak->line.length(), 0, this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine = itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + } + else + { + // continuation of a previous directive + strSize = CUtil::TruncateLine(itfmBak->line.length(), strDirLine.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + strDirLine += "\n" + itfmBak->line.substr(0, strSize); + result->directive_lines[PHY]++; + } + + if (contd) + { + contd = false; + if (result->addSLOC(strDirLine, lineNumber, trunc_flag)) + result->directive_lines[LOG]++; + iter->line = ""; + } + } + return 1; +} + +/*! + * Processes physical and logical lines according to language specific rules. + * NOTE: all the blank lines + + * whole line comments + + * lines with compiler directives + * should have been blanked from filemap by previous processing + * before reaching this function + * + * \param fmap list of processed file lines + * \param result counter results + * \param fmapBak list of original file lines (same as fmap except it contains unmodified quoted strings) + * + * \return method status + */ +int CVerilogCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak) +{ + unsigned int paren_count = 0; + bool for_flag = false; + bool always_flag = false; + bool case_flag = false; + bool repeat_flag = false; + bool found_for = false; + bool found_forifwhile = false; + bool found_while = false; + char prev_char = 0; + bool data_continue = false; + string strLSLOC = ""; + string strLSLOCBak = ""; + + filemap::iterator fit, fitbak; + string line, lineBak; + StringVector loopLevel; + + unsigned int phys_exec_lines = 0; + unsigned int phys_data_lines = 0; + unsigned int temp_lines = 0; + unsigned int cnt = 0; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$"; + + for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++) + { + line = fit->line; + + // insert blank at the beginning (for searching keywords) + line = ' ' + line; + lineBak = ' ' + fitbak->line; + + // do not process blank lines + // blank line means blank_line/comment_line/directive + if (!CUtil::CheckBlank(line)) + { + LSLOC(result, line, fit->lineNumber, lineBak, strLSLOC, strLSLOCBak, paren_count, for_flag, found_forifwhile, + found_while, prev_char, data_continue, temp_lines, phys_exec_lines, phys_data_lines, found_for, + loopLevel, always_flag, case_flag, repeat_flag); + + if (print_cmplx) + { + cnt = 0; + CUtil::CountTally(line, exec_name_list, cnt, 1, exclude, "", "", &result->exec_name_count); + } + + result->exec_lines[PHY] += phys_exec_lines; + phys_exec_lines = 0; + + result->data_lines[PHY] += phys_data_lines; + phys_data_lines = 0; + } + } + return 1; +} + +/*! + * Extracts and stores logical lines of code. + * Determines and extract logical SLOC to place in the result variable + * using addSLOC function. Each time the addSLOC function is called, + * a new logical SLOC is added. This function assumes that the directive + * is handled before it is called. + * + * \param result counter results + * \param line processed physical line of code + * \param lineBak original physical line of code + * \param strLSLOC processed logical string + * \param strLSLOCBak original logical string + * \param paren_cnt count of parenthesis + * \param forflag found for flag + * \param found_forifwhile found for, if, or while flag + * \param found_while found while flag + * \param prev_char previous character + * \param data_continue continuation of a data declaration line + * \param temp_lines tracks physical line count + * \param phys_exec_lines number of physical executable lines + * \param phys_data_lines number of physical data lines + * \param found_for found for loop + * \param loopLevel nested loop level + * \param always_flag found always + * \param case_flag found case + * \param repeat_flag found repeat + */ +void CVerilogCounter::LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + unsigned int &paren_cnt, bool &forflag, bool &found_forifwhile, bool &found_while, char &prev_char, + bool &data_continue, unsigned int &temp_lines, unsigned int &phys_exec_lines, unsigned int &phys_data_lines, + bool &found_for, StringVector &loopLevel, bool &always_flag, bool &case_flag, bool &repeat_flag) +{ + // paren_cnt is used with 'for' statement only + size_t start = 0, startmax = 0; // starting index of the working string + size_t i = 0, strSize; + bool trunc_flag = false; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$:"; + string dataExclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$:()."; // avoid double count of casts as data and executable lines (e.g. set { m_uiValue = (uint)value; } + bool found_end = false, found_endcase = false; + unsigned int cnt = 0; + bool newline = true; + + string tmp = CUtil::TrimString(strLSLOC); + size_t tmpi; + + // there may be more than 1 logical SLOC in this line + while (i < line.length()) + { + tmp = CUtil::TrimString(line.substr(start, i + 1 - start)); + if (CUtil::FindKeyword(tmp, "end") != string::npos && loopLevel.size() > 0 && loopLevel.back().compare("begin") == 0) + { + loopLevel.pop_back(); // pop begin + loopLevel.pop_back(); // pop looping + start = i + 1; + } + if ((tmpi = CUtil::FindKeyword(line.substr(start, i + 1 - start), "generate")) != string::npos) + { + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + strLSLOCBak = strLSLOC = ""; + phys_exec_lines = temp_lines; + temp_lines = 0; + start = start + 7 + tmpi; + found_forifwhile = false; + forflag = false; + found_for = false; + always_flag = false; + case_flag = false; + repeat_flag = false; + } + + if ((tmpi = CUtil::FindKeyword(line.substr(start, i + 1 - start), "forever")) != string::npos) + { + if (print_cmplx) + { + tmp = CUtil::TrimString(line.substr(start, i + 1 - start)); + tmp = strLSLOC + " " + tmp; + if (CUtil::FindKeyword(tmp, "begin") != string::npos && loopLevel.size() > 0 && loopLevel.back().compare("looping") == 0) + { + loopLevel.push_back("begin"); + } + else if (loopLevel.size() > 0) + { + // didn't find begin, so pop off since no longer in a looping block + loopLevel.pop_back(); + } + loopLevel.push_back("looping"); + // forever doesn't have any conditions so just add it to sloc + unsigned int loopCnt = 0; + for (StringVector::iterator lit = loopLevel.begin(); lit < loopLevel.end(); lit++) + { + if ((*lit) != "begin") + loopCnt++; + } + if ((unsigned int)result->cmplx_nestloop_count.size() < loopCnt) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopCnt-1]++; + } + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + strLSLOCBak = strLSLOC = ""; + phys_exec_lines = temp_lines; + temp_lines = 0; + start = start + 7 + tmpi; + found_forifwhile = true; + forflag = false; + found_for = false; + always_flag = false; + case_flag = false; + repeat_flag = false; + } + + switch (line[i]) + { + case ';': // LSLOC terminators + // ';' for normal executable or declaration statement + // '{' for starting a function or 'do' stmt or a block (which is counted) + // get the previous logical mark until i-1 index is the new LSLOC + // except 'do' precedes '{' + // except '}' precedes ';' ?? + // do nothing inside 'for' statement + if (found_for == true && paren_cnt > 0 && line[i] == ';') + break; + + // record open bracket for nested loop processing + if (print_cmplx) + { + tmp = CUtil::TrimString(line.substr(start, i + 1 - start)); + tmp = strLSLOC + " " + tmp; + + if (CUtil::FindKeyword(tmp, "begin") != string::npos && loopLevel.size() > 0 && loopLevel.back().compare("looping") == 0) + { + loopLevel.push_back("begin"); + } + else if (loopLevel.size() > 0 && loopLevel.back().compare("begin") != 0) // check that this isn't already in a begin block...if it is leave it alone + { + // didn't find begin, so pop off since no longer in a looping block + loopLevel.pop_back(); + } + } + // case 'while(...);', 'while(...) {', and '} while(...);' + // this case is handled in case ')' + if (found_while && found_forifwhile) + { + found_while = false; + found_forifwhile = false; + start = i + 1; + break; + } + + // check for empty statement (=1 LSLOC) + if (CUtil::TrimString(line.substr(start, i + 1 - start)) == ";" && strLSLOC.length() < 1) + { + strLSLOC = ";"; + strLSLOCBak = ";"; + } + else + { + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + { + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, dataExclude, "", "", &result->data_name_count); + + temp_lines++; + if (data_continue == true && line[i] == ';') + { + result->data_lines[LOG]++; + phys_data_lines = temp_lines; + } + else + { + if (cnt > 0 && line[i] == ';' ) + { + result->data_lines[LOG]++; + if (newline) + { + // only add a physical line once per line, otherwise a line of code might have multiple physical data and exec lines + phys_data_lines = temp_lines; + newline = false; + } + } + else + { + result->exec_lines[LOG]++; + if (newline) + { + // only add a physical line once per line, otherwise a line of code might have multiple physical data and exec lines + phys_exec_lines = temp_lines; + newline = false; + } + } + } + } + else if (data_continue == true && line[i] == ';') + phys_data_lines = temp_lines; + else + phys_exec_lines = temp_lines; + data_continue = false; + temp_lines = 0; + strLSLOC = strLSLOCBak = ""; + start = i + 1; + + // reset some flagging parameters + forflag = false; + paren_cnt = 0; + found_while = false; + found_forifwhile = false; + found_for = false; + + break; + case '(': + tmp = CUtil::TrimString(line.substr(start, i)); + if (CUtil::FindKeyword(tmp, "always") != string::npos) + { + // found always + paren_cnt++; + always_flag = true; + } + if (CUtil::FindKeyword(tmp, "case") != string::npos || CUtil::FindKeyword(tmp, "casex") != string::npos || CUtil::FindKeyword(tmp, "casez") != string::npos) + { + // found case + paren_cnt++; + case_flag = true; + } + if (forflag) + paren_cnt++; + else + { + // handle 'for', 'while', 'if', 'repeat' the same way + + if (CUtil::FindKeyword(tmp, "for") != string::npos + || CUtil::FindKeyword(tmp, "while")!= string::npos + || CUtil::FindKeyword(tmp, "if") != string::npos + || CUtil::FindKeyword(tmp, "repeat") != string::npos) + { + forflag = true; + paren_cnt++; + + if (print_cmplx) + { + tmp = CUtil::TrimString(line.substr(start, i + 1 - start)); + tmp = strLSLOC + " " + tmp; + if (CUtil::FindKeyword(tmp, "begin") != string::npos && loopLevel.size() > 0 && loopLevel.back().compare("looping") == 0) + { + loopLevel.push_back("begin"); + } + else if (loopLevel.size() > 0 && loopLevel.back().compare("begin") != 0) + { + // didn't find begin, so pop off since no longer in a looping block + loopLevel.pop_back(); + } + } + + if (CUtil::FindKeyword(tmp, "for") != string::npos) + { + if (print_cmplx) + loopLevel.push_back("looping"); + found_for = true; + } + else if (CUtil::FindKeyword(tmp, "while")!= string::npos) + { + if (print_cmplx) + loopLevel.push_back("looping"); + found_while = true; + } + else if (CUtil::FindKeyword(tmp, "repeat")!= string::npos) + { + if (print_cmplx) + loopLevel.push_back("looping"); + repeat_flag = true; + } + + + // record nested loop level + if (print_cmplx) + { + if (CUtil::FindKeyword(tmp, "if") == string::npos) + { + unsigned int loopCnt = 0; + for (StringVector::iterator lit = loopLevel.begin(); lit < loopLevel.end(); lit++) + { + if ((*lit) != "begin") + loopCnt++; + } + if ((unsigned int)result->cmplx_nestloop_count.size() < loopCnt) + result->cmplx_nestloop_count.push_back(1); + else + result->cmplx_nestloop_count[loopCnt-1]++; + } + } + } + } + break; + case ')': + if (always_flag || case_flag || repeat_flag || forflag) + { + if (paren_cnt > 0) + paren_cnt--; + if (paren_cnt == 0) + { + // handle always @ + strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0) + { + strLSLOC += line.substr(start, strSize); + strLSLOCBak += lineBak.substr(start, strSize); + } + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + strLSLOCBak = strLSLOC = ""; + phys_exec_lines = temp_lines; + temp_lines = 0; + start = i + 1; + found_forifwhile = true; + forflag = false; + found_for = false; + always_flag = false; + case_flag = false; + repeat_flag = false; + } + } + break; + } + + if (line[i] != ' ' && line[i] != '\t') + { + // if ;}}} --> don't count }}} at all + // also, if {}}} --> don't count }}} at all + // if ( !(line[i] == '}' && (prev_char == ';' || prev_char == '{'))) // see case '}' above + prev_char = line[i]; + + // change to not found if a char appears before + if (line[i] != ')' && found_forifwhile) + found_forifwhile = false; + } + i++; + } + + // don't save end statements to add to next sloc, they will be counted as physical sloc but not logical + tmp = CUtil::TrimString(line.substr(start, i - start)); + if ((tmpi = CUtil::FindKeyword(tmp, "endcase")) != string::npos) + { + startmax = max((start + tmpi + 8), startmax); + found_endcase = true; + } + if ((tmpi = CUtil::FindKeyword(tmp, "endmodule")) != string::npos) + { + startmax = max((start + tmpi + 10), startmax); + } + if ((tmpi = CUtil::FindKeyword(tmp, "endtask")) != string::npos) + { + startmax = max((start + tmpi + 8), startmax); + } + if ((tmpi = CUtil::FindKeyword(tmp, "endfunction")) != string::npos) + { + startmax = max((start + tmpi + 12), startmax); + } + if ((tmpi = CUtil::FindKeyword(tmp, "end")) != string::npos) + { + startmax = max((start + tmpi + 4), startmax); + found_end = true; // this is to catch any empty begin and end statements + } + if (startmax != 0) start = min(i, startmax); // if we found and end statement update start to be the max of i and startmax + + tmp = CUtil::TrimString(line.substr(start, i - start)); + strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), this->lsloc_truncate, trunc_flag); + if (strSize > 0 || (strLSLOC.size() > 0 && found_end)) + { + strLSLOC += tmp.substr(0, strSize); + tmp = CUtil::TrimString(lineBak.substr(start, i - start)); + strLSLOCBak += tmp.substr(0, strSize); + + if (found_end) + { + found_end = false; + if (strLSLOC.compare(strLSLOCBak) != 0) + { + if (result->addSLOC(strLSLOCBak, lineNumber, trunc_flag)) + result->exec_lines[LOG]++; + strLSLOCBak = strLSLOC = ""; + phys_exec_lines = temp_lines; + temp_lines = 0; + } + } + } + + // make sure that we are not beginning to process a new data line + cnt = 0; + CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", NULL); + + if (cnt > 0) + data_continue = true; + if (data_continue) + temp_lines++; + if (startmax > 0 && !found_endcase && !found_end) + phys_data_lines = 1; + else if (temp_lines == 0 && phys_data_lines == 0 && phys_exec_lines == 0) + phys_exec_lines = 1; +} + +/* COMPLEXITY MODULE FUNCTIONS */ + +/*! + * Parses lines for function/procedure names. + * + * \param line line to be processed + * \param lastline last line processed + * \param functionStack stack of functions + * \param functionName function name found + * + * \return 1 if function name is found + * \return 0 if it is still in some function + * \return 2 if the code line doesn't belong to any function + */ +int CVerilogCounter::ParseFunctionName(const string &line, string &lastline, StringVector &functionStack, string &functionName) +{ + string str; + size_t idx; + /* FIND KEYWORD "task" / "function" */ + static int func_flag = 0, task_flag = 0; + /* FIND KEYWORD "task" / "function" */ + idx = CUtil::FindKeyword(line, "task"); + if (idx != string::npos) + { + if (idx + 5 < line.length()) + { + str = line.substr(idx + 5); + functionStack.push_back(str); + } + task_flag++;/* FOUND KEYWORD "task" */ + } + + idx = CUtil::FindKeyword(line, "function"); + if (idx != string::npos) + { + if (idx + 9 < line.length()) + { + str = line.substr(idx + 9); + functionStack.push_back(str); + } + func_flag++;/* FOUND KEYWORD "function" */ + } + + if (functionStack.empty()) + { + // dealing with some code out of any subroutines, it a "main" code + return 2; + } + + idx = CUtil::FindKeyword(line, "endtask"); + if(idx != string::npos){ + task_flag--; + } + else{ + idx = CUtil::FindKeyword(line, "endfunction"); + if(idx != string::npos){ + func_flag--; + } + } + + if (idx != string::npos) + { + str = functionStack.back(); + functionStack.pop_back(); + idx = str.find(";"); + if (idx != string::npos) + { + functionName = CUtil::ClearRedundantSpaces(str.substr(0, idx)); + lastline=line; // warning fix + return 1; + } + } + return 0; +} + +int CVerilogCounter::CountComplexity(filemap* fmap, results* result) +{ + if (classtype == UNKNOWN || classtype == DATAFILE) + return 0; + filemap::iterator fit; + filemap fitBak; + filemap::iterator fitForw, fitBack;//used to check prior an later lines for semicolons + //unsigned int cnt; + //string line, line2; + string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$><=:"; + tokLocVect conditionalVector; + tokLocVect::reverse_iterator r_tlvIter; + //StringVector::iterator strIter = this->cmplx_cond_list.begin(); warning fix + string buf; // have a buffer string + stringstream ss; // insert the string into a stream + tokenLocation tl; + int count; + bool whenCont; + + size_t idx; + unsigned int cnt, ret, cyclomatic_cnt = 0, ignore_cyclomatic_cnt = 0, main_cyclomatic_cnt = 0, /*function_count = 0, */cyclomatic_logic_cnt = 0, main_cyclomatic_logic_cnt = 1, cyclomatic_case_cnt = 0, main_cyclomatic_case_cnt = 1; // warning fix + string line, lastline, file_ext, function_name = ""; + StringVector function_stack; + stack cyclomatic_stack; + stack cyclomatic_logic_stack; + stack cyclomatic_case_stack; + bool process_cyclomatic_complexity = false; + bool first_line_in_main = true; + + StringVector switch_case_key_list; + StringVector switch_case_stack; + switch_case_key_list.push_back(":"); + + // check whether to process cyclomatic complexity + if (cmplx_cyclomatic_list.size() > 0) + { + process_cyclomatic_complexity = true; + if (skip_cmplx_cyclomatic_file_extension_list.size() > 0) + { + idx = result->file_name.find_last_of("."); + if (idx != string::npos) + { + file_ext = result->file_name.substr(idx); + file_ext = CUtil::ToLower(file_ext); + if (find(skip_cmplx_cyclomatic_file_extension_list.begin(), skip_cmplx_cyclomatic_file_extension_list.end(), file_ext) != skip_cmplx_cyclomatic_file_extension_list.end()) + process_cyclomatic_complexity = false; + } + } + } + + for (fit = fmap->begin(); fit != fmap->end(); fit++) + { + line = fit->line; + + if (CUtil::CheckBlank(line)) + continue; + + line = " " + line; + + // mathematical functions + cnt = 0; + CUtil::CountTally(line, math_func_list, cnt, 1, exclude, "", "", &result->math_func_count, casesensitive); + result->cmplx_math_lines += cnt; + + // trigonometric functions + cnt = 0; + CUtil::CountTally(line, trig_func_list, cnt, 1, exclude, "", "", &result->trig_func_count, casesensitive); + result->cmplx_trig_lines += cnt; + + // logarithmic functions + cnt = 0; + CUtil::CountTally(line, log_func_list, cnt, 1, exclude, "", "", &result->log_func_count, casesensitive); + result->cmplx_logarithm_lines += cnt; + + // calculations + cnt = 0; + CUtil::CountTally(line, cmplx_calc_list, cnt, 1, exclude, "", "", &result->cmplx_calc_count, casesensitive); + result->cmplx_calc_lines += cnt; + + // conditionals + cnt = 0; + CUtil::CountTally(line, cmplx_cond_list, cnt, 1, exclude, "", "", &result->cmplx_cond_count, casesensitive); + result->cmplx_cond_lines += cnt; + + // logical operators + cnt = 0; + StringVector tmpList = cmplx_logic_list;//making a temporary list with the '<=' operator removed from the list; counting it on another pass; + tmpList.pop_back(); + CUtil::CountTally(line, tmpList, cnt, 1, exclude, "", "", &result->cmplx_logic_count, casesensitive); + result->cmplx_logic_lines += cnt; + + // preprocessor directives + cnt = 0; + CUtil::CountTally(line, cmplx_preproc_list, cnt, 1, exclude, "", "", &result->cmplx_preproc_count, casesensitive); + result->cmplx_preproc_lines += cnt; + + // assignments + cnt = 0; + tmpList.clear(); + tmpList = cmplx_assign_list;//making a temporary list with the '<=' operator removed from the list; counting it on another pass; + tmpList.pop_back(); + CUtil::CountTally(line, tmpList, cnt, 1, exclude, "", "", &result->cmplx_assign_count, casesensitive); + result->cmplx_assign_lines += cnt; + + /* No pointer for Verilog + // pointers + cnt = 0; + // Pointers are embedded syntax so there is NO exclude string or include strings + CUtil::CountTally(line, cmplx_pointer_list, cnt, 1, "", "", "", &result->cmplx_pointer_count, casesensitive); + result->cmplx_pointer_lines += cnt; + */ + + // cyclomatic complexity + if (process_cyclomatic_complexity) + { + // search for cyclomatic complexity keywords + unsigned int temp = 0; + CUtil::CountTally(line, cmplx_cyclomatic_list, temp, 1, exclude, "", "", 0, casesensitive); + cyclomatic_cnt += temp; + + // search for keywords to exclude + if (ignore_cmplx_cyclomatic_list.size() > 0) + CUtil::CountTally(line, ignore_cmplx_cyclomatic_list, ignore_cyclomatic_cnt, 1, exclude, "", "", 0, casesensitive); + + // search for cyclomatic complexity case keywords + if (cmplx_cyclomatic_case_list.size() > 0) + CUtil::CountTally(line, cmplx_cyclomatic_case_list, cyclomatic_case_cnt, 1, exclude, "", "", 0, casesensitive); + cyclomatic_case_cnt += temp; + + //search for keyword "case" + size_t idx_case = CUtil::FindKeyword(line, "case"); + if (idx_case != string::npos) { + switch_case_stack.push_back("case"); + } + //search for keyword "case" + idx_case = CUtil::FindKeyword(line, "casez"); + if (idx_case != string::npos) { + switch_case_stack.push_back("case"); + } + //search for keyword "case" + idx_case = CUtil::FindKeyword(line, "casex"); + if (idx_case != string::npos) { + switch_case_stack.push_back("case"); + } + + //only if switch_case_stack is not empty, we will search keyword ":" and "[" and "?" + if(!switch_case_stack.empty()){ + size_t idx = CUtil::FindKeyword(line, ":"); + size_t idx_1 = CUtil::FindKeyword(line, "["); + size_t idx_2 = CUtil::FindKeyword(line, "?"); + size_t idx_3 = CUtil::FindKeyword(line, "default"); + if ((idx != string::npos) && ((idx_1 == string::npos) || (idx < idx_1)) && ((idx_2 == string::npos) || (idx < idx_2)) && (idx_3 == string::npos)) { + cyclomatic_cnt++; + } + + //search for keyword "endcase" + idx = CUtil::FindKeyword(line, "endcase"); + if (idx != string::npos) { + switch_case_stack.pop_back(); + } + } + + // search for cyclomatic complexity logical keywords + if (cmplx_cyclomatic_logic_list.size() > 0) + CUtil::CountTally(line, cmplx_cyclomatic_logic_list, cyclomatic_logic_cnt, 1, exclude, "", "", 0, casesensitive); + + + // parse function name if found + ret = (unsigned)ParseFunctionName(line, lastline, function_stack, function_name); + if (ret != 1 && !cyclomatic_stack.empty() && cyclomatic_stack.size() == function_stack.size()) + { + // remove count stack entry for non-function names + cyclomatic_cnt += cyclomatic_stack.top(); + ignore_cyclomatic_cnt = 0; + cyclomatic_stack.pop(); + } + if (ret != 1 && !cyclomatic_logic_stack.empty() && cyclomatic_logic_stack.size() == function_stack.size()-1) + { + // remove count stack entry for non-function names + cyclomatic_logic_cnt += cyclomatic_logic_stack.top(); + cyclomatic_logic_stack.pop(); + } + if (ret != 1 && !cyclomatic_case_stack.empty() && cyclomatic_case_stack.size() == function_stack.size()-1) + { + // remove count stack entry for non-function names + cyclomatic_case_cnt += cyclomatic_case_stack.top(); + cyclomatic_case_stack.pop(); + } + + if (ret == 1) + { + // capture count at end of function + lineElement element(cyclomatic_cnt - ignore_cyclomatic_cnt + 1, function_name); + result->cmplx_cycfunct_count.push_back(element); + + lineElement n_element(cyclomatic_cnt - ignore_cyclomatic_cnt + cyclomatic_logic_cnt + 1, function_name); + result->cmplx_cycfunct_CC2_count.push_back(n_element); + + lineElement c_element(cyclomatic_case_cnt - ignore_cyclomatic_cnt + 1, function_name); + result->cmplx_cycfunct_CC3_count.push_back(c_element); + + if (!function_stack.empty()) + { + // grab previous function from stack to continue + if (!cyclomatic_stack.empty()) + { + cyclomatic_cnt = cyclomatic_stack.top(); + cyclomatic_stack.pop(); + } + if (!cyclomatic_logic_stack.empty()) + { + cyclomatic_logic_cnt = cyclomatic_logic_stack.top(); + cyclomatic_logic_stack.pop(); + } + if (!cyclomatic_case_stack.empty()) + { + cyclomatic_case_cnt = cyclomatic_case_stack.top(); + cyclomatic_case_stack.pop(); + } + } + else { + cyclomatic_cnt = 0; + cyclomatic_logic_cnt = 0; + cyclomatic_case_cnt = 0; + } + function_name = ""; + ignore_cyclomatic_cnt = 0; + } + else if (ret == 2) + { + if (first_line_in_main) + { + main_cyclomatic_cnt = 1; + first_line_in_main = false; + } + if (main_cyclomatic_cnt < 1) + main_cyclomatic_cnt = 1; // add 1 for main function here in case no other decision points are found in main + // some code doesn't belong to any function + main_cyclomatic_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt; + main_cyclomatic_logic_cnt += cyclomatic_cnt - ignore_cyclomatic_cnt + cyclomatic_logic_cnt; + main_cyclomatic_case_cnt += cyclomatic_case_cnt - ignore_cyclomatic_cnt; + cyclomatic_cnt = ignore_cyclomatic_cnt = cyclomatic_logic_cnt = cyclomatic_case_cnt = 0; + } + else { + if (!function_stack.empty() && (function_stack.size() > cyclomatic_stack.size() + 1 || (cyclomatic_stack.empty() && function_stack.size() > 1))) + { + // capture previous complexity count from open function + cyclomatic_stack.push(cyclomatic_cnt - ignore_cyclomatic_cnt); + cyclomatic_cnt = ignore_cyclomatic_cnt = 0; + } + if (!function_stack.empty() && (function_stack.size() > cyclomatic_logic_stack.size() + 1 || (cyclomatic_logic_stack.empty() && function_stack.size() > 1))) + { + // capture previous complexity count from open function + cyclomatic_logic_stack.push(cyclomatic_logic_cnt); + cyclomatic_logic_cnt = 0; + } + if (!function_stack.empty() && (function_stack.size() > cyclomatic_case_stack.size() + 1 || (cyclomatic_case_stack.empty() && function_stack.size() > 1))) + { + // capture previous complexity count from open function + cyclomatic_case_stack.push(cyclomatic_case_cnt); + cyclomatic_case_cnt = 0; + } + } + } + + } + + // do a single pass to mark and replace logical operator lessThan or equal "<=" + // these appear only in conditional statements + // the remaining are signal assignment operators + for (fit = fmap->begin(); fit != fmap->end(); fit++) + { + line = fit->line; + line = CUtil::ToLower(line); + + if (CUtil::CheckBlank(line)) + continue; + ss.clear(); + ss.str(""); + ss << line; + count = -1; + while (ss >> buf) + { + ++count; + if (!buf.compare("if")) + { + tl.lineNumber = fit->lineNumber; + tl.position = count; + tl.token = buf; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + // No wait in Verilog + // No until")) + // No assert")) + if (!buf.compare("while")) + { + tl.lineNumber = fit->lineNumber; + tl.position = count; + tl.token = buf; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + // No loop")) + // No next")) + // No when")) + // No exit")) + if (!buf.compare("return")) + { + tl.lineNumber = fit->lineNumber; + tl.position = count; + tl.token = buf; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + if (!buf.compare("case")) + { + tl.lineNumber = fit->lineNumber; + tl.position = count; + tl.token = buf; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + if (!buf.compare("casex")) + { + tl.lineNumber = fit->lineNumber; + tl.position = count; + tl.token = buf; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + if (!buf.compare("casez")) + { + tl.lineNumber = fit->lineNumber; + tl.position = count; + tl.token = buf; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + if (buf.find_last_of(";") != string::npos) + { + tl.lineNumber = fit->lineNumber; + tl.position = count; + tl.token = ";"; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + if (buf.find("<=") != string::npos) + { + whenCont = false; + // iterate up the vector an look for the first conditional statement + r_tlvIter = conditionalVector.rbegin(); + while (r_tlvIter != conditionalVector.rend()) + { + if (!r_tlvIter->token.compare(";")) + { + result->cmplx_assign_count.back()++; + result->cmplx_assign_lines++; + tl.token = "assign"; + break; + } + else + { + if ((!r_tlvIter->token.compare("if") || !r_tlvIter->token.compare("elsif") || !r_tlvIter->token.compare("assert") || + !r_tlvIter->token.compare("while") || !r_tlvIter->token.compare("return") || !r_tlvIter->token.compare("until") ) && !whenCont) + { + result->cmplx_logic_count.back()++; + result->cmplx_logic_lines++; + tl.token = "lte"; + break; + } + if (!r_tlvIter->token.compare("when")) + { + whenCont = true; + r_tlvIter++; + continue; + } + if (!r_tlvIter->token.compare("case") || !r_tlvIter->token.compare("next") || !r_tlvIter->token.compare("exit")) + { + result->cmplx_assign_count.back()++; + result->cmplx_assign_lines++; + tl.token = "assign"; + whenCont = false; + break; + } + result->cmplx_assign_count.back()++; + result->cmplx_assign_lines++; + tl.token = "assign"; + break; + } + // r_tlvIter++; MS VC++ warning C4702 unreachable code TODO: Review ! ! ! + } + tl.lineNumber = fit->lineNumber; + tl.position = count; + buf.clear(); + conditionalVector.push_back(tl); + continue; + } + } + + } + // done with a file, if has "main" code add it + if (main_cyclomatic_cnt > 0) + { + lineElement element(main_cyclomatic_cnt, "main"); + lineElement n_element(main_cyclomatic_logic_cnt, "main"); + lineElement c_element(main_cyclomatic_case_cnt, "main"); + result->cmplx_cycfunct_count.push_back(element); + result->cmplx_cycfunct_CC2_count.push_back(n_element); + result->cmplx_cycfunct_CC3_count.push_back(c_element); + } + return 1; +} diff --git a/src/CVerilogCounter.h b/src/CVerilogCounter.h new file mode 100644 index 0000000..35b9883 --- /dev/null +++ b/src/CVerilogCounter.h @@ -0,0 +1,50 @@ +//! Code counter class definition for the Verilog language. +/*! +* \file CVerilogCounter.h +* +* This file contains the code counter class definition for the Verilog hardware definition language (used in FPGA programming). +*/ + +#ifndef CVerilogCounter_h +#define CVerilogCounter_h + +#include "CCodeCounter.h" +#include "CVHDLCounter.h" + +//! Verilog code counter class. +/*! +* \class CVerilogCounter +* +* Defines the Verilog code counter class. +*/ + +class CVerilogCounter : public CCodeCounter +{ +public: + CVerilogCounter(); + +protected: + virtual int CountComplexity(filemap* fmap, results* result); + virtual int CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapmBak = NULL); + virtual int LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapmBak = NULL); + void LSLOC(results* result, string line, size_t lineNumber, string lineBak, string &strLSLOC, string &strLSLOCBak, + unsigned int &paren_cnt, bool &forflag, bool &found_forifwhile, bool &found_while, char &prev_char, + bool &data_continue, unsigned int &temp_lines, unsigned int &phys_exec_lines, unsigned int &phys_data_lines, + bool &found_for, StringVector &loopLevel, bool &always_flag, bool &case_flag, bool &repeat_flag); + using CCodeCounter::ParseFunctionName; // warning fix + int ParseFunctionName(const string &line, string &lastline, StringVector &functionStack, string &functionName); + +private: +// This class is NOT copied or assigned to. +// Avoid copying of this class. Avoid assignment of this class. +// Compiler will give an Error if a copy or assignment is done and those Errors do NOT happen. +// This avoids a VC++ -W4 or -Wall warning C4625, C4626 + + // Take care of warning C4625: 'CVerilogCounter' : copy constructor could not be generated because a base class copy constructor is inaccessible + CVerilogCounter(const CVerilogCounter& rhs); // Declare without implementation + + // Take care of warning C4626: 'CVerilogCounter' : assignment operator could not be generated because a base class assignment operator is inaccessible + CVerilogCounter operator=(const CVerilogCounter); // Declare without implementation +}; + +#endif diff --git a/src/CWebCounter.cpp b/src/CWebCounter.cpp new file mode 100644 index 0000000..4a99e78 --- /dev/null +++ b/src/CWebCounter.cpp @@ -0,0 +1,1183 @@ +//! Code counter class methods for web languages. +/*! +* \file CWebCounter.cpp +* +* This file contains the code counter class methods for web languages. +*/ + +#include "UCCBeforeLibraryIncludes.h" // Modification: 2015.12 +#include +#include "UCCAfterLibraryIncludes.h" // Modification: 2015.12 + +#include "UCCFilesOut.h" // Modification: 2015.12 +#include "CWebCounter.h" + +/*! +* Constructs a CWebCounter object. +*/ +CWebCounter::CWebCounter() +{ + classtype = WEB; + + // initialize list of web language names + web_lang_names.push_back("PHP"); + web_lang_names.push_back("ASP"); + web_lang_names.push_back("JSP"); + web_lang_names.push_back("HTML"); + web_lang_names.push_back("XML"); + web_lang_names.push_back("ColdFusion"); + + // initialize language file counters + total_php_filesA = 0; + total_asp_filesA = 0; + total_jsp_filesA = 0; + total_htm_filesA = 0; + total_xml_filesA = 0; + total_cfm_filesA = 0; + total_php_filesB = 0; + total_asp_filesB = 0; + total_jsp_filesB = 0; + total_htm_filesB = 0; + total_xml_filesB = 0; + total_cfm_filesB = 0; + total_php_dupFilesA = 0; + total_asp_dupFilesA = 0; + total_jsp_dupFilesA = 0; + total_htm_dupFilesA = 0; + total_xml_dupFilesA = 0; + total_cfm_dupFilesA = 0; + total_php_dupFilesB = 0; + total_asp_dupFilesB = 0; + total_jsp_dupFilesB = 0; + total_htm_dupFilesB = 0; + total_xml_dupFilesB = 0; + total_cfm_dupFilesB = 0; + + // initialize web language file extensions + file_exten_htm.push_back(".html"); + file_exten_htm.push_back(".htm"); + file_exten_htm.push_back(".shtml"); + file_exten_htm.push_back(".shtm"); + file_exten_htm.push_back(".stm"); + file_exten_htm.push_back(".sht"); + file_exten_htm.push_back(".oth"); + file_exten_htm.push_back(".xhtml"); + file_exten_xml.push_back(".xml"); + file_exten_php.push_back(".php"); + file_exten_asp.push_back(".asp"); + file_exten_asp.push_back(".aspx"); + file_exten_jsp.push_back(".jsp"); + file_exten_cfm.push_back(".cfm"); + file_exten_cfm.push_back(".cfml"); + file_exten_cfm.push_back(".cfc"); + UpdateWebFileExt(); + + // the space to save the separated file + SourceFileElement tmp; + tmp.second.file_name = "*.*htm"; + Separation.insert(map::value_type(HTML ,tmp)); + tmp.second.file_name = "*.*htmphp"; + Separation.insert(map::value_type(HTML_PHP ,tmp)); + tmp.second.file_name = "*.*htmjsp"; + Separation.insert(map::value_type(HTML_JSP ,tmp)); + tmp.second.file_name = "*.*htmasp"; + Separation.insert(map::value_type(HTML_ASP ,tmp)); + tmp.second.file_name = "*.*htmcfm"; + Separation.insert(map::value_type(HTML_CFM ,tmp)); + tmp.second.file_name = "*.*xml"; + Separation.insert(map::value_type(XML ,tmp)); + tmp.second.file_name = "*.*jshtm"; + Separation.insert(map::value_type(JAVASCRIPT_HTML ,tmp)); + tmp.second.file_name = "*.*jsxml"; + Separation.insert(map::value_type(JAVASCRIPT_XML ,tmp)); + tmp.second.file_name = "*.*jsphp"; + Separation.insert(map::value_type(JAVASCRIPT_PHP ,tmp)); + tmp.second.file_name = "*.*jsjsp"; + Separation.insert(map::value_type(JAVASCRIPT_JSP ,tmp)); + tmp.second.file_name = "*.*jsasps"; + Separation.insert(map::value_type(JAVASCRIPT_ASP_S ,tmp)); + tmp.second.file_name = "*.*jsaspc"; + Separation.insert(map::value_type(JAVASCRIPT_ASP_C ,tmp)); + tmp.second.file_name = "*.*jscfm"; + Separation.insert(map::value_type(JAVASCRIPT_CFM ,tmp)); + tmp.second.file_name = "*.*php"; + Separation.insert(map::value_type(PHP ,tmp)); + tmp.second.file_name = "*.*sqlcfm"; + Separation.insert(map::value_type(SQL_CFM ,tmp)); + tmp.second.file_name = "*.*java"; + Separation.insert(map::value_type(JAVA_JSP ,tmp)); + tmp.second.file_name = "*.*cshtm"; + Separation.insert(map::value_type(CSHARP_HTML ,tmp)); + tmp.second.file_name = "*.*csxml"; + Separation.insert(map::value_type(CSHARP_XML ,tmp)); + tmp.second.file_name = "*.*csasps"; + Separation.insert(map::value_type(CSHARP_ASP_S ,tmp)); + tmp.second.file_name = "*.*vbshtm"; + Separation.insert(map::value_type(VBS_HTML ,tmp)); + tmp.second.file_name = "*.*vbsxml"; + Separation.insert(map::value_type(VBS_XML ,tmp)); + tmp.second.file_name = "*.*vbsphp"; + Separation.insert(map::value_type(VBS_PHP ,tmp)); + tmp.second.file_name = "*.*vbsjsp"; + Separation.insert(map::value_type(VBS_JSP ,tmp)); + tmp.second.file_name = "*.*vbsasps"; + Separation.insert(map::value_type(VBS_ASP_S ,tmp)); + tmp.second.file_name = "*.*vbsaspc"; + Separation.insert(map::value_type(VBS_ASP_C ,tmp)); + tmp.second.file_name = "*.*vbscfm"; + Separation.insert(map::value_type(VBS_CFM ,tmp)); + tmp.second.file_name = "*.*cfm"; + Separation.insert(map::value_type(COLDFUSION ,tmp)); + tmp.second.file_name = "*.*cfs"; + Separation.insert(map::value_type(CFSCRIPT ,tmp)); + + // the space to save the separated line + lineElement tmp2; + SeparatedLine.insert(map::value_type(HTML ,tmp2)); + SeparatedLine.insert(map::value_type(HTML_PHP ,tmp2)); + SeparatedLine.insert(map::value_type(HTML_JSP ,tmp2)); + SeparatedLine.insert(map::value_type(HTML_ASP ,tmp2)); + SeparatedLine.insert(map::value_type(HTML_CFM ,tmp2)); + SeparatedLine.insert(map::value_type(XML ,tmp2)); + SeparatedLine.insert(map::value_type(JAVASCRIPT_HTML ,tmp2)); + SeparatedLine.insert(map::value_type(JAVASCRIPT_XML ,tmp2)); + SeparatedLine.insert(map::value_type(JAVASCRIPT_PHP ,tmp2)); + SeparatedLine.insert(map::value_type(JAVASCRIPT_JSP ,tmp2)); + SeparatedLine.insert(map::value_type(JAVASCRIPT_ASP_S ,tmp2)); + SeparatedLine.insert(map::value_type(JAVASCRIPT_ASP_C ,tmp2)); + SeparatedLine.insert(map::value_type(JAVASCRIPT_CFM ,tmp2)); + SeparatedLine.insert(map::value_type(PHP ,tmp2)); + SeparatedLine.insert(map::value_type(SQL_CFM ,tmp2)); + SeparatedLine.insert(map::value_type(JAVA_JSP ,tmp2)); + SeparatedLine.insert(map::value_type(CSHARP_HTML ,tmp2)); + SeparatedLine.insert(map::value_type(CSHARP_XML ,tmp2)); + SeparatedLine.insert(map::value_type(CSHARP_ASP_S ,tmp2)); + SeparatedLine.insert(map::value_type(VBS_HTML ,tmp2)); + SeparatedLine.insert(map::value_type(VBS_XML ,tmp2)); + SeparatedLine.insert(map::value_type(VBS_PHP ,tmp2)); + SeparatedLine.insert(map::value_type(VBS_JSP ,tmp2)); + SeparatedLine.insert(map::value_type(VBS_ASP_S ,tmp2)); + SeparatedLine.insert(map::value_type(VBS_ASP_C ,tmp2)); + SeparatedLine.insert(map::value_type(VBS_CFM ,tmp2)); + SeparatedLine.insert(map::value_type(COLDFUSION ,tmp2)); + SeparatedLine.insert(map::value_type(CFSCRIPT ,tmp2)); + + // supported script tags + // this container contains the tags that may appear in html code to specify the script language + TagTable.insert(map::value_type(string("::value_type(string("::value_type(string("?>") ,WEB_PHP_END)); + TagTable.insert(map::value_type(string("::value_type(string("::value_type(string("<%") ,WEB_ASP_JSP_START)); + TagTable.insert(map::value_type(string("%>") ,WEB_ASP_JSP_END)); + TagTable.insert(map::value_type(string("::value_type(string(" + ScriptTable.insert(map::value_type(string("javascript"),JAVASCRIPT)); + ScriptTable.insert(map::value_type(string("jscript") ,JAVASCRIPT)); + ScriptTable.insert(map::value_type(string("c#") ,CSHARP_HTML)); + ScriptTable.insert(map::value_type(string("php") ,PHP)); + ScriptTable.insert(map::value_type(string("vb") ,VB)); +} + +/*! +* Cleans up separated language file objects. +*/ +void CWebCounter::CleanSeparatedFile() +{ + for (map::iterator iter = Separation.begin(); iter != Separation.end(); iter++) + iter->second.first.clear(); + while (!PreviousLanguage.empty()) + PreviousLanguage.pop(); +} + +/*! +* Cleans up separated language file lines. +*/ +void CWebCounter::CleanSeparatedLine() +{ + for (map::iterator iter = SeparatedLine.begin(); iter != SeparatedLine.end(); iter++) + { + iter->second.line = ""; + iter->second.lineNumber = 0; + } +} + +/*! +* Separate web files into different languages and count these +* languages using their corresponding counters. +* Names the newly created file with its embedding file's filename. +* The filename is used in the matching function for the comparison purpose. +* new file's filename = embedding file's filename + its default filename +* NOTE: the filename contains *.* in its name. +* +* \param fmap list of file lines +* \param result counter results +* +* \return method status +*/ +int CWebCounter::CountSLOC(filemap* fmap, results* result) +{ + CleanSeparatedFile(); + lineElement current_line; + lineElement separate_line; + size_t pos, close_pos, search_result, tempi, lng; + char c; + int current_language, cf_mode = -1, tag_open_mode = -1; + int script_language_of_asp = VBS_ASP_S; + int script_language_of_cfm = VBS_CFM; + bool blank_line, found; + bool first_line_of_asp = true; + size_t preLang = INVALID_POSITION; // used for writing to files + string language, tempstr; + WebType webType; + + webType = GetWebType(result->file_name); + language_name = GetWebLangName(webType); + switch (webType) + { + case WEB_ASP: + current_language = HTML_ASP; + break; + case WEB_JSP: + current_language = HTML_JSP; + break; + case WEB_PHP: + current_language = HTML_PHP; + break; + case WEB_CFM: + current_language = COLDFUSION; + cf_mode = current_language; + break; + case WEB_XML: + current_language = XML; + break; + case WEB_HTM: + current_language = HTML; + break; + case WEB_UNKNOWN: + default: + current_language = HTML; + break; + } + + for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++) + { + current_line.line = iter->line; + current_line.lineNumber = iter->lineNumber; + + if (current_line.line.length() == 0) + blank_line = true; + else + blank_line = false; + + while (current_line.line.length() > 0) + { + search_result = CUtil::FindStringsCaseInsensitive(current_line.line, TagTable, pos, preLang); + if (search_result != INVALID_POSITION) + preLang = search_result; + + switch (search_result) + { + case WEB_PHP_START: + { + SeparatedLine[current_language].line += current_line.line.substr(0, pos + 5); + PreviousLanguage.push(current_language); + current_language = PHP; + current_line.line = current_line.line.substr(pos + 5); + } + break; + case WEB_PHP_START2: + { + // check for = pos + 5) + { + tempstr = CUtil::ToLower(current_line.line); + if (tempstr.substr(pos + 2, 3) == "php") + tempi = 5; + } + SeparatedLine[current_language].line += current_line.line.substr(0, pos + tempi); + PreviousLanguage.push(current_language); + current_language = PHP; + current_line.line = current_line.line.substr(pos + tempi); + } + break; + case WEB_PHP_END: + { + // check for line termination (not required at an end tag), add semi-colon if none + tempstr = CUtil::TrimString(current_line.line.substr(0, pos)); + if (CUtil::CheckBlank(tempstr)) + { + filemap::iterator iter2 = Separation[current_language].first.end(); + while (CUtil::CheckBlank(tempstr)) + { + if (iter2 == Separation[current_language].first.begin()) + break; + iter2--; + tempstr = CUtil::TrimString(iter2->line); + if (iter2 == Separation[current_language].first.begin()) + break; + } + if (!CUtil::CheckBlank(tempstr)) + { + c = tempstr[tempstr.length() - 1]; + if (c != ';' && c != '{' && c != '}' && c != ':') + iter->line += ";"; + } + } + else + { + c = tempstr[tempstr.length() - 1]; + if (c != ';' && c != '{' && c != '}' && c != ':') + { + current_line.line.insert(pos, ";"); + pos++; + } + } + if (cf_mode >= 0) + current_language = cf_mode; + else + { + current_language = PreviousLanguage.top(); + PreviousLanguage.pop(); + } + } + break; + case WEB_ASP_JSP_END: + { + SeparatedLine[current_language].line += current_line.line.substr(0, pos); + if (current_language == JAVA_JSP || current_language == CSHARP_ASP_S) + { + // special process for JSP; add semicolon at the end of line if there is no semicolon + SeparatedLine[current_language].line = CUtil::TrimString(SeparatedLine[current_language].line); + lng = SeparatedLine[current_language].line.length(); + if (lng > 0 && SeparatedLine[current_language].line[lng-1] != ';') + SeparatedLine[current_language].line += ";"; + } + if (cf_mode >= 0) + current_language = cf_mode; + else + { + current_language = PreviousLanguage.top(); + PreviousLanguage.pop(); + } + + // remove %>, next line + current_line.line = current_line.line.substr(pos + 2); + } + break; + case WEB_SCRIPT_START: + { + close_pos = current_line.line.find_first_of(">", pos + 7); + SeparatedLine[current_language].line += current_line.line.substr(0, close_pos + 1); + string language = CUtil::ToLower(current_line.line.substr(pos + 7, close_pos-pos + 7)); + if (close_pos < current_line.line.size()) + current_line.line = current_line.line.substr(close_pos + 1); + else + current_line.line = ""; + + PreviousLanguage.push(current_language); + current_language = (int)CUtil::FindStringsCaseInsensitive(language, ScriptTable, pos); + + // check for