RedPanda-CPP/tools/astyle/astyle_main.cpp

4360 lines
126 KiB
C++

// astyle_main.cpp
// Copyright (c) 2018 by Jim Pattee <jimp03@email.com>.
// This code is licensed under the MIT License.
// License.md describes the conditions under which this software may be distributed.
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* AStyle_main source file map.
* This source file contains several classes.
* They are arranged as follows.
* ---------------------------------------
* namespace astyle {
* ASStreamIterator methods
* ASConsole methods
* // Windows specific
* // Linux specific
* ASLibrary methods
* // Windows specific
* // Linux specific
* ASOptions methods
* ASEncoding methods
* } // end of astyle namespace
* Global Area ---------------------------
* Java Native Interface functions
* AStyleMainUtf16 entry point
* AStyleMain entry point
* AStyleGetVersion entry point
* main entry point
* ---------------------------------------
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
//-----------------------------------------------------------------------------
// headers
//-----------------------------------------------------------------------------
#include "astyle_main.h"
#include <algorithm>
#include <cerrno>
#include <clocale> // needed by some compilers
#include <cstdlib>
#include <fstream>
#include <sstream>
// includes for recursive getFileNames() function
#ifdef _WIN32
#undef UNICODE // use ASCII windows functions
#include <windows.h>
#else
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#ifdef __VMS
#include <unixlib.h>
#include <rms.h>
#include <ssdef.h>
#include <stsdef.h>
#include <lib$routines.h>
#include <starlet.h>
#endif /* __VMS */
#endif
//-----------------------------------------------------------------------------
// declarations
//-----------------------------------------------------------------------------
// turn off MinGW automatic file globbing
// this CANNOT be in the astyle namespace
#ifndef ASTYLE_LIB
int _CRT_glob = 0;
#endif
//----------------------------------------------------------------------------
// astyle namespace
//----------------------------------------------------------------------------
namespace astyle {
//
// console build variables
#ifndef ASTYLE_LIB
#ifdef _WIN32
char g_fileSeparator = '\\'; // Windows file separator
bool g_isCaseSensitive = false; // Windows IS NOT case sensitive
#else
char g_fileSeparator = '/'; // Linux file separator
bool g_isCaseSensitive = true; // Linux IS case sensitive
#endif // _WIN32
#endif // ASTYLE_LIB
// java library build variables
#ifdef ASTYLE_JNI
JNIEnv* g_env;
jobject g_obj;
jmethodID g_mid;
#endif
const char* g_version = "3.2 beta";
//-----------------------------------------------------------------------------
// ASStreamIterator class
// typename will be stringstream for AStyle
// it could be istream or wxChar for plug-ins
//-----------------------------------------------------------------------------
template<typename T>
ASStreamIterator<T>::ASStreamIterator(T* in)
{
inStream = in;
buffer.reserve(200);
eolWindows = 0;
eolLinux = 0;
eolMacOld = 0;
peekStart = 0;
prevLineDeleted = false;
checkForEmptyLine = false;
// get length of stream
inStream->seekg(0, inStream->end);
streamLength = inStream->tellg();
inStream->seekg(0, inStream->beg);
}
template<typename T>
ASStreamIterator<T>::~ASStreamIterator() = default;
/**
* get the length of the input stream.
* streamLength variable is set by the constructor.
*
* @return length of the input file stream, converted to an int.
*/
template<typename T>
int ASStreamIterator<T>::getStreamLength() const
{
return static_cast<int>(streamLength);
}
/**
* read the input stream, delete any end of line characters,
* and build a string that contains the input line.
*
* @return string containing the next input line minus any end of line characters
*/
template<typename T>
string ASStreamIterator<T>::nextLine(bool emptyLineWasDeleted)
{
// verify that the current position is correct
assert(peekStart == 0);
// a deleted line may be replaced if break-blocks is requested
// this sets up the compare to check for a replaced empty line
if (prevLineDeleted)
{
prevLineDeleted = false;
checkForEmptyLine = true;
}
if (!emptyLineWasDeleted)
prevBuffer = buffer;
else
prevLineDeleted = true;
// read the next record
buffer.clear();
char ch;
inStream->get(ch);
while (!inStream->eof() && ch != '\n' && ch != '\r')
{
buffer.append(1, ch);
inStream->get(ch);
}
if (inStream->eof())
{
return buffer;
}
int peekCh = inStream->peek();
// find input end-of-line characters
if (!inStream->eof())
{
if (ch == '\r') // CR+LF is windows otherwise Mac OS 9
{
if (peekCh == '\n')
{
inStream->get();
eolWindows++;
}
else
eolMacOld++;
}
else // LF is Linux, allow for improbable LF/CR
{
if (peekCh == '\r')
{
inStream->get();
eolWindows++;
}
else
eolLinux++;
}
}
else
{
inStream->clear();
}
// has not detected an input end of line
if (!eolWindows && !eolLinux && !eolMacOld)
{
#ifdef _WIN32
eolWindows++;
#else
eolLinux++;
#endif
}
// set output end of line characters
if (eolWindows >= eolLinux)
{
if (eolWindows >= eolMacOld)
outputEOL = "\r\n"; // Windows (CR+LF)
else
outputEOL = "\r"; // MacOld (CR)
}
else if (eolLinux >= eolMacOld)
outputEOL = "\n"; // Linux (LF)
else
outputEOL = "\r"; // MacOld (CR)
return buffer;
}
// save the current position and get the next line
// this can be called for multiple reads
// when finished peeking you MUST call peekReset()
// call this function from ASFormatter ONLY
template<typename T>
string ASStreamIterator<T>::peekNextLine()
{
assert(hasMoreLines());
string nextLine_;
char ch;
if (!peekStart)
peekStart = inStream->tellg();
// read the next record
inStream->get(ch);
while (!inStream->eof() && ch != '\n' && ch != '\r')
{
nextLine_.append(1, ch);
inStream->get(ch);
}
if (inStream->eof())
{
return nextLine_;
}
int peekCh = inStream->peek();
// remove end-of-line characters
if (!inStream->eof())
{
if ((peekCh == '\n' || peekCh == '\r') && peekCh != ch)
inStream->get();
}
return nextLine_;
}
// reset current position and EOF for peekNextLine()
template<typename T>
void ASStreamIterator<T>::peekReset()
{
assert(peekStart != 0);
inStream->clear();
inStream->seekg(peekStart);
peekStart = 0;
}
// save the last input line after input has reached EOF
template<typename T>
void ASStreamIterator<T>::saveLastInputLine()
{
assert(inStream->eof());
prevBuffer = buffer;
}
// return position of the get pointer
template<typename T>
streamoff ASStreamIterator<T>::tellg()
{
return inStream->tellg();
}
// check for a change in line ends
template<typename T>
bool ASStreamIterator<T>::getLineEndChange(int lineEndFormat) const
{
assert(lineEndFormat == LINEEND_DEFAULT
|| lineEndFormat == LINEEND_WINDOWS
|| lineEndFormat == LINEEND_LINUX
|| lineEndFormat == LINEEND_MACOLD);
bool lineEndChange = false;
if (lineEndFormat == LINEEND_WINDOWS)
lineEndChange = (eolLinux + eolMacOld != 0);
else if (lineEndFormat == LINEEND_LINUX)
lineEndChange = (eolWindows + eolMacOld != 0);
else if (lineEndFormat == LINEEND_MACOLD)
lineEndChange = (eolWindows + eolLinux != 0);
else
{
if (eolWindows > 0)
lineEndChange = (eolLinux + eolMacOld != 0);
else if (eolLinux > 0)
lineEndChange = (eolWindows + eolMacOld != 0);
else if (eolMacOld > 0)
lineEndChange = (eolWindows + eolLinux != 0);
}
return lineEndChange;
}
//-----------------------------------------------------------------------------
// ASConsole class
// main function will be included only in the console build
//-----------------------------------------------------------------------------
#ifndef ASTYLE_LIB
ASConsole::ASConsole(ASFormatter& formatterArg) : formatter(formatterArg)
{
errorStream = &cerr;
// command line options
isRecursive = false;
isDryRun = false;
noBackup = false;
preserveDate = false;
isVerbose = false;
isQuiet = false;
isFormattedOnly = false;
ignoreExcludeErrors = false;
ignoreExcludeErrorsDisplay = false;
useAscii = false;
// other variables
bypassBrowserOpen = false;
hasWildcard = false;
filesAreIdentical = true;
lineEndsMixed = false;
origSuffix = ".orig";
mainDirectoryLength = 0;
filesFormatted = 0;
filesUnchanged = 0;
linesOut = 0;
}
// rewrite a stringstream converting the line ends
void ASConsole::convertLineEnds(ostringstream& out, int lineEnd)
{
assert(lineEnd == LINEEND_WINDOWS || lineEnd == LINEEND_LINUX || lineEnd == LINEEND_MACOLD);
const string& inStr = out.str(); // avoids strange looking syntax
string outStr; // the converted output
int inLength = (int) inStr.length();
for (int pos = 0; pos < inLength; pos++)
{
if (inStr[pos] == '\r')
{
if (inStr[pos + 1] == '\n')
{
// CRLF
if (lineEnd == LINEEND_CR)
{
outStr += inStr[pos]; // Delete the LF
pos++;
continue;
}
if (lineEnd == LINEEND_LF)
{
outStr += inStr[pos + 1]; // Delete the CR
pos++;
continue;
}
outStr += inStr[pos]; // Do not change
outStr += inStr[pos + 1];
pos++;
continue;
}
else // NOLINT
{
// CR
if (lineEnd == LINEEND_CRLF)
{
outStr += inStr[pos]; // Insert the CR
outStr += '\n'; // Insert the LF
continue;
}
if (lineEnd == LINEEND_LF)
{
outStr += '\n'; // Insert the LF
continue;
}
outStr += inStr[pos]; // Do not change
continue;
}
}
else if (inStr[pos] == '\n')
{
// LF
if (lineEnd == LINEEND_CRLF)
{
outStr += '\r'; // Insert the CR
outStr += inStr[pos]; // Insert the LF
continue;
}
if (lineEnd == LINEEND_CR)
{
outStr += '\r'; // Insert the CR
continue;
}
outStr += inStr[pos]; // Do not change
continue;
}
else
{
outStr += inStr[pos]; // Write the current char
}
}
// replace the stream
out.str(outStr);
}
void ASConsole::correctMixedLineEnds(ostringstream& out)
{
LineEndFormat lineEndFormat = LINEEND_DEFAULT;
if (outputEOL == "\r\n")
lineEndFormat = LINEEND_WINDOWS;
if (outputEOL == "\n")
lineEndFormat = LINEEND_LINUX;
if (outputEOL == "\r")
lineEndFormat = LINEEND_MACOLD;
convertLineEnds(out, lineEndFormat);
}
// check files for 16 or 32 bit encoding
// the file must have a Byte Order Mark (BOM)
// NOTE: some string functions don't work with NULLs (e.g. length())
FileEncoding ASConsole::detectEncoding(const char* data, size_t dataSize) const
{
FileEncoding encoding = ENCODING_8BIT;
if (dataSize >= 3 && memcmp(data, "\xEF\xBB\xBF", 3) == 0)
encoding = UTF_8BOM;
else if (dataSize >= 4 && memcmp(data, "\x00\x00\xFE\xFF", 4) == 0)
encoding = UTF_32BE;
else if (dataSize >= 4 && memcmp(data, "\xFF\xFE\x00\x00", 4) == 0)
encoding = UTF_32LE;
else if (dataSize >= 2 && memcmp(data, "\xFE\xFF", 2) == 0)
encoding = UTF_16BE;
else if (dataSize >= 2 && memcmp(data, "\xFF\xFE", 2) == 0)
encoding = UTF_16LE;
return encoding;
}
// error exit without a message
void ASConsole::error() const
{
(*errorStream) << _("Artistic Style has terminated\n") << endl;
exit(EXIT_FAILURE);
}
// error exit with a message
void ASConsole::error(const char* why, const char* what) const
{
(*errorStream) << why << ' ' << what << endl;
error();
}
/**
* If no files have been given, use cin for input and cout for output.
*
* This is used to format text for text editors.
* Do NOT display any console messages when this function is used.
*/
void ASConsole::formatCinToCout()
{
// check for files from --stdin= and --stdout=
if (!stdPathIn.empty())
{
if (!freopen(stdPathIn.c_str(), "r", stdin))
error("Cannot open input file", stdPathIn.c_str());
}
if (!stdPathOut.empty())
{
if (!freopen(stdPathOut.c_str(), "w", stdout))
error("Cannot open output file", stdPathOut.c_str());
}
// Using cin.tellg() causes problems with both Windows and Linux.
// The Windows problem occurs when the input is not Windows line-ends.
// The tellg() will be out of sequence with the get() statements.
// The Linux cin.tellg() will return -1 (invalid).
// Copying the input sequentially to a stringstream before
// formatting solves the problem for both.
istream* inStream = &cin;
stringstream outStream;
char ch;
inStream->get(ch);
while (!inStream->eof() && !inStream->fail())
{
outStream.put(ch);
inStream->get(ch);
}
ASStreamIterator<stringstream> streamIterator(&outStream);
// Windows pipe or redirection always outputs Windows line-ends.
// Linux pipe or redirection will output any line end.
#ifdef _WIN32
LineEndFormat lineEndFormat = LINEEND_DEFAULT;
#else
LineEndFormat lineEndFormat = formatter.getLineEndFormat();
#endif // _WIN32
initializeOutputEOL(lineEndFormat);
formatter.init(&streamIterator);
while (formatter.hasMoreLines())
{
cout << formatter.nextLine();
if (formatter.hasMoreLines())
{
setOutputEOL(lineEndFormat, streamIterator.getOutputEOL());
cout << outputEOL;
}
else
{
// this can happen if the file if missing a closing brace and break-blocks is requested
if (formatter.getIsLineReady())
{
setOutputEOL(lineEndFormat, streamIterator.getOutputEOL());
cout << outputEOL;
cout << formatter.nextLine();
}
}
}
cout.flush();
}
/**
* Open input file, format it, and close the output.
*
* @param fileName_ The path and name of the file to be processed.
*/
void ASConsole::formatFile(const string& fileName_)
{
stringstream in;
ostringstream out;
FileEncoding encoding = readFile(fileName_, in);
// Unless a specific language mode has been set, set the language mode
// according to the file's suffix.
if (!formatter.getModeManuallySet())
{
if (stringEndsWith(fileName_, string(".java")))
formatter.setJavaStyle();
else if (stringEndsWith(fileName_, string(".cs")))
formatter.setSharpStyle();
else
formatter.setCStyle();
}
// set line end format
string nextLine; // next output line
filesAreIdentical = true; // input and output files are identical
LineEndFormat lineEndFormat = formatter.getLineEndFormat();
initializeOutputEOL(lineEndFormat);
// do this AFTER setting the file mode
ASStreamIterator<stringstream> streamIterator(&in);
formatter.init(&streamIterator);
// format the file
while (formatter.hasMoreLines())
{
nextLine = formatter.nextLine();
out << nextLine;
linesOut++;
if (formatter.hasMoreLines())
{
setOutputEOL(lineEndFormat, streamIterator.getOutputEOL());
out << outputEOL;
}
else
{
streamIterator.saveLastInputLine(); // to compare the last input line
// this can happen if the file if missing a closing brace and break-blocks is requested
if (formatter.getIsLineReady())
{
setOutputEOL(lineEndFormat, streamIterator.getOutputEOL());
out << outputEOL;
nextLine = formatter.nextLine();
out << nextLine;
linesOut++;
streamIterator.saveLastInputLine();
}
}
if (filesAreIdentical)
{
if (streamIterator.checkForEmptyLine)
{
if (nextLine.find_first_not_of(" \t") != string::npos)
filesAreIdentical = false;
}
else if (!streamIterator.compareToInputBuffer(nextLine))
filesAreIdentical = false;
streamIterator.checkForEmptyLine = false;
}
}
// correct for mixed line ends
if (lineEndsMixed)
{
correctMixedLineEnds(out);
filesAreIdentical = false;
}
// remove targetDirectory from filename if required by print
string displayName;
if (hasWildcard)
displayName = fileName_.substr(targetDirectory.length() + 1);
else
displayName = fileName_;
// if file has changed, write the new file
if (!filesAreIdentical || streamIterator.getLineEndChange(lineEndFormat))
{
if (!isDryRun)
writeFile(fileName_, encoding, out);
printMsg(_("Formatted %s\n"), displayName);
filesFormatted++;
}
else
{
if (!isFormattedOnly)
printMsg(_("Unchanged %s\n"), displayName);
filesUnchanged++;
}
assert(formatter.getChecksumDiff() == 0);
}
/**
* Searches for a file named fileName_ in the current directory. If it is not
* found, recursively searches for fileName_ in the current directory's parent
* directories, returning the location of the first instance of fileName_
* found. If fileName_ is not found, an empty string is returned.
*
* @param fileName_ The filename the function should attempt to locate.
* @return The full path to fileName_ in the current directory or
* nearest parent directory if found, otherwise an empty
* string.
*/
string ASConsole::findProjectOptionFilePath(const string& fileName_) const
{
string parent;
if (!fileNameVector.empty())
parent = getFullPathName(fileNameVector.front());
else if (!stdPathIn.empty())
parent = getFullPathName(stdPathIn);
else
parent = getFullPathName(getCurrentDirectory(fileName_));
// remove filename from path
size_t endPath = parent.find_last_of(g_fileSeparator);
if (endPath != string::npos)
parent = parent.substr(0, endPath + 1);
while (!parent.empty())
{
string filepath = parent + fileName_;
if (fileExists(filepath.c_str()))
return filepath;
if (fileName_ == ".astylerc")
{
filepath = parent + "_astylerc";
if (fileExists(filepath.c_str()))
return filepath;
}
parent = getParentDirectory(parent);
}
return string();
}
// for unit testing
vector<bool> ASConsole::getExcludeHitsVector() const
{ return excludeHitsVector; }
// for unit testing
vector<string> ASConsole::getExcludeVector() const
{ return excludeVector; }
// for unit testing
vector<string> ASConsole::getFileName() const
{ return fileName; }
// for unit testing
vector<string> ASConsole::getFileNameVector() const
{ return fileNameVector; }
// for unit testing
vector<string> ASConsole::getFileOptionsVector() const
{ return fileOptionsVector; }
// for unit testing
bool ASConsole::getFilesAreIdentical() const
{ return filesAreIdentical; }
// for unit testing
int ASConsole::getFilesFormatted() const
{ return filesFormatted; }
// for unit testing
bool ASConsole::getIgnoreExcludeErrors() const
{ return ignoreExcludeErrors; }
// for unit testing
bool ASConsole::getIgnoreExcludeErrorsDisplay() const
{ return ignoreExcludeErrorsDisplay; }
// for unit testing
bool ASConsole::getIsDryRun() const
{ return isDryRun; }
// for unit testing
bool ASConsole::getIsFormattedOnly() const
{ return isFormattedOnly; }
// for unit testing
string ASConsole::getLanguageID() const
{ return localizer.getLanguageID(); }
// for unit testing
bool ASConsole::getIsQuiet() const
{ return isQuiet; }
// for unit testing
bool ASConsole::getIsRecursive() const
{ return isRecursive; }
// for unit testing
bool ASConsole::getIsVerbose() const
{ return isVerbose; }
// for unit testing
bool ASConsole::getLineEndsMixed() const
{ return lineEndsMixed; }
// for unit testing
bool ASConsole::getNoBackup() const
{ return noBackup; }
// for unit testing
string ASConsole::getOptionFileName() const
{ return optionFileName; }
// for unit testing
vector<string> ASConsole::getOptionsVector() const
{ return optionsVector; }
// for unit testing
string ASConsole::getOrigSuffix() const
{ return origSuffix; }
// for unit testing
bool ASConsole::getPreserveDate() const
{ return preserveDate; }
// for unit testing
string ASConsole::getProjectOptionFileName() const
{
assert(projectOptionFileName.length() > 0);
// remove the directory path
size_t start = projectOptionFileName.find_last_of(g_fileSeparator);
if (start == string::npos)
start = 0;
return projectOptionFileName.substr(start + 1);
}
// for unit testing
vector<string> ASConsole::getProjectOptionsVector() const
{ return projectOptionsVector; }
// for unit testing
string ASConsole::getStdPathIn() const
{ return stdPathIn; }
// for unit testing
string ASConsole::getStdPathOut() const
{ return stdPathOut; }
// for unit testing
void ASConsole::setBypassBrowserOpen(bool state)
{ bypassBrowserOpen = state; }
// for unit testing
ostream* ASConsole::getErrorStream() const
{
return errorStream;
}
void ASConsole::setErrorStream(ostream* errStreamPtr)
{
errorStream = errStreamPtr;
}
// build a vector of argv options
// the program path argv[0] is excluded
vector<string> ASConsole::getArgvOptions(int argc, char** argv)
{
if (argc > 0)
astyleExePath = getFullPathName(argv[0]);
vector<string> argvOptions;
for (int i = 1; i < argc; i++)
{
argvOptions.emplace_back(string(argv[i]));
}
return argvOptions;
}
string ASConsole::getParam(const string& arg, const char* op)
{
return arg.substr(strlen(op));
}
void ASConsole::getTargetFilenames(string& targetFilename_,
vector<string>& targetFilenameVector) const
{
size_t beg = 0;
size_t sep = 0;
while (beg < targetFilename_.length())
{
// find next target
sep = targetFilename_.find_first_of(",;", beg);
if (sep == string::npos)
sep = targetFilename_.length();
string fileExtension = targetFilename_.substr(beg, sep - beg);
beg = sep + 1;
// remove whitespace
while (fileExtension.length() > 0
&& (fileExtension[0] == ' ' || fileExtension[0] == '\t'))
fileExtension = fileExtension.erase(0, 1);
while (fileExtension.length() > 0
&& (fileExtension[fileExtension.length() - 1] == ' '
|| fileExtension[fileExtension.length() - 1] == '\t'))
fileExtension = fileExtension.erase(fileExtension.length() - 1, 1);
if (fileExtension.length() > 0)
targetFilenameVector.emplace_back(fileExtension);
}
if (targetFilenameVector.empty())
{
fprintf(stderr, _("Missing filename in %s\n"), targetFilename_.c_str());
error();
}
}
// initialize output end of line
void ASConsole::initializeOutputEOL(LineEndFormat lineEndFormat)
{
assert(lineEndFormat == LINEEND_DEFAULT
|| lineEndFormat == LINEEND_WINDOWS
|| lineEndFormat == LINEEND_LINUX
|| lineEndFormat == LINEEND_MACOLD);
outputEOL.clear(); // current line end
prevEOL.clear(); // previous line end
lineEndsMixed = false; // output has mixed line ends, LINEEND_DEFAULT only
if (lineEndFormat == LINEEND_WINDOWS)
outputEOL = "\r\n";
else if (lineEndFormat == LINEEND_LINUX)
outputEOL = "\n";
else if (lineEndFormat == LINEEND_MACOLD)
outputEOL = "\r";
else
outputEOL.clear();
}
// read a file into the stringstream 'in'
FileEncoding ASConsole::readFile(const string& fileName_, stringstream& in) const
{
const int blockSize = 65536; // 64 KB
ifstream fin(fileName_.c_str(), ios::binary);
if (!fin)
error("Cannot open file", fileName_.c_str());
char* data = new (nothrow) char[blockSize];
if (data == nullptr)
error("Cannot allocate memory to open file", fileName_.c_str());
fin.read(data, blockSize);
if (fin.bad())
error("Cannot read file", fileName_.c_str());
size_t dataSize = static_cast<size_t>(fin.gcount());
FileEncoding encoding = detectEncoding(data, dataSize);
if (encoding == UTF_32BE || encoding == UTF_32LE)
error(_("Cannot process UTF-32 encoding"), fileName_.c_str());
bool firstBlock = true;
bool isBigEndian = (encoding == UTF_16BE);
while (dataSize != 0)
{
if (encoding == UTF_16LE || encoding == UTF_16BE)
{
// convert utf-16 to utf-8
size_t utf8Size = encode.utf8LengthFromUtf16(data, dataSize, isBigEndian);
char* utf8Out = new (nothrow) char[utf8Size];
if (utf8Out == nullptr)
error("Cannot allocate memory for utf-8 conversion", fileName_.c_str());
size_t utf8Len = encode.utf16ToUtf8(data, dataSize, isBigEndian, firstBlock, utf8Out);
assert(utf8Len <= utf8Size);
in << string(utf8Out, utf8Len);
delete[] utf8Out;
}
else
in << string(data, dataSize);
fin.read(data, blockSize);
if (fin.bad())
error("Cannot read file", fileName_.c_str());
dataSize = static_cast<size_t>(fin.gcount());
firstBlock = false;
}
fin.close();
delete[] data;
return encoding;
}
void ASConsole::setIgnoreExcludeErrors(bool state)
{ ignoreExcludeErrors = state; }
void ASConsole::setIgnoreExcludeErrorsAndDisplay(bool state)
{ ignoreExcludeErrors = state; ignoreExcludeErrorsDisplay = state; }
void ASConsole::setIsFormattedOnly(bool state)
{ isFormattedOnly = state; }
void ASConsole::setIsQuiet(bool state)
{ isQuiet = state; }
void ASConsole::setIsRecursive(bool state)
{ isRecursive = state; }
void ASConsole::setIsDryRun(bool state)
{ isDryRun = state; }
void ASConsole::setIsVerbose(bool state)
{ isVerbose = state; }
void ASConsole::setNoBackup(bool state)
{ noBackup = state; }
void ASConsole::setOptionFileName(const string& name)
{ optionFileName = name; }
void ASConsole::setOrigSuffix(const string& suffix)
{ origSuffix = suffix; }
void ASConsole::setPreserveDate(bool state)
{ preserveDate = state; }
void ASConsole::setProjectOptionFileName(const string& optfilepath)
{ projectOptionFileName = optfilepath; }
void ASConsole::setStdPathIn(const string& path)
{ stdPathIn = path; }
void ASConsole::setStdPathOut(const string& path)
{ stdPathOut = path; }
// set outputEOL variable
void ASConsole::setOutputEOL(LineEndFormat lineEndFormat, const string& currentEOL)
{
if (lineEndFormat == LINEEND_DEFAULT)
{
outputEOL = currentEOL;
if (prevEOL.empty())
prevEOL = outputEOL;
if (prevEOL != outputEOL)
{
lineEndsMixed = true;
filesAreIdentical = false;
prevEOL = outputEOL;
}
}
else
{
prevEOL = currentEOL;
if (prevEOL != outputEOL)
filesAreIdentical = false;
}
}
#ifdef _WIN32 // Windows specific
/**
* WINDOWS function to display the last system error.
*/
void ASConsole::displayLastError()
{
LPSTR msgBuf;
DWORD lastError = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
nullptr,
lastError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPSTR) &msgBuf,
0,
nullptr
);
// Display the string.
(*errorStream) << "Error (" << lastError << ") " << msgBuf << endl;
// Free the buffer.
LocalFree(msgBuf);
}
/**
* WINDOWS function to get the current directory.
* NOTE: getenv("CD") does not work for Windows Vista.
* The Windows function GetCurrentDirectory is used instead.
*
* @return The path of the current directory
*/
string ASConsole::getCurrentDirectory(const string& fileName_) const
{
char currdir[MAX_PATH];
currdir[0] = '\0';
if (!GetCurrentDirectory(sizeof(currdir), currdir))
error("Cannot find file", fileName_.c_str());
return string(currdir);
}
/**
* WINDOWS function to resolve wildcards and recurse into sub directories.
* The fileName vector is filled with the path and names of files to process.
*
* @param directory The path of the directory to be processed.
* @param wildcards A vector of wildcards to be processed (e.g. *.cpp).
*/
void ASConsole::getFileNames(const string& directory, const vector<string>& wildcards)
{
vector<string> subDirectory; // sub directories of directory
WIN32_FIND_DATA findFileData; // for FindFirstFile and FindNextFile
// Find the first file in the directory
// Find will get at least "." and "..".
string firstFile = directory + "\\*";
HANDLE hFind = FindFirstFile(firstFile.c_str(), &findFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
// Error (3) The system cannot find the path specified.
// Error (123) The filename, directory name, or volume label syntax is incorrect.
// ::FindClose(hFind); before exiting
displayLastError();
error(_("Cannot open directory"), directory.c_str());
}
// save files and sub directories
do
{
// skip hidden or read only
if (findFileData.cFileName[0] == '.'
|| (findFileData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
|| (findFileData.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
continue;
// is this a sub directory
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (!isRecursive)
continue;
// if a sub directory and recursive, save sub directory
string subDirectoryPath = directory + g_fileSeparator + findFileData.cFileName;
if (isPathExclued(subDirectoryPath))
printMsg(_("Exclude %s\n"), subDirectoryPath.substr(mainDirectoryLength));
else
subDirectory.emplace_back(subDirectoryPath);
continue;
}
string filePathName = directory + g_fileSeparator + findFileData.cFileName;
// check exclude before wildcmp to avoid "unmatched exclude" error
bool isExcluded = isPathExclued(filePathName);
// save file name if wildcard match
for (const string& wildcard : wildcards)
{
if (wildcmp(wildcard.c_str(), findFileData.cFileName))
{
if (isExcluded)
printMsg(_("Exclude %s\n"), filePathName.substr(mainDirectoryLength));
else
fileName.emplace_back(filePathName);
break;
}
}
}
while (FindNextFile(hFind, &findFileData) != 0);
// check for processing error
::FindClose(hFind);
DWORD dwError = GetLastError();
if (dwError != ERROR_NO_MORE_FILES)
error("Error processing directory", directory.c_str());
// recurse into sub directories
// if not doing recursive subDirectory is empty
for (const string& subDirectoryName : subDirectory)
getFileNames(subDirectoryName, wildcards);
}
// WINDOWS function to get the full path name from the relative path name
// Return the full path name or an empty string if failed.
string ASConsole::getFullPathName(const string& relativePath) const
{
char fullPath[MAX_PATH];
GetFullPathName(relativePath.c_str(), MAX_PATH, fullPath, nullptr);
return fullPath;
}
/**
* WINDOWS function to format a number according to the current locale.
* This formats positive integers only, no float.
*
* @param num The number to be formatted.
* @param lcid The LCID of the locale to be used for testing.
* @return The formatted number.
*/
string ASConsole::getNumberFormat(int num, size_t lcid) const
{
#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__BORLANDC__) || defined(__GNUC__)
// Compilers that don't support C++ locales should still support this assert.
// The C locale should be set but not the C++.
// This function is not necessary if the C++ locale is set.
// The locale().name() return value is not portable to all compilers.
assert(locale().name() == "C");
#endif
// convert num to a string
stringstream alphaNum;
alphaNum << num;
string number = alphaNum.str();
if (useAscii)
return number;
// format the number using the Windows API
if (lcid == 0)
lcid = LOCALE_USER_DEFAULT;
int outSize = ::GetNumberFormat(lcid, 0, number.c_str(), nullptr, nullptr, 0);
char* outBuf = new (nothrow) char[outSize];
if (outBuf == nullptr)
return number;
::GetNumberFormat(lcid, 0, number.c_str(), nullptr, outBuf, outSize);
string formattedNum(outBuf);
delete[] outBuf;
// remove the decimal
int decSize = ::GetLocaleInfo(lcid, LOCALE_SDECIMAL, nullptr, 0);
char* decBuf = new (nothrow) char[decSize];
if (decBuf == nullptr)
return number;
::GetLocaleInfo(lcid, LOCALE_SDECIMAL, decBuf, decSize);
size_t i = formattedNum.rfind(decBuf);
delete[] decBuf;
if (i != string::npos)
formattedNum.erase(i);
if (!formattedNum.length())
formattedNum = "0";
return formattedNum;
}
/**
* WINDOWS function to check for a HOME directory
*
* @param absPath The path to be evaluated.
* @returns true if absPath is HOME or is an invalid absolute
* path, false otherwise.
*/
bool ASConsole::isHomeOrInvalidAbsPath(const string& absPath) const
{
const char* const env = getenv("USERPROFILE");
if (env == nullptr)
return true;
if (absPath.c_str() == env)
return true;
if (absPath.compare(0, strlen(env), env) != 0)
return true;
return false;
}
/**
* WINDOWS function to open a HTML file in the default browser.
*/
void ASConsole::launchDefaultBrowser(const char* filePathIn /*nullptr*/) const
{
struct stat statbuf;
const char* envPaths[] = { "PROGRAMFILES(X86)", "PROGRAMFILES" };
size_t pathsLen = sizeof(envPaths) / sizeof(envPaths[0]);
string htmlDefaultPath;
for (size_t i = 0; i < pathsLen; i++)
{
const char* const envPath = getenv(envPaths[i]);
if (envPath == nullptr)
continue;
htmlDefaultPath = envPath;
if (htmlDefaultPath.length() > 0
&& htmlDefaultPath[htmlDefaultPath.length() - 1] == g_fileSeparator)
htmlDefaultPath.erase(htmlDefaultPath.length() - 1);
htmlDefaultPath.append("\\AStyle\\doc");
if (stat(htmlDefaultPath.c_str(), &statbuf) == 0 && statbuf.st_mode & S_IFDIR)
break;
}
htmlDefaultPath.append("\\");
// build file path
string htmlFilePath;
if (filePathIn == nullptr)
htmlFilePath = htmlDefaultPath + "astyle.html";
else
{
if (strpbrk(filePathIn, "\\/") == nullptr)
htmlFilePath = htmlDefaultPath + filePathIn;
else
htmlFilePath = filePathIn;
}
standardizePath(htmlFilePath);
if (stat(htmlFilePath.c_str(), &statbuf) != 0 || !(statbuf.st_mode & S_IFREG))
{
printf(_("Cannot open HTML file %s\n"), htmlFilePath.c_str());
return;
}
SHELLEXECUTEINFO sei = { sizeof(sei), {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} };
sei.fMask = SEE_MASK_FLAG_NO_UI;
sei.lpVerb = "open";
sei.lpFile = htmlFilePath.c_str();
sei.nShow = SW_SHOWNORMAL;
// browser open will be bypassed in test programs
printf(_("Opening HTML documentation %s\n"), htmlFilePath.c_str());
if (!bypassBrowserOpen)
{
int ret = ShellExecuteEx(&sei);
if (!ret)
error(_("Command execute failure"), htmlFilePath.c_str());
}
}
#else // Linux specific
/**
* LINUX function to get the current directory.
* This is done if the fileName does not contain a path.
* It is probably from an editor sending a single file.
*
* @param fileName_ The filename is used only for the error message.
* @return The path of the current directory
*/
string ASConsole::getCurrentDirectory(const string& fileName_) const
{
const char* const currdir = getenv("PWD");
if (currdir == nullptr)
error("Cannot find file", fileName_.c_str());
return string(currdir);
}
/**
* LINUX function to resolve wildcards and recurse into sub directories.
* The fileName vector is filled with the path and names of files to process.
*
* @param directory The path of the directory to be processed.
* @param wildcards A vector of wildcards to be processed (e.g. *.cpp).
*/
void ASConsole::getFileNames(const string& directory, const vector<string>& wildcards)
{
struct dirent* entry; // entry from readdir()
struct stat statbuf; // entry from stat()
vector<string> subDirectory; // sub directories of this directory
// errno is defined in <errno.h> and is set for errors in opendir, readdir, or stat
errno = 0;
DIR* dp = opendir(directory.c_str());
if (dp == nullptr)
error(_("Cannot open directory"), directory.c_str());
// save the first fileName entry for this recursion
const unsigned firstEntry = fileName.size();
// save files and sub directories
while ((entry = readdir(dp)) != nullptr)
{
// get file status
string entryFilepath = directory + g_fileSeparator + entry->d_name;
if (stat(entryFilepath.c_str(), &statbuf) != 0)
{
if (errno == EOVERFLOW) // file over 2 GB is OK
{
errno = 0;
continue;
}
perror("errno message");
error("Error getting file status in directory", directory.c_str());
}
// skip hidden or read only
if (entry->d_name[0] == '.' || !(statbuf.st_mode & S_IWUSR))
continue;
// if a sub directory and recursive, save sub directory
if (S_ISDIR(statbuf.st_mode) && isRecursive)
{
if (isPathExclued(entryFilepath))
printMsg(_("Exclude %s\n"), entryFilepath.substr(mainDirectoryLength));
else
subDirectory.emplace_back(entryFilepath);
continue;
}
// if a file, save file name
if (S_ISREG(statbuf.st_mode))
{
// check exclude before wildcmp to avoid "unmatched exclude" error
bool isExcluded = isPathExclued(entryFilepath);
// save file name if wildcard match
for (const string& wildcard : wildcards)
{
if (wildcmp(wildcard.c_str(), entry->d_name) != 0)
{
if (isExcluded)
printMsg(_("Exclude %s\n"), entryFilepath.substr(mainDirectoryLength));
else
fileName.emplace_back(entryFilepath);
break;
}
}
}
}
if (closedir(dp) != 0)
{
perror("errno message");
error("Error reading directory", directory.c_str());
}
// sort the current entries for fileName
if (firstEntry < fileName.size())
sort(fileName.begin() + firstEntry, fileName.end());
// recurse into sub directories
// if not doing recursive, subDirectory is empty
if (subDirectory.size() > 1)
sort(subDirectory.begin(), subDirectory.end());
for (unsigned i = 0; i < subDirectory.size(); i++)
{
getFileNames(subDirectory[i], wildcards);
}
}
// LINUX function to get the full path name from the relative path name
// Return the full path name or an empty string if failed.
string ASConsole::getFullPathName(const string& relativePath) const
{
// ignore realPath attribute warning, only with cmake
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
char fullPath[PATH_MAX];
fullPath[0] = '\0';
realpath(relativePath.c_str(), fullPath);
return fullPath;
#pragma GCC diagnostic pop
}
// LINUX function to get the documentation file path prefix
// from the executable file path.
// Return the documentation path prefix or an empty string if failed.
string ASConsole::getHtmlInstallPrefix() const
{
string astyleHtmlPrefix = astyleExePath;
size_t end = astyleHtmlPrefix.find("/bin/");
if (end == string::npos)
return "";
astyleHtmlPrefix = astyleHtmlPrefix.substr(0, end);
return astyleHtmlPrefix;
}
/**
* LINUX function to get locale information and call getNumberFormat.
* This formats positive integers only, no float.
*
* @param num The number to be formatted.
* size_t is for compatibility with the Windows function.
* @return The formatted number.
*/
string ASConsole::getNumberFormat(int num, size_t /*lcid*/) const
{
#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__BORLANDC__) || defined(__GNUC__)
// Compilers that don't support C++ locales should still support this assert.
// The C locale should be set but not the C++.
// This function is not necessary if the C++ locale is set.
// The locale().name() return value is not portable to all compilers.
assert(locale().name() == "C");
#endif
// get the locale info
struct lconv* lc;
lc = localeconv();
// format the number
return getNumberFormat(num, lc->grouping, lc->thousands_sep);
}
/**
* LINUX function to format a number according to the current locale.
* This formats positive integers only, no float.
*
* @param num The number to be formatted.
* @param groupingArg The grouping string from the locale.
* @param separator The thousands group separator from the locale.
* @return The formatted number.
*/
string ASConsole::getNumberFormat(int num, const char* groupingArg, const char* separator) const
{
// convert num to a string
stringstream alphaNum;
alphaNum << num;
string number = alphaNum.str();
// format the number from right to left
string formattedNum;
size_t ig = 0; // grouping index
int grouping = groupingArg[ig];
int i = number.length();
// check for no grouping
if (grouping == 0)
grouping = number.length();
while (i > 0)
{
// extract a group of numbers
string group;
if (i < grouping)
group = number;
else
group = number.substr(i - grouping);
// update formatted number
formattedNum.insert(0, group);
i -= grouping;
if (i < 0)
i = 0;
if (i > 0)
formattedNum.insert(0, separator);
number.erase(i);
// update grouping
if (groupingArg[ig] != '\0'
&& groupingArg[ig + 1] != '\0')
grouping = groupingArg[++ig];
}
return formattedNum;
}
/**
* LINUX function to check for a HOME directory
*
* @param absPath The path to be evaluated.
* @returns true if absPath is HOME or is an invalid absolute
* path, false otherwise.
*/
bool ASConsole::isHomeOrInvalidAbsPath(const string& absPath) const
{
const char* const env = getenv("HOME");
if (env == nullptr)
return true;
if (absPath.c_str() == env)
return true;
if (absPath.compare(0, strlen(env), env) != 0)
return true;
return false;
}
/**
* LINUX function to open a HTML file in the default browser.
* Use xdg-open from freedesktop.org cross-desktop compatibility suite xdg-utils.
* see http://portland.freedesktop.org/wiki/
* This is installed on most modern distributions.
*/
void ASConsole::launchDefaultBrowser(const char* filePathIn /*nullptr*/) const
{
#ifdef __APPLE__
string htmlDefaultPrefix = "/usr/local";
#else
string htmlDefaultPrefix = "/usr";
#endif
string htmlDefaultPath = htmlDefaultPrefix + "/share/doc/astyle/html/";
string htmlDefaultFile = "astyle.html";
string htmlFilePath;
struct stat statbuf;
// build html path
if (filePathIn == nullptr)
{
string htmlPrefix = getHtmlInstallPrefix();
if (htmlPrefix.empty())
htmlFilePath = htmlDefaultPrefix + htmlDefaultPath + htmlDefaultFile;
else
htmlFilePath = htmlPrefix + htmlDefaultPath + htmlDefaultFile;
}
else
{
if (strpbrk(filePathIn, "\\/") == nullptr)
htmlFilePath = htmlDefaultPath + filePathIn;
else
htmlFilePath = filePathIn;
}
standardizePath(htmlFilePath);
if (stat(htmlFilePath.c_str(), &statbuf) != 0 || !(statbuf.st_mode & S_IFREG))
{
printf(_("Cannot open HTML file %s\n"), htmlFilePath.c_str());
return;
}
// get search paths
const char* const envPaths = getenv("PATH");
if (envPaths == nullptr)
error("Cannot read PATH environment variable", "");
size_t envlen = strlen(envPaths);
char* paths = new char[envlen + 1];
strcpy(paths, envPaths);
// find xdg-open (usually in /usr/bin)
// Mac uses open instead
#ifdef __APPLE__
const char* fileOpen = "open";
#else
const char* fileOpen = "xdg-open";
#endif
string searchPath;
char* searchDir = strtok(paths, ":");
while (searchDir != nullptr)
{
searchPath = searchDir;
if (searchPath.length() > 0
&& searchPath[searchPath.length() - 1] != g_fileSeparator)
searchPath.append(string(1, g_fileSeparator));
searchPath.append(fileOpen);
if (stat(searchPath.c_str(), &statbuf) == 0 && (statbuf.st_mode & S_IFREG))
break;
searchDir = strtok(nullptr, ":");
}
delete[] paths;
if (searchDir == nullptr)
error(_("Command is not installed"), fileOpen);
// browser open will be bypassed in test programs
printf(_("Opening HTML documentation %s\n"), htmlFilePath.c_str());
if (!bypassBrowserOpen)
{
execlp(fileOpen, fileOpen, htmlFilePath.c_str(), nullptr);
// execlp will NOT return if successful
error(_("Command execute failure"), fileOpen);
}
}
#endif // _WIN32
/**
* Returns the parent directory of absPath. If absPath is not a valid absolute
* path or if it does not have a parent, an empty string is returned.
*
* @param absPath The initial directory.
* @return The parent directory of absPath, or an empty string if
* one cannot be found.
*/
string ASConsole::getParentDirectory(const string& absPath) const
{
if (isHomeOrInvalidAbsPath(absPath))
{
return string();
}
size_t offset = absPath.size() - 1;
if (absPath[absPath.size() - 1] == g_fileSeparator)
{
offset -= 1;
}
size_t idx = absPath.rfind(g_fileSeparator, offset);
if (idx == string::npos)
{
return string();
}
string str = absPath.substr(0, idx + 1);
return str;
}
// get individual file names from the command-line file path
void ASConsole::getFilePaths(const string& filePath)
{
fileName.clear();
targetDirectory = string();
targetFilename = string();
vector<string> targetFilenameVector;
// separate directory and file name
size_t separator = filePath.find_last_of(g_fileSeparator);
if (separator == string::npos)
{
// if no directory is present, use the currently active directory
targetDirectory = getCurrentDirectory(filePath);
targetFilename = filePath;
mainDirectoryLength = targetDirectory.length() + 1; // +1 includes trailing separator
}
else
{
targetDirectory = filePath.substr(0, separator);
targetFilename = filePath.substr(separator + 1);
mainDirectoryLength = targetDirectory.length() + 1; // +1 includes trailing separator
}
if (targetFilename.length() == 0)
{
fprintf(stderr, _("Missing filename in %s\n"), filePath.c_str());
error();
}
// check filename for wildcards
hasWildcard = false;
if (targetFilename.find_first_of("*?") != string::npos)
hasWildcard = true;
// If the filename is not quoted on Linux, bash will replace the
// wildcard instead of passing it to the program.
if (isRecursive && !hasWildcard)
{
fprintf(stderr, "%s\n", _("Recursive option with no wildcard"));
#ifndef _WIN32
fprintf(stderr, "%s\n", _("Did you intend quote the filename"));
#endif
error();
}
bool hasMultipleTargets = false;
if (targetFilename.find_first_of(",;") != string::npos)
hasMultipleTargets = true;
// display directory name for wildcard processing
if (hasWildcard)
{
printSeparatingLine();
printMsg(_("Directory %s\n"), targetDirectory + g_fileSeparator + targetFilename);
}
// clear exclude hits vector
size_t excludeHitsVectorSize = excludeHitsVector.size();
for (size_t ix = 0; ix < excludeHitsVectorSize; ix++)
excludeHitsVector[ix] = false;
// create a vector of paths and file names to process
if (hasWildcard || isRecursive || hasMultipleTargets)
{
getTargetFilenames(targetFilename, targetFilenameVector);
getFileNames(targetDirectory, targetFilenameVector);
}
else
{
// verify a single file is not a directory (needed on Linux)
string entryFilepath = targetDirectory + g_fileSeparator + targetFilename;
struct stat statbuf;
if (stat(entryFilepath.c_str(), &statbuf) == 0 && (statbuf.st_mode & S_IFREG))
fileName.emplace_back(entryFilepath);
}
// check for unprocessed excludes
bool excludeErr = false;
for (size_t ix = 0; ix < excludeHitsVector.size(); ix++)
{
if (!excludeHitsVector[ix])
{
excludeErr = true;
if (!ignoreExcludeErrorsDisplay)
{
if (ignoreExcludeErrors)
printMsg(_("Exclude (unmatched) %s\n"), excludeVector[ix]);
else
fprintf(stderr, _("Exclude (unmatched) %s\n"), excludeVector[ix].c_str());
}
else
{
if (!ignoreExcludeErrors)
fprintf(stderr, _("Exclude (unmatched) %s\n"), excludeVector[ix].c_str());
}
}
}
if (excludeErr && !ignoreExcludeErrors)
{
if (hasWildcard && !isRecursive)
fprintf(stderr, "%s\n", _("Did you intend to use --recursive"));
error();
}
// check if files were found (probably an input error if not)
if (fileName.empty())
{
fprintf(stderr, _("No file to process %s\n"), filePath.c_str());
if (hasWildcard && !isRecursive)
fprintf(stderr, "%s\n", _("Did you intend to use --recursive"));
error();
}
if (hasWildcard)
printSeparatingLine();
}
// Check if a file exists
bool ASConsole::fileExists(const char* file) const
{
struct stat buf;
return (stat(file, &buf) == 0);
}
bool ASConsole::fileNameVectorIsEmpty() const
{
return fileNameVector.empty();
}
bool ASConsole::isOption(const string& arg, const char* op)
{
return arg == op;
}
bool ASConsole::isOption(const string& arg, const char* a, const char* b)
{
return (isOption(arg, a) || isOption(arg, b));
}
bool ASConsole::isParamOption(const string& arg, const char* option)
{
bool retVal = arg.compare(0, strlen(option), option) == 0;
// if comparing for short option, 2nd char of arg must be numeric
if (retVal && strlen(option) == 1 && arg.length() > 1)
if (!isdigit((unsigned char) arg[1]))
retVal = false;
return retVal;
}
// compare a path to the exclude vector
// used for both directories and filenames
// updates the g_excludeHitsVector
// return true if a match
bool ASConsole::isPathExclued(const string& subPath)
{
bool retVal = false;
// read the exclude vector checking for a match
for (size_t i = 0; i < excludeVector.size(); i++)
{
string exclude = excludeVector[i];
if (subPath.length() < exclude.length())
continue;
size_t compareStart = subPath.length() - exclude.length();
// subPath compare must start with a directory name
if (compareStart > 0)
{
char lastPathChar = subPath[compareStart - 1];
if (lastPathChar != g_fileSeparator)
continue;
}
string compare = subPath.substr(compareStart);
if (!g_isCaseSensitive)
{
// make it case insensitive for Windows
for (size_t j = 0; j < compare.length(); j++)
compare[j] = (char) tolower(compare[j]);
for (size_t j = 0; j < exclude.length(); j++)
exclude[j] = (char) tolower(exclude[j]);
}
// compare sub directory to exclude data - must check them all
if (compare == exclude)
{
excludeHitsVector[i] = true;
retVal = true;
break;
}
}
return retVal;
}
void ASConsole::printHelp() const
{
cout << endl;
cout << " Artistic Style " << g_version << endl;
cout << " Maintained by: Jim Pattee\n";
cout << " Original Author: Tal Davidson\n";
cout << endl;
cout << "Usage:\n";
cout << "------\n";
cout << " astyle [OPTIONS] File1 File2 File3 [...]\n";
cout << endl;
cout << " astyle [OPTIONS] < Original > Beautified\n";
cout << endl;
cout << " When indenting a specific file, the resulting indented file RETAINS\n";
cout << " the original file-name. The original pre-indented file is renamed,\n";
cout << " with a suffix of \'.orig\' added to the original filename.\n";
cout << endl;
cout << " Wildcards (* and ?) may be used in the filename.\n";
cout << " A \'recursive\' option can process directories recursively.\n";
cout << " Multiple file extensions may be separated by a comma.\n";
cout << endl;
cout << " By default, astyle is set up to indent with four spaces per indent,\n";
cout << " a maximal indentation of 40 spaces inside continuous statements,\n";
cout << " a minimum indentation of eight spaces inside conditional statements,\n";
cout << " and NO formatting options.\n";
cout << endl;
cout << "Options:\n";
cout << "--------\n";
cout << " This program follows the usual GNU command line syntax.\n";
cout << " Long options (starting with '--') must be written one at a time.\n";
cout << " Short options (starting with '-') may be appended together.\n";
cout << " Thus, -bps4 is the same as -b -p -s4.\n";
cout << endl;
cout << "Option Files:\n";
cout << "-------------\n";
cout << " Artistic Style looks for a default option file and/or a project\n";
cout << " option file in the following order:\n";
cout << " 1. The command line options have precedence.\n";
cout << " 2. The project option file has precedence over the default file\n";
cout << " o the file name indicated by the --project= command line option.\n";
cout << " o the file named .astylerc or _ astylerc.\n";
cout << " o the file name identified by ARTISTIC_STYLE_PROJECT_OPTIONS.\n";
cout << " o the file is disabled by --project=none on the command line.\n";
cout << " 3. The default option file that can be used for all projects.\n";
cout << " o the file path indicated by the --options= command line option.\n";
cout << " o the file path indicated by ARTISTIC_STYLE_OPTIONS.\n";
cout << " o the file named .astylerc in the HOME directory (for Linux).\n";
cout << " o the file name astylerc in the APPDATA directory (for Windows).\n";
cout << " o the file is disabled by --project=none on the command line.\n";
cout << " Long options within the option files may be written without '--'.\n";
cout << " Line-end comments begin with a '#'.\n";
cout << endl;
cout << "Disable Formatting:\n";
cout << "-------------------\n";
cout << " Disable Block\n";
cout << " Blocks of code can be disabled with the comment tags *INDENT-OFF*\n";
cout << " and *INDENT-ON*. It must be contained in a one-line comment.\n";
cout << endl;
cout << " Disable Line\n";
cout << " Padding of operators can be disabled on a single line using the\n";
cout << " comment tag *NOPAD*. It must be contained in a line-end comment.\n";
cout << endl;
cout << "Brace Style Options:\n";
cout << "--------------------\n";
cout << " default brace style\n";
cout << " If no brace style is requested, the opening braces will not be\n";
cout << " changed and closing braces will be broken from the preceding line.\n";
cout << endl;
cout << " --style=allman OR --style=bsd OR --style=break OR -A1\n";
cout << " Allman style formatting/indenting.\n";
cout << " Broken braces.\n";
cout << endl;
cout << " --style=java OR --style=attach OR -A2\n";
cout << " Java style formatting/indenting.\n";
cout << " Attached braces.\n";
cout << endl;
cout << " --style=kr OR --style=k&r OR --style=k/r OR -A3\n";
cout << " Kernighan & Ritchie style formatting/indenting.\n";
cout << " Linux braces.\n";
cout << endl;
cout << " --style=stroustrup OR -A4\n";
cout << " Stroustrup style formatting/indenting.\n";
cout << " Linux braces, with broken closing headers.\n";
cout << endl;
cout << " --style=whitesmith OR -A5\n";
cout << " Whitesmith style formatting/indenting.\n";
cout << " Broken, indented braces.\n";
cout << " Indented class blocks and switch blocks.\n";
cout << endl;
cout << " --style=vtk OR -A15\n";
cout << " VTK style formatting/indenting.\n";
cout << " Broken, indented braces except for the opening braces.\n";
cout << endl;
cout << " --style=ratliff OR --style=banner OR -A6\n";
cout << " Ratliff style formatting/indenting.\n";
cout << " Attached, indented braces.\n";
cout << endl;
cout << " --style=gnu OR -A7\n";
cout << " GNU style formatting/indenting.\n";
cout << " Broken braces, indented blocks.\n";
cout << endl;
cout << " --style=linux OR --style=knf OR -A8\n";
cout << " Linux style formatting/indenting.\n";
cout << " Linux braces, minimum conditional indent is one-half indent.\n";
cout << endl;
cout << " --style=horstmann OR --style=run-in OR -A9\n";
cout << " Horstmann style formatting/indenting.\n";
cout << " Run-in braces, indented switches.\n";
cout << endl;
cout << " --style=1tbs OR --style=otbs OR -A10\n";
cout << " One True Brace Style formatting/indenting.\n";
cout << " Linux braces, add braces to all conditionals.\n";
cout << endl;
cout << " --style=google OR -A14\n";
cout << " Google style formatting/indenting.\n";
cout << " Attached braces, indented class modifiers.\n";
cout << endl;
cout << " --style=mozilla OR -A16\n";
cout << " Mozilla style formatting/indenting.\n";
cout << " Linux braces, with broken braces for structs and enums,\n";
cout << " and attached braces for namespaces.\n";
cout << endl;
cout << " --style=webkit OR -A17\n";
cout << " WebKit style formatting/indenting.\n";
cout << " Linux braces, with attached closing headers.\n";
cout << endl;
cout << " --style=pico OR -A11\n";
cout << " Pico style formatting/indenting.\n";
cout << " Run-in opening braces and attached closing braces.\n";
cout << " Uses keep one line blocks and keep one line statements.\n";
cout << endl;
cout << " --style=lisp OR -A12\n";
cout << " Lisp style formatting/indenting.\n";
cout << " Attached opening braces and attached closing braces.\n";
cout << " Uses keep one line statements.\n";
cout << endl;
cout << "Tab Options:\n";
cout << "------------\n";
cout << " default indent option\n";
cout << " If no indentation option is set, the default\n";
cout << " option of 4 spaces per indent will be used.\n";
cout << endl;
cout << " --indent=spaces=# OR -s#\n";
cout << " Indent using # spaces per indent. Not specifying #\n";
cout << " will result in a default of 4 spaces per indent.\n";
cout << endl;
cout << " --indent=tab OR --indent=tab=# OR -t OR -t#\n";
cout << " Indent using tab characters, assuming that each\n";
cout << " indent is # spaces long. Not specifying # will result\n";
cout << " in a default assumption of 4 spaces per indent.\n";
cout << endl;
cout << " --indent=force-tab=# OR -T#\n";
cout << " Indent using tab characters, assuming that each\n";
cout << " indent is # spaces long. Force tabs to be used in areas\n";
cout << " AStyle would prefer to use spaces.\n";
cout << endl;
cout << " --indent=force-tab-x=# OR -xT#\n";
cout << " Allows the tab length to be set to a length that is different\n";
cout << " from the indent length. This may cause the indentation to be\n";
cout << " a mix of both spaces and tabs. This option sets the tab length.\n";
cout << endl;
cout << "Brace Modify Options:\n";
cout << "---------------------\n";
cout << " --attach-namespaces OR -xn\n";
cout << " Attach braces to a namespace statement.\n";
cout << endl;
cout << " --attach-classes OR -xc\n";
cout << " Attach braces to a class statement.\n";
cout << endl;
cout << " --attach-inlines OR -xl\n";
cout << " Attach braces to class inline function definitions.\n";
cout << endl;
cout << " --attach-extern-c OR -xk\n";
cout << " Attach braces to an extern \"C\" statement.\n";
cout << endl;
cout << " --attach-closing-while OR -xV\n";
cout << " Attach closing while of do-while to the closing brace.\n";
cout << endl;
cout << "Indentation Options:\n";
cout << "--------------------\n";
cout << " --indent-classes OR -C\n";
cout << " Indent 'class' blocks so that the entire block is indented.\n";
cout << endl;
cout << " --indent-modifiers OR -xG\n";
cout << " Indent 'class' access modifiers, 'public:', 'protected:' or\n";
cout << " 'private:', one half indent. The rest of the class is not\n";
cout << " indented. \n";
cout << endl;
cout << " --indent-switches OR -S\n";
cout << " Indent 'switch' blocks, so that the inner 'case XXX:'\n";
cout << " headers are indented in relation to the switch block.\n";
cout << endl;
cout << " --indent-cases OR -K\n";
cout << " Indent case blocks from the 'case XXX:' headers.\n";
cout << " Case statements not enclosed in blocks are NOT indented.\n";
cout << endl;
cout << " --indent-namespaces OR -N\n";
cout << " Indent the contents of namespace blocks.\n";
cout << endl;
cout << " --indent-after-parens OR -xU\n";
cout << " Indent, instead of align, continuation lines following lines\n";
cout << " that contain an opening paren '(' or an assignment '='. \n";
cout << endl;
cout << " --indent-continuation=# OR -xt#\n";
cout << " Indent continuation lines an additional # indents.\n";
cout << " The valid values are 0 thru 4 indents.\n";
cout << " The default value is 1 indent.\n";
cout << endl;
cout << " --indent-labels OR -L\n";
cout << " Indent labels so that they appear one indent less than\n";
cout << " the current indentation level, rather than being\n";
cout << " flushed completely to the left (which is the default).\n";
cout << endl;
cout << " --indent-preproc-block OR -xW\n";
cout << " Indent preprocessor blocks at brace level 0.\n";
cout << " Without this option the preprocessor block is not indented.\n";
cout << endl;
cout << " --indent-preproc-cond OR -xw\n";
cout << " Indent preprocessor conditional statements #if/#else/#endif\n";
cout << " to the same level as the source code.\n";
cout << endl;
cout << " --indent-preproc-define OR -w\n";
cout << " Indent multi-line preprocessor #define statements.\n";
cout << endl;
cout << " --indent-col1-comments OR -Y\n";
cout << " Indent line comments that start in column one.\n";
cout << endl;
cout << " --min-conditional-indent=# OR -m#\n";
cout << " Indent a minimal # spaces in a continuous conditional\n";
cout << " belonging to a conditional header.\n";
cout << " The valid values are:\n";
cout << " 0 - no minimal indent.\n";
cout << " 1 - indent at least one additional indent.\n";
cout << " 2 - indent at least two additional indents.\n";
cout << " 3 - indent at least one-half an additional indent.\n";
cout << " The default value is 2, two additional indents.\n";
cout << endl;
cout << " --max-continuation-indent=# OR -M#\n";
cout << " Indent a maximal # spaces in a continuation line,\n";
cout << " relative to the previous line.\n";
cout << " The valid values are 40 thru 120.\n";
cout << " The default value is 40.\n";
cout << endl;
cout << "Padding Options:\n";
cout << "----------------\n";
cout << " --break-blocks OR -f\n";
cout << " Insert empty lines around unrelated blocks, labels, classes, ...\n";
cout << endl;
cout << " --break-blocks=all OR -F\n";
cout << " Like --break-blocks, except also insert empty lines \n";
cout << " around closing headers (e.g. 'else', 'catch', ...).\n";
cout << endl;
cout << " --pad-oper OR -p\n";
cout << " Insert space padding around operators.\n";
cout << endl;
cout << " --pad-comma OR -xg\n";
cout << " Insert space padding after commas.\n";
cout << endl;
cout << " --pad-paren OR -P\n";
cout << " Insert space padding around parenthesis on both the outside\n";
cout << " and the inside.\n";
cout << endl;
cout << " --pad-paren-out OR -d\n";
cout << " Insert space padding around parenthesis on the outside only.\n";
cout << endl;
cout << " --pad-first-paren-out OR -xd\n";
cout << " Insert space padding around first parenthesis in a series on\n";
cout << " the outside only.\n";
cout << endl;
cout << " --pad-paren-in OR -D\n";
cout << " Insert space padding around parenthesis on the inside only.\n";
cout << endl;
cout << " --pad-header OR -H\n";
cout << " Insert space padding after paren headers (e.g. 'if', 'for'...).\n";
cout << endl;
cout << " --unpad-paren OR -U\n";
cout << " Remove unnecessary space padding around parenthesis. This\n";
cout << " can be used in combination with the 'pad' options above.\n";
cout << endl;
cout << " --delete-empty-lines OR -xe\n";
cout << " Delete empty lines.\n";
cout << " It will NOT delete lines added by the break-blocks options.\n";
cout << endl;
cout << " --delete-multiple-empty-lines OR -xm\n";
cout << " Delete consecutive empty lines.\n";
cout << " It will NOT delete lines added by the break-blocks options.\n";
cout << endl;
cout << " --fill-empty-lines OR -E\n";
cout << " Fill empty lines with the white space of their\n";
cout << " previous lines.\n";
cout << endl;
cout << " --align-pointer=type OR -k1\n";
cout << " --align-pointer=middle OR -k2\n";
cout << " --align-pointer=name OR -k3\n";
cout << " Attach a pointer or reference operator (*, &, or ^) to either\n";
cout << " the operator type (left), middle, or operator name (right).\n";
cout << " To align the reference separately use --align-reference.\n";
cout << endl;
cout << " --align-reference=none OR -W0\n";
cout << " --align-reference=type OR -W1\n";
cout << " --align-reference=middle OR -W2\n";
cout << " --align-reference=name OR -W3\n";
cout << " Attach a reference operator (&) to either\n";
cout << " the operator type (left), middle, or operator name (right).\n";
cout << " If not set, follow pointer alignment.\n";
cout << endl;
cout << "Formatting Options:\n";
cout << "-------------------\n";
cout << " --break-closing-braces OR -y\n";
cout << " Break braces before closing headers (e.g. 'else', 'catch', ...).\n";
cout << " Use with --style=java, --style=kr, --style=stroustrup,\n";
cout << " --style=linux, or --style=1tbs.\n";
cout << endl;
cout << " --break-elseifs OR -e\n";
cout << " Break 'else if()' statements into two different lines.\n";
cout << endl;
cout << " --break-one-line-headers OR -xb\n";
cout << " Break one line headers (e.g. 'if', 'while', 'else', ...) from a\n";
cout << " statement residing on the same line.\n";
cout << endl;
cout << " --add-braces OR -j\n";
cout << " Add braces to unbraced one line conditional statements.\n";
cout << endl;
cout << " --add-one-line-braces OR -J\n";
cout << " Add one line braces to unbraced one line conditional\n";
cout << " statements.\n";
cout << endl;
cout << " --remove-braces OR -xj\n";
cout << " Remove braces from a braced one line conditional statements.\n";
cout << endl;
cout << " --break-return-type OR -xB\n";
cout << " --break-return-type-decl OR -xD\n";
cout << " Break the return type from the function name. Options are\n";
cout << " for the function definitions and the function declarations.\n";
cout << endl;
cout << " --attach-return-type OR -xf\n";
cout << " --attach-return-type-decl OR -xh\n";
cout << " Attach the return type to the function name. Options are\n";
cout << " for the function definitions and the function declarations.\n";
cout << endl;
cout << " --keep-one-line-blocks OR -O\n";
cout << " Don't break blocks residing completely on one line.\n";
cout << endl;
cout << " --keep-one-line-statements OR -o\n";
cout << " Don't break lines containing multiple statements into\n";
cout << " multiple single-statement lines.\n";
cout << endl;
cout << " --convert-tabs OR -c\n";
cout << " Convert tabs to the appropriate number of spaces.\n";
cout << endl;
cout << " --close-templates OR -xy\n";
cout << " Close ending angle brackets on template definitions.\n";
cout << endl;
cout << " --remove-comment-prefix OR -xp\n";
cout << " Remove the leading '*' prefix on multi-line comments and\n";
cout << " indent the comment text one indent.\n";
cout << endl;
cout << " --max-code-length=# OR -xC#\n";
cout << " --break-after-logical OR -xL\n";
cout << " max-code-length=# will break the line if it exceeds more than\n";
cout << " # characters. The valid values are 50 thru 200.\n";
cout << " If the line contains logical conditionals they will be placed\n";
cout << " first on the new line. The option break-after-logical will\n";
cout << " cause the logical conditional to be placed last on the\n";
cout << " previous line.\n";
cout << endl;
cout << " --mode=c\n";
cout << " Indent a C or C++ source file (this is the default).\n";
cout << endl;
cout << " --mode=java\n";
cout << " Indent a Java source file.\n";
cout << endl;
cout << " --mode=cs\n";
cout << " Indent a C# source file.\n";
cout << endl;
cout << "Objective-C Options:\n";
cout << "--------------------\n";
cout << " --pad-method-prefix OR -xQ\n";
cout << " Insert space padding after the '-' or '+' Objective-C\n";
cout << " method prefix.\n";
cout << endl;
cout << " --unpad-method-prefix OR -xR\n";
cout << " Remove all space padding after the '-' or '+' Objective-C\n";
cout << " method prefix.\n";
cout << endl;
cout << " --pad-return-type OR -xq\n";
cout << " Insert space padding after the Objective-C return type.\n";
cout << endl;
cout << " --unpad-return-type OR -xr\n";
cout << " Remove all space padding after the Objective-C return type.\n";
cout << endl;
cout << " --pad-param-type OR -xS\n";
cout << " Insert space padding after the Objective-C return type.\n";
cout << endl;
cout << " --unpad-param-type OR -xs\n";
cout << " Remove all space padding after the Objective-C return type.\n";
cout << endl;
cout << " --align-method-colon OR -xM\n";
cout << " Align the colons in an Objective-C method definition.\n";
cout << endl;
cout << " --pad-method-colon=none OR -xP\n";
cout << " --pad-method-colon=all OR -xP1\n";
cout << " --pad-method-colon=after OR -xP2\n";
cout << " --pad-method-colon=before OR -xP3\n";
cout << " Add or remove space padding before or after the colons in an\n";
cout << " Objective-C method call.\n";
cout << endl;
cout << "Other Options:\n";
cout << "--------------\n";
cout << " --suffix=####\n";
cout << " Append the suffix #### instead of '.orig' to original filename.\n";
cout << endl;
cout << " --suffix=none OR -n\n";
cout << " Do not retain a backup of the original file.\n";
cout << endl;
cout << " --recursive OR -r OR -R\n";
cout << " Process subdirectories recursively.\n";
cout << endl;
cout << " --dry-run\n";
cout << " Perform a trial run with no changes made to check for formatting.\n";
cout << endl;
cout << " --exclude=####\n";
cout << " Specify a file or directory #### to be excluded from processing.\n";
cout << endl;
cout << " --ignore-exclude-errors OR -i\n";
cout << " Allow processing to continue if there are errors in the exclude=####\n";
cout << " options. It will display the unmatched excludes.\n";
cout << endl;
cout << " --ignore-exclude-errors-x OR -xi\n";
cout << " Allow processing to continue if there are errors in the exclude=####\n";
cout << " options. It will NOT display the unmatched excludes.\n";
cout << endl;
cout << " --errors-to-stdout OR -X\n";
cout << " Print errors and help information to standard-output rather than\n";
cout << " to standard-error.\n";
cout << endl;
cout << " --preserve-date OR -Z\n";
cout << " Preserve the original file's date and time modified. The time\n";
cout << " modified will be changed a few micro seconds to force a compile.\n";
cout << endl;
cout << " --verbose OR -v\n";
cout << " Verbose mode. Extra informational messages will be displayed.\n";
cout << endl;
cout << " --formatted OR -Q\n";
cout << " Formatted display mode. Display only the files that have been\n";
cout << " formatted.\n";
cout << endl;
cout << " --quiet OR -q\n";
cout << " Quiet mode. Suppress all output except error messages.\n";
cout << endl;
cout << " --lineend=windows OR -z1\n";
cout << " --lineend=linux OR -z2\n";
cout << " --lineend=macold OR -z3\n";
cout << " Force use of the specified line end style. Valid options\n";
cout << " are windows (CRLF), linux (LF), and macold (CR).\n";
cout << endl;
cout << "Command Line Only:\n";
cout << "------------------\n";
cout << " --options=####\n";
cout << " --options=none\n";
cout << " Specify a default option file #### to read and use.\n";
cout << " It must contain a file path and a file name.\n";
cout << " 'none' disables the default option file.\n";
cout << endl;
cout << " --project\n";
cout << " --project=####\n";
cout << " --project=none\n";
cout << " Specify a project option file #### to read and use.\n";
cout << " It must contain a file name only, without a directory path.\n";
cout << " The file should be included in the project top-level directory.\n";
cout << " The default file name is .astylerc or _astylerc.\n";
cout << " 'none' disables the project or environment variable file.\n";
cout << endl;
cout << " --ascii OR -I\n";
cout << " The displayed output will be ascii characters only.\n";
cout << endl;
cout << " --version OR -V\n";
cout << " Print version number.\n";
cout << endl;
cout << " --help OR -h OR -?\n";
cout << " Print this help message.\n";
cout << endl;
cout << " --html OR -!\n";
cout << " Open the HTML help file \"astyle.html\" in the default browser.\n";
cout << " The documentation must be installed in the standard install path.\n";
cout << endl;
cout << " --html=####\n";
cout << " Open a HTML help file in the default browser using the file path\n";
cout << " ####. The path may include a directory path and a file name, or a\n";
cout << " file name only. Paths containing spaces must be enclosed in quotes.\n";
cout << endl;
cout << " --stdin=####\n";
cout << " Use the file path #### as input to single file formatting.\n";
cout << " This is a replacement for redirection.\n";
cout << endl;
cout << " --stdout=####\n";
cout << " Use the file path #### as output from single file formatting.\n";
cout << " This is a replacement for redirection.\n";
cout << endl;
cout << endl;
}
/**
* Process files in the fileNameVector.
*/
void ASConsole::processFiles()
{
if (isVerbose)
printVerboseHeader();
clock_t startTime = clock(); // start time of file formatting
// loop thru input fileNameVector and process the files
for (const string& fileNameVectorName : fileNameVector)
{
getFilePaths(fileNameVectorName);
// loop thru fileName vector formatting the files
for (const string& file : fileName)
formatFile(file);
}
// files are processed, display stats
if (isVerbose)
printVerboseStats(startTime);
}
// process options from the command line and option files
// build the vectors fileNameVector, excludeVector, optionsVector,
// projectOptionsVector and fileOptionsVector
void ASConsole::processOptions(const vector<string>& argvOptions)
{
bool ok = true;
bool optionFileRequired = false;
bool shouldParseOptionFile = true;
bool projectOptionFileRequired = false;
bool shouldParseProjectOptionFile = true;
string projectOptionArg; // save for display
// get command line options
for (string arg : argvOptions)
{
if (isOption(arg, "-I")
|| isOption(arg, "--ascii"))
{
useAscii = true;
setlocale(LC_ALL, "C"); // use English decimal indicator
localizer.setLanguageFromName("en");
}
else if (isOption(arg, "--options=none"))
{
optionFileRequired = false;
shouldParseOptionFile = false;
optionFileName = "";
}
else if (isParamOption(arg, "--options="))
{
optionFileName = getParam(arg, "--options=");
standardizePath(optionFileName);
optionFileName = getFullPathName(optionFileName);
optionFileRequired = true;
}
else if (isOption(arg, "--project=none"))
{
projectOptionFileRequired = false;
shouldParseProjectOptionFile = false;
setProjectOptionFileName("");
}
else if (isParamOption(arg, "--project="))
{
projectOptionFileName = getParam(arg, "--project=");
standardizePath(projectOptionFileName);
projectOptionFileRequired = true;
shouldParseProjectOptionFile = false;
projectOptionArg = projectOptionFileName;
}
else if (isOption(arg, "--project"))
{
projectOptionFileName = ".astylerc";
projectOptionFileRequired = true;
shouldParseProjectOptionFile = false;
projectOptionArg = projectOptionFileName;
}
else if (isOption(arg, "-h")
|| isOption(arg, "--help")
|| isOption(arg, "-?"))
{
printHelp();
exit(EXIT_SUCCESS);
}
else if (isOption(arg, "-!")
|| isOption(arg, "--html"))
{
launchDefaultBrowser();
exit(EXIT_SUCCESS);
}
else if (isParamOption(arg, "--html="))
{
string htmlFilePath = getParam(arg, "--html=");
launchDefaultBrowser(htmlFilePath.c_str());
exit(EXIT_SUCCESS);
}
else if (isOption(arg, "-V")
|| isOption(arg, "--version"))
{
printf("Artistic Style Version %s\n", g_version);
exit(EXIT_SUCCESS);
}
else if (isParamOption(arg, "--stdin="))
{
string path = getParam(arg, "--stdin=");
standardizePath(path);
setStdPathIn(path);
}
else if (isParamOption(arg, "--stdout="))
{
string path = getParam(arg, "--stdout=");
standardizePath(path);
setStdPathOut(path);
}
else if (arg[0] == '-')
{
optionsVector.emplace_back(arg);
}
else // file-name
{
standardizePath(arg);
fileNameVector.emplace_back(arg);
}
}
// get option file path and name
if (shouldParseOptionFile)
{
if (optionFileName.empty())
{
const char* const env = getenv("ARTISTIC_STYLE_OPTIONS");
if (env != nullptr)
{
setOptionFileName(env);
standardizePath(optionFileName);
optionFileName = getFullPathName(optionFileName);
}
}
// for Linux
if (optionFileName.empty())
{
const char* const env = getenv("HOME");
if (env != nullptr)
{
string name = string(env) + "/.astylerc";
if (fileExists(name.c_str()))
setOptionFileName(name);
}
}
// for Windows
if (optionFileName.empty())
{
const char* const env = getenv("APPDATA");
if (env != nullptr)
{
string name = string(env) + "\\astylerc";
if (fileExists(name.c_str()))
setOptionFileName(name);
}
}
// for Windows
// NOTE: depreciated with release 3.1, remove when appropriate
// there is NO test data for this option
if (optionFileName.empty())
{
const char* const env = getenv("USERPROFILE");
if (env != nullptr)
{
string name = string(env) + "\\astylerc";
if (fileExists(name.c_str()))
setOptionFileName(name);
}
}
}
// find project option file
if (projectOptionFileRequired)
{
string optfilepath = findProjectOptionFilePath(projectOptionFileName);
if (optfilepath.empty() || projectOptionArg.empty())
error(_("Cannot open project option file"), projectOptionArg.c_str());
standardizePath(optfilepath);
setProjectOptionFileName(optfilepath);
}
if (shouldParseProjectOptionFile)
{
const char* const env = getenv("ARTISTIC_STYLE_PROJECT_OPTIONS");
if (env != nullptr)
{
string optfilepath = findProjectOptionFilePath(env);
standardizePath(optfilepath);
setProjectOptionFileName(optfilepath);
}
}
ASOptions options(formatter, *this);
if (!optionFileName.empty())
{
stringstream optionsIn;
if (!fileExists(optionFileName.c_str()))
error(_("Cannot open default option file"), optionFileName.c_str());
FileEncoding encoding = readFile(optionFileName, optionsIn);
// bypass a BOM, all BOMs have been converted to utf-8
if (encoding == UTF_8BOM || encoding == UTF_16LE || encoding == UTF_16BE)
{
char buf[4];
optionsIn.get(buf, 4);
assert(strcmp(buf, "\xEF\xBB\xBF") == 0);
}
options.importOptions(optionsIn, fileOptionsVector);
ok = options.parseOptions(fileOptionsVector,
string(_("Invalid default options:")));
}
else if (optionFileRequired)
error(_("Cannot open default option file"), optionFileName.c_str());
if (!ok)
{
(*errorStream) << options.getOptionErrors();
(*errorStream) << _("For help on options type 'astyle -h'") << endl;
error();
}
if (!projectOptionFileName.empty())
{
stringstream projectOptionsIn;
if (!fileExists(projectOptionFileName.c_str()))
error(_("Cannot open project option file"), projectOptionFileName.c_str());
FileEncoding encoding = readFile(projectOptionFileName, projectOptionsIn);
// bypass a BOM, all BOMs have been converted to utf-8
if (encoding == UTF_8BOM || encoding == UTF_16LE || encoding == UTF_16BE)
{
char buf[4];
projectOptionsIn.get(buf, 4);
assert(strcmp(buf, "\xEF\xBB\xBF") == 0);
}
options.importOptions(projectOptionsIn, projectOptionsVector);
ok = options.parseOptions(projectOptionsVector,
string(_("Invalid project options:")));
}
if (!ok)
{
(*errorStream) << options.getOptionErrors();
(*errorStream) << _("For help on options type 'astyle -h'") << endl;
error();
}
// parse the command line options vector for errors
ok = options.parseOptions(optionsVector,
string(_("Invalid command line options:")));
if (!ok)
{
(*errorStream) << options.getOptionErrors();
(*errorStream) << _("For help on options type 'astyle -h'") << endl;
error();
}
}
// remove a file and check for an error
void ASConsole::removeFile(const char* fileName_, const char* errMsg) const
{
if (remove(fileName_) != 0)
{
if (errno == ENOENT) // no file is OK
errno = 0;
if (errno)
{
perror("errno message");
error(errMsg, fileName_);
}
}
}
// rename a file and check for an error
void ASConsole::renameFile(const char* oldFileName, const char* newFileName, const char* errMsg) const
{
int result = rename(oldFileName, newFileName);
if (result != 0)
{
// if file still exists the remove needs more time - retry
if (errno == EEXIST)
{
errno = 0;
waitForRemove(newFileName);
result = rename(oldFileName, newFileName);
}
if (result != 0)
{
perror("errno message");
error(errMsg, oldFileName);
}
}
}
// make sure file separators are correct type (Windows or Linux)
// remove ending file separator
// remove beginning file separator if requested and NOT a complete file path
void ASConsole::standardizePath(string& path, bool removeBeginningSeparator /*false*/) const
{
#ifdef __VMS
struct FAB fab;
struct NAML naml;
char less[NAML$C_MAXRSS];
char sess[NAM$C_MAXRSS];
int r0_status;
// If we are on a VMS system, translate VMS style filenames to unix
// style.
fab = cc$rms_fab;
fab.fab$l_fna = (char*) -1;
fab.fab$b_fns = 0;
fab.fab$l_naml = &naml;
naml = cc$rms_naml;
strcpy(sess, path.c_str());
naml.naml$l_long_filename = (char*) sess;
naml.naml$l_long_filename_size = path.length();
naml.naml$l_long_expand = less;
naml.naml$l_long_expand_alloc = sizeof(less);
naml.naml$l_esa = sess;
naml.naml$b_ess = sizeof(sess);
naml.naml$v_no_short_upcase = 1;
r0_status = sys$parse(&fab);
if (r0_status == RMS$_SYN)
{
error("File syntax error", path.c_str());
}
else
{
if (!$VMS_STATUS_SUCCESS(r0_status))
{
(void) lib$signal(r0_status);
}
}
less[naml.naml$l_long_expand_size - naml.naml$b_ver] = '\0';
sess[naml.naml$b_esl - naml.naml$b_ver] = '\0';
if (naml.naml$l_long_expand_size > naml.naml$b_esl)
{
path = decc$translate_vms(less);
}
else
{
path = decc$translate_vms(sess);
}
#endif /* __VMS */
// make sure separators are correct type (Windows or Linux)
for (size_t i = 0; i < path.length(); i++)
{
i = path.find_first_of("/\\", i);
if (i == string::npos)
break;
path[i] = g_fileSeparator;
}
// remove beginning separator if requested
if (removeBeginningSeparator && (path[0] == g_fileSeparator))
path.erase(0, 1);
}
void ASConsole::printMsg(const char* msg, const string& data) const
{
if (isQuiet)
return;
printf(msg, data.c_str());
}
void ASConsole::printSeparatingLine() const
{
string line;
for (size_t i = 0; i < 60; i++)
line.append("-");
printMsg("%s\n", line);
}
void ASConsole::printVerboseHeader() const
{
assert(isVerbose);
if (isQuiet)
return;
// get the date
time_t lt;
char str[20];
lt = time(nullptr);
struct tm* ptr = localtime(&lt);
strftime(str, 20, "%x", ptr);
// print the header
// 60 is the length of the separator in printSeparatingLine()
string header = "Artistic Style " + string(g_version);
size_t numSpaces = 60 - header.length() - strlen(str);
header.append(numSpaces, ' ');
header.append(str);
header.append("\n");
printf("%s", header.c_str());
// print option files
if (!optionFileName.empty())
printf(_("Default option file %s\n"), optionFileName.c_str());
// NOTE: depreciated with release 3.1, remove when appropriate
if (!optionFileName.empty())
{
const char* const env = getenv("USERPROFILE");
if (env != nullptr && optionFileName == string(env) + "\\astylerc")
printf("The above option file has been DEPRECIATED\n");
}
// end depreciated
if (!projectOptionFileName.empty())
printf(_("Project option file %s\n"), projectOptionFileName.c_str());
}
void ASConsole::printVerboseStats(clock_t startTime) const
{
assert(isVerbose);
if (isQuiet)
return;
if (hasWildcard)
printSeparatingLine();
string formatted = getNumberFormat(filesFormatted);
string unchanged = getNumberFormat(filesUnchanged);
printf(_(" %s formatted %s unchanged "), formatted.c_str(), unchanged.c_str());
// show processing time
clock_t stopTime = clock();
double secs = (stopTime - startTime) / double(CLOCKS_PER_SEC);
if (secs < 60)
{
if (secs < 2.0)
printf("%.2f", secs);
else if (secs < 20.0)
printf("%.1f", secs);
else
printf("%.0f", secs);
printf("%s", _(" seconds "));
}
else
{
// show minutes and seconds if time is greater than one minute
int min = (int) secs / 60;
secs -= min * 60;
// NOTE: lround is not supported by MinGW and Embarcadero
int minsec = int(secs + .5);
printf(_("%d min %d sec "), min, minsec);
}
string lines = getNumberFormat(linesOut);
printf(_("%s lines\n"), lines.c_str());
printf("\n");
}
void ASConsole::sleep(int seconds) const
{
clock_t endwait;
endwait = clock_t(clock() + seconds * CLOCKS_PER_SEC);
while (clock() < endwait) {}
}
bool ASConsole::stringEndsWith(const string& str, const string& suffix) const
{
int strIndex = (int) str.length() - 1;
int suffixIndex = (int) suffix.length() - 1;
while (strIndex >= 0 && suffixIndex >= 0)
{
if (tolower(str[strIndex]) != tolower(suffix[suffixIndex]))
return false;
--strIndex;
--suffixIndex;
}
// suffix longer than string
if (strIndex < 0 && suffixIndex >= 0)
return false;
return true;
}
void ASConsole::updateExcludeVector(const string& suffixParam)
{
excludeVector.emplace_back(suffixParam);
standardizePath(excludeVector.back(), true);
// do not use emplace_back on vector<bool> until supported by macOS
excludeHitsVector.push_back(false);
}
int ASConsole::waitForRemove(const char* newFileName) const
{
struct stat stBuf;
int seconds;
// sleep a max of 20 seconds for the remove
for (seconds = 1; seconds <= 20; seconds++)
{
sleep(1);
if (stat(newFileName, &stBuf) != 0)
break;
}
errno = 0;
return seconds;
}
// From The Code Project http://www.codeproject.com/string/wildcmp.asp
// Written by Jack Handy - jakkhandy@hotmail.com
// Modified to compare case insensitive for Windows
int ASConsole::wildcmp(const char* wild, const char* data) const
{
const char* cp = nullptr;
const char* mp = nullptr;
bool cmpval;
while ((*data) && (*wild != '*'))
{
if (!g_isCaseSensitive)
cmpval = (tolower(*wild) != tolower(*data)) && (*wild != '?');
else
cmpval = (*wild != *data) && (*wild != '?');
if (cmpval)
{
return 0;
}
wild++;
data++;
}
while (*data)
{
if (*wild == '*')
{
if (!*++wild)
{
return 1;
}
mp = wild;
cp = data + 1;
}
else
{
if (!g_isCaseSensitive)
cmpval = (tolower(*wild) == tolower(*data) || (*wild == '?'));
else
cmpval = (*wild == *data) || (*wild == '?');
if (cmpval)
{
wild++;
data++;
}
else
{
wild = mp;
data = cp++;
}
}
}
while (*wild == '*')
{
wild++;
}
return !*wild;
}
void ASConsole::writeFile(const string& fileName_, FileEncoding encoding, ostringstream& out) const
{
// save date accessed and date modified of original file
struct stat stBuf;
bool statErr = false;
if (stat(fileName_.c_str(), &stBuf) == -1)
statErr = true;
// create a backup
if (!noBackup)
{
string origFileName = fileName_ + origSuffix;
removeFile(origFileName.c_str(), "Cannot remove pre-existing backup file");
renameFile(fileName_.c_str(), origFileName.c_str(), "Cannot create backup file");
}
// write the output file
ofstream fout(fileName_.c_str(), ios::binary | ios::trunc);
if (!fout)
error("Cannot open output file", fileName_.c_str());
if (encoding == UTF_16LE || encoding == UTF_16BE)
{
// convert utf-8 to utf-16
bool isBigEndian = (encoding == UTF_16BE);
size_t utf16Size = encode.utf16LengthFromUtf8(out.str().c_str(), out.str().length());
char* utf16Out = new char[utf16Size];
size_t utf16Len = encode.utf8ToUtf16(const_cast<char*>(out.str().c_str()),
out.str().length(), isBigEndian, utf16Out);
assert(utf16Len <= utf16Size);
fout << string(utf16Out, utf16Len);
delete[] utf16Out;
}
else
fout << out.str();
fout.close();
// change date modified to original file date
// Embarcadero must be linked with cw32mt not cw32
if (preserveDate)
{
if (!statErr)
{
struct utimbuf outBuf;
outBuf.actime = stBuf.st_atime;
// add ticks so 'make' will recognize a change
// Visual Studio 2008 needs more than 1
outBuf.modtime = stBuf.st_mtime + 10;
if (utime(fileName_.c_str(), &outBuf) == -1)
statErr = true;
}
if (statErr)
{
perror("errno message");
(*errorStream) << "********* Cannot preserve file date" << endl;
}
}
}
#else // ASTYLE_LIB
//-----------------------------------------------------------------------------
// ASLibrary class
// used by shared object (DLL) calls
//-----------------------------------------------------------------------------
char16_t* ASLibrary::formatUtf16(const char16_t* pSourceIn, // the source to be formatted
const char16_t* pOptions, // AStyle options
fpError fpErrorHandler, // error handler function
fpAlloc fpMemoryAlloc) const // memory allocation function)
{
const char* utf8In = convertUtf16ToUtf8(pSourceIn);
if (utf8In == nullptr)
{
fpErrorHandler(121, "Cannot convert input utf-16 to utf-8.");
return nullptr;
}
const char* utf8Options = convertUtf16ToUtf8(pOptions);
if (utf8Options == nullptr)
{
delete[] utf8In;
fpErrorHandler(122, "Cannot convert options utf-16 to utf-8.");
return nullptr;
}
// call the Artistic Style formatting function
// cannot use the callers memory allocation here
char* utf8Out = AStyleMain(utf8In,
utf8Options,
fpErrorHandler,
ASLibrary::tempMemoryAllocation);
// finished with these
delete[] utf8In;
delete[] utf8Options;
utf8In = nullptr;
utf8Options = nullptr;
// AStyle error has already been sent
if (utf8Out == nullptr)
return nullptr;
// convert text to wide char and return it
char16_t* utf16Out = convertUtf8ToUtf16(utf8Out, fpMemoryAlloc);
delete[] utf8Out;
utf8Out = nullptr;
if (utf16Out == nullptr)
{
fpErrorHandler(123, "Cannot convert output utf-8 to utf-16.");
return nullptr;
}
return utf16Out;
}
// STATIC method to allocate temporary memory for AStyle formatting.
// The data will be converted before being returned to the calling program.
char* STDCALL ASLibrary::tempMemoryAllocation(unsigned long memoryNeeded)
{
char* buffer = new (nothrow) char[memoryNeeded];
return buffer;
}
/**
* Convert utf-8 strings to utf16 strings.
* Memory is allocated by the calling program memory allocation function.
* The calling function must check for errors.
*/
char16_t* ASLibrary::convertUtf8ToUtf16(const char* utf8In, fpAlloc fpMemoryAlloc) const
{
if (utf8In == nullptr)
return nullptr;
char* data = const_cast<char*>(utf8In);
size_t dataSize = strlen(utf8In);
bool isBigEndian = encode.getBigEndian();
// return size is in number of CHARs, not char16_t
size_t utf16Size = (encode.utf16LengthFromUtf8(data, dataSize) + sizeof(char16_t));
char* utf16Out = fpMemoryAlloc((long) utf16Size);
if (utf16Out == nullptr)
return nullptr;
#ifdef NDEBUG
encode.utf8ToUtf16(data, dataSize + 1, isBigEndian, utf16Out);
#else
size_t utf16Len = encode.utf8ToUtf16(data, dataSize + 1, isBigEndian, utf16Out);
assert(utf16Len == utf16Size);
#endif
assert(utf16Size == (encode.utf16len(reinterpret_cast<char16_t*>(utf16Out)) + 1) * sizeof(char16_t));
return reinterpret_cast<char16_t*>(utf16Out);
}
/**
* Convert utf16 strings to utf-8.
* The calling function must check for errors and delete the
* allocated memory.
*/
char* ASLibrary::convertUtf16ToUtf8(const char16_t* utf16In) const
{
if (utf16In == nullptr)
return nullptr;
char* data = reinterpret_cast<char*>(const_cast<char16_t*>(utf16In));
// size must be in chars
size_t dataSize = encode.utf16len(utf16In) * sizeof(char16_t);
bool isBigEndian = encode.getBigEndian();
size_t utf8Size = encode.utf8LengthFromUtf16(data, dataSize, isBigEndian) + 1;
char* utf8Out = new (nothrow) char[utf8Size];
if (utf8Out == nullptr)
return nullptr;
#ifdef NDEBUG
encode.utf16ToUtf8(data, dataSize + 1, isBigEndian, true, utf8Out);
#else
size_t utf8Len = encode.utf16ToUtf8(data, dataSize + 1, isBigEndian, true, utf8Out);
assert(utf8Len == utf8Size);
#endif
assert(utf8Size == strlen(utf8Out) + 1);
return utf8Out;
}
#endif // ASTYLE_LIB
//-----------------------------------------------------------------------------
// ASOptions class
// used by both console and library builds
//-----------------------------------------------------------------------------
#ifdef ASTYLE_LIB
ASOptions::ASOptions(ASFormatter& formatterArg)
: formatter(formatterArg)
{ }
#else
ASOptions::ASOptions(ASFormatter& formatterArg, ASConsole& consoleArg)
: formatter(formatterArg), console(consoleArg)
{ }
#endif
/**
* parse the options vector
* optionsVector can be either a fileOptionsVector (option file),
* a projectOptionsVector (project option file),
* or an optionsVector (command line)
*
* @return true if no errors, false if errors
*/
bool ASOptions::parseOptions(vector<string>& optionsVector, const string& errorInfo)
{
vector<string>::iterator option;
string arg;
string subArg;
optionErrors.clear();
for (option = optionsVector.begin(); option != optionsVector.end(); ++option)
{
arg = *option;
if (arg.compare(0, 2, "--") == 0)
parseOption(arg.substr(2), errorInfo);
else if (arg[0] == '-')
{
size_t i;
for (i = 1; i < arg.length(); ++i)
{
if (i > 1
&& isalpha((unsigned char) arg[i])
&& arg[i - 1] != 'x')
{
// parse the previous option in subArg
parseOption(subArg, errorInfo);
subArg = "";
}
// append the current option to subArg
subArg.append(1, arg[i]);
}
// parse the last option
parseOption(subArg, errorInfo);
subArg = "";
}
else
{
parseOption(arg, errorInfo);
subArg = "";
}
}
if (optionErrors.str().length() > 0)
return false;
return true;
}
void ASOptions::parseOption(const string& arg, const string& errorInfo)
{
if (isOption(arg, "A1", "style=allman") || isOption(arg, "style=bsd") || isOption(arg, "style=break"))
{
formatter.setFormattingStyle(STYLE_ALLMAN);
}
else if (isOption(arg, "A2", "style=java") || isOption(arg, "style=attach"))
{
formatter.setFormattingStyle(STYLE_JAVA);
}
else if (isOption(arg, "A3", "style=k&r") || isOption(arg, "style=kr") || isOption(arg, "style=k/r"))
{
formatter.setFormattingStyle(STYLE_KR);
}
else if (isOption(arg, "A4", "style=stroustrup"))
{
formatter.setFormattingStyle(STYLE_STROUSTRUP);
}
else if (isOption(arg, "A5", "style=whitesmith"))
{
formatter.setFormattingStyle(STYLE_WHITESMITH);
}
else if (isOption(arg, "A15", "style=vtk"))
{
formatter.setFormattingStyle(STYLE_VTK);
}
else if (isOption(arg, "A6", "style=ratliff") || isOption(arg, "style=banner"))
{
formatter.setFormattingStyle(STYLE_RATLIFF);
}
else if (isOption(arg, "A7", "style=gnu"))
{
formatter.setFormattingStyle(STYLE_GNU);
}
else if (isOption(arg, "A8", "style=linux") || isOption(arg, "style=knf"))
{
formatter.setFormattingStyle(STYLE_LINUX);
}
else if (isOption(arg, "A9", "style=horstmann") || isOption(arg, "style=run-in"))
{
formatter.setFormattingStyle(STYLE_HORSTMANN);
}
else if (isOption(arg, "A10", "style=1tbs") || isOption(arg, "style=otbs"))
{
formatter.setFormattingStyle(STYLE_1TBS);
}
else if (isOption(arg, "A14", "style=google"))
{
formatter.setFormattingStyle(STYLE_GOOGLE);
}
else if (isOption(arg, "A16", "style=mozilla"))
{
formatter.setFormattingStyle(STYLE_MOZILLA);
}
else if (isOption(arg, "A17", "style=webkit"))
{
formatter.setFormattingStyle(STYLE_WEBKIT);
}
else if (isOption(arg, "A11", "style=pico"))
{
formatter.setFormattingStyle(STYLE_PICO);
}
else if (isOption(arg, "A12", "style=lisp") || isOption(arg, "style=python"))
{
formatter.setFormattingStyle(STYLE_LISP);
}
// must check for mode=cs before mode=c !!!
else if (isOption(arg, "mode=cs"))
{
formatter.setSharpStyle();
formatter.setModeManuallySet(true);
}
else if (isOption(arg, "mode=c"))
{
formatter.setCStyle();
formatter.setModeManuallySet(true);
}
else if (isOption(arg, "mode=java"))
{
formatter.setJavaStyle();
formatter.setModeManuallySet(true);
}
else if (isParamOption(arg, "t", "indent=tab="))
{
int spaceNum = 4;
string spaceNumParam = getParam(arg, "t", "indent=tab=");
if (spaceNumParam.length() > 0)
spaceNum = atoi(spaceNumParam.c_str());
if (spaceNum < 2 || spaceNum > 20)
isOptionError(arg, errorInfo);
else
{
formatter.setTabIndentation(spaceNum, false);
}
}
else if (isOption(arg, "indent=tab"))
{
formatter.setTabIndentation(4);
}
else if (isParamOption(arg, "T", "indent=force-tab="))
{
int spaceNum = 4;
string spaceNumParam = getParam(arg, "T", "indent=force-tab=");
if (spaceNumParam.length() > 0)
spaceNum = atoi(spaceNumParam.c_str());
if (spaceNum < 2 || spaceNum > 20)
isOptionError(arg, errorInfo);
else
{
formatter.setTabIndentation(spaceNum, true);
}
}
else if (isOption(arg, "indent=force-tab"))
{
formatter.setTabIndentation(4, true);
}
else if (isParamOption(arg, "xT", "indent=force-tab-x="))
{
int tabNum = 8;
string tabNumParam = getParam(arg, "xT", "indent=force-tab-x=");
if (tabNumParam.length() > 0)
tabNum = atoi(tabNumParam.c_str());
if (tabNum < 2 || tabNum > 20)
isOptionError(arg, errorInfo);
else
{
formatter.setForceTabXIndentation(tabNum);
}
}
else if (isOption(arg, "indent=force-tab-x"))
{
formatter.setForceTabXIndentation(8);
}
else if (isParamOption(arg, "s", "indent=spaces="))
{
int spaceNum = 4;
string spaceNumParam = getParam(arg, "s", "indent=spaces=");
if (spaceNumParam.length() > 0)
spaceNum = atoi(spaceNumParam.c_str());
if (spaceNum < 2 || spaceNum > 20)
isOptionError(arg, errorInfo);
else
{
formatter.setSpaceIndentation(spaceNum);
}
}
else if (isOption(arg, "indent=spaces"))
{
formatter.setSpaceIndentation(4);
}
else if (isParamOption(arg, "xt", "indent-continuation="))
{
int contIndent = 1;
string contIndentParam = getParam(arg, "xt", "indent-continuation=");
if (contIndentParam.length() > 0)
contIndent = atoi(contIndentParam.c_str());
if (contIndent < 0)
isOptionError(arg, errorInfo);
else if (contIndent > 4)
isOptionError(arg, errorInfo);
else
formatter.setContinuationIndentation(contIndent);
}
else if (isParamOption(arg, "m", "min-conditional-indent="))
{
int minIndent = MINCOND_TWO;
string minIndentParam = getParam(arg, "m", "min-conditional-indent=");
if (minIndentParam.length() > 0)
minIndent = atoi(minIndentParam.c_str());
if (minIndent >= MINCOND_END)
isOptionError(arg, errorInfo);
else
formatter.setMinConditionalIndentOption(minIndent);
}
else if (isParamOption(arg, "M", "max-continuation-indent="))
{
int maxIndent = 40;
string maxIndentParam = getParam(arg, "M", "max-continuation-indent=");
if (maxIndentParam.length() > 0)
maxIndent = atoi(maxIndentParam.c_str());
if (maxIndent < 40)
isOptionError(arg, errorInfo);
else if (maxIndent > 120)
isOptionError(arg, errorInfo);
else
formatter.setMaxContinuationIndentLength(maxIndent);
}
else if (isOption(arg, "N", "indent-namespaces"))
{
formatter.setNamespaceIndent(true);
}
else if (isOption(arg, "C", "indent-classes"))
{
formatter.setClassIndent(true);
}
else if (isOption(arg, "xG", "indent-modifiers"))
{
formatter.setModifierIndent(true);
}
else if (isOption(arg, "S", "indent-switches"))
{
formatter.setSwitchIndent(true);
}
else if (isOption(arg, "K", "indent-cases"))
{
formatter.setCaseIndent(true);
}
else if (isOption(arg, "xU", "indent-after-parens"))
{
formatter.setAfterParenIndent(true);
}
else if (isOption(arg, "L", "indent-labels"))
{
formatter.setLabelIndent(true);
}
else if (isOption(arg, "xW", "indent-preproc-block"))
{
formatter.setPreprocBlockIndent(true);
}
else if (isOption(arg, "w", "indent-preproc-define"))
{
formatter.setPreprocDefineIndent(true);
}
else if (isOption(arg, "xw", "indent-preproc-cond"))
{
formatter.setPreprocConditionalIndent(true);
}
else if (isOption(arg, "y", "break-closing-braces"))
{
formatter.setBreakClosingHeaderBracesMode(true);
}
else if (isOption(arg, "O", "keep-one-line-blocks"))
{
formatter.setBreakOneLineBlocksMode(false);
}
else if (isOption(arg, "o", "keep-one-line-statements"))
{
formatter.setBreakOneLineStatementsMode(false);
}
else if (isOption(arg, "P", "pad-paren"))
{
formatter.setParensOutsidePaddingMode(true);
formatter.setParensInsidePaddingMode(true);
}
else if (isOption(arg, "d", "pad-paren-out"))
{
formatter.setParensOutsidePaddingMode(true);
}
else if (isOption(arg, "xd", "pad-first-paren-out"))
{
formatter.setParensFirstPaddingMode(true);
}
else if (isOption(arg, "D", "pad-paren-in"))
{
formatter.setParensInsidePaddingMode(true);
}
else if (isOption(arg, "H", "pad-header"))
{
formatter.setParensHeaderPaddingMode(true);
}
else if (isOption(arg, "U", "unpad-paren"))
{
formatter.setParensUnPaddingMode(true);
}
else if (isOption(arg, "p", "pad-oper"))
{
formatter.setOperatorPaddingMode(true);
}
else if (isOption(arg, "xg", "pad-comma"))
{
formatter.setCommaPaddingMode(true);
}
else if (isOption(arg, "xe", "delete-empty-lines"))
{
formatter.setDeleteEmptyLinesMode(true);
}
else if (isOption(arg, "xm", "delete-multiple-empty-lines"))
{
formatter.setDeleteMultipleEmptyLinesMode(true);
}
else if (isOption(arg, "E", "fill-empty-lines"))
{
formatter.setEmptyLineFill(true);
}
else if (isOption(arg, "c", "convert-tabs"))
{
formatter.setTabSpaceConversionMode(true);
}
else if (isOption(arg, "xy", "close-templates"))
{
formatter.setCloseTemplatesMode(true);
}
else if (isOption(arg, "F", "break-blocks=all"))
{
formatter.setBreakBlocksMode(true);
formatter.setBreakClosingHeaderBlocksMode(true);
}
else if (isOption(arg, "f", "break-blocks"))
{
formatter.setBreakBlocksMode(true);
}
else if (isOption(arg, "e", "break-elseifs"))
{
formatter.setBreakElseIfsMode(true);
}
else if (isOption(arg, "xb", "break-one-line-headers"))
{
formatter.setBreakOneLineHeadersMode(true);
}
else if (isOption(arg, "j", "add-braces"))
{
formatter.setAddBracesMode(true);
}
else if (isOption(arg, "J", "add-one-line-braces"))
{
formatter.setAddOneLineBracesMode(true);
}
else if (isOption(arg, "xj", "remove-braces"))
{
formatter.setRemoveBracesMode(true);
}
else if (isOption(arg, "Y", "indent-col1-comments"))
{
formatter.setIndentCol1CommentsMode(true);
}
else if (isOption(arg, "align-pointer=type"))
{
formatter.setPointerAlignment(PTR_ALIGN_TYPE);
}
else if (isOption(arg, "align-pointer=middle"))
{
formatter.setPointerAlignment(PTR_ALIGN_MIDDLE);
}
else if (isOption(arg, "align-pointer=name"))
{
formatter.setPointerAlignment(PTR_ALIGN_NAME);
}
else if (isParamOption(arg, "k"))
{
int align = 0;
string styleParam = getParam(arg, "k");
if (styleParam.length() > 0)
align = atoi(styleParam.c_str());
if (align < 1 || align > 3)
isOptionError(arg, errorInfo);
else if (align == 1)
formatter.setPointerAlignment(PTR_ALIGN_TYPE);
else if (align == 2)
formatter.setPointerAlignment(PTR_ALIGN_MIDDLE);
else if (align == 3)
formatter.setPointerAlignment(PTR_ALIGN_NAME);
}
else if (isOption(arg, "align-reference=none"))
{
formatter.setReferenceAlignment(REF_ALIGN_NONE);
}
else if (isOption(arg, "align-reference=type"))
{
formatter.setReferenceAlignment(REF_ALIGN_TYPE);
}
else if (isOption(arg, "align-reference=middle"))
{
formatter.setReferenceAlignment(REF_ALIGN_MIDDLE);
}
else if (isOption(arg, "align-reference=name"))
{
formatter.setReferenceAlignment(REF_ALIGN_NAME);
}
else if (isParamOption(arg, "W"))
{
int align = 0;
string styleParam = getParam(arg, "W");
if (styleParam.length() > 0)
align = atoi(styleParam.c_str());
if (align < 0 || align > 3)
isOptionError(arg, errorInfo);
else if (align == 0)
formatter.setReferenceAlignment(REF_ALIGN_NONE);
else if (align == 1)
formatter.setReferenceAlignment(REF_ALIGN_TYPE);
else if (align == 2)
formatter.setReferenceAlignment(REF_ALIGN_MIDDLE);
else if (align == 3)
formatter.setReferenceAlignment(REF_ALIGN_NAME);
}
else if (isParamOption(arg, "max-code-length="))
{
int maxLength = 50;
string maxLengthParam = getParam(arg, "max-code-length=");
if (maxLengthParam.length() > 0)
maxLength = atoi(maxLengthParam.c_str());
if (maxLength < 50)
isOptionError(arg, errorInfo);
else if (maxLength > 200)
isOptionError(arg, errorInfo);
else
formatter.setMaxCodeLength(maxLength);
}
else if (isParamOption(arg, "xC"))
{
int maxLength = 50;
string maxLengthParam = getParam(arg, "xC");
if (maxLengthParam.length() > 0)
maxLength = atoi(maxLengthParam.c_str());
if (maxLength > 200)
isOptionError(arg, errorInfo);
else
formatter.setMaxCodeLength(maxLength);
}
else if (isOption(arg, "xL", "break-after-logical"))
{
formatter.setBreakAfterMode(true);
}
else if (isOption(arg, "xc", "attach-classes"))
{
formatter.setAttachClass(true);
}
else if (isOption(arg, "xV", "attach-closing-while"))
{
formatter.setAttachClosingWhile(true);
}
else if (isOption(arg, "xk", "attach-extern-c"))
{
formatter.setAttachExternC(true);
}
else if (isOption(arg, "xn", "attach-namespaces"))
{
formatter.setAttachNamespace(true);
}
else if (isOption(arg, "xl", "attach-inlines"))
{
formatter.setAttachInline(true);
}
else if (isOption(arg, "xp", "remove-comment-prefix"))
{
formatter.setStripCommentPrefix(true);
}
else if (isOption(arg, "xB", "break-return-type"))
{
formatter.setBreakReturnType(true);
}
else if (isOption(arg, "xD", "break-return-type-decl"))
{
formatter.setBreakReturnTypeDecl(true);
}
else if (isOption(arg, "xf", "attach-return-type"))
{
formatter.setAttachReturnType(true);
}
else if (isOption(arg, "xh", "attach-return-type-decl"))
{
formatter.setAttachReturnTypeDecl(true);
}
// To avoid compiler limit of blocks nested too deep.
else if (!parseOptionContinued(arg, errorInfo))
{
isOptionError(arg, errorInfo);
}
} // End of parseOption function
// Continuation of parseOption.
// To avoid compiler limit of blocks nested too deep.
// Return 'true' if the option was found and processed.
// Return 'false' if the option was not found.
bool ASOptions::parseOptionContinued(const string& arg, const string& errorInfo)
{
// Objective-C options
if (isOption(arg, "xQ", "pad-method-prefix"))
{
formatter.setMethodPrefixPaddingMode(true);
}
else if (isOption(arg, "xR", "unpad-method-prefix"))
{
formatter.setMethodPrefixUnPaddingMode(true);
}
else if (isOption(arg, "xq", "pad-return-type"))
{
formatter.setReturnTypePaddingMode(true);
}
else if (isOption(arg, "xr", "unpad-return-type"))
{
formatter.setReturnTypeUnPaddingMode(true);
}
else if (isOption(arg, "xS", "pad-param-type"))
{
formatter.setParamTypePaddingMode(true);
}
else if (isOption(arg, "xs", "unpad-param-type"))
{
formatter.setParamTypeUnPaddingMode(true);
}
else if (isOption(arg, "xM", "align-method-colon"))
{
formatter.setAlignMethodColon(true);
}
else if (isOption(arg, "xP0", "pad-method-colon=none"))
{
formatter.setObjCColonPaddingMode(COLON_PAD_NONE);
}
else if (isOption(arg, "xP1", "pad-method-colon=all"))
{
formatter.setObjCColonPaddingMode(COLON_PAD_ALL);
}
else if (isOption(arg, "xP2", "pad-method-colon=after"))
{
formatter.setObjCColonPaddingMode(COLON_PAD_AFTER);
}
else if (isOption(arg, "xP3", "pad-method-colon=before"))
{
formatter.setObjCColonPaddingMode(COLON_PAD_BEFORE);
}
// NOTE: depreciated options - remove when appropriate
// depreciated options ////////////////////////////////////////////////////////////////////////
else if (isOption(arg, "indent-preprocessor")) // depreciated release 2.04
{
formatter.setPreprocDefineIndent(true);
}
else if (isOption(arg, "style=ansi")) // depreciated release 2.05
{
formatter.setFormattingStyle(STYLE_ALLMAN);
}
// depreciated in release 3.0 /////////////////////////////////////////////////////////////////
else if (isOption(arg, "break-closing-brackets")) // depreciated release 3.0
{
formatter.setBreakClosingHeaderBracketsMode(true);
}
else if (isOption(arg, "add-brackets")) // depreciated release 3.0
{
formatter.setAddBracketsMode(true);
}
else if (isOption(arg, "add-one-line-brackets")) // depreciated release 3.0
{
formatter.setAddOneLineBracketsMode(true);
}
else if (isOption(arg, "remove-brackets")) // depreciated release 3.0
{
formatter.setRemoveBracketsMode(true);
}
else if (isParamOption(arg, "max-instatement-indent=")) // depreciated release 3.0
{
int maxIndent = 40;
string maxIndentParam = getParam(arg, "max-instatement-indent=");
if (maxIndentParam.length() > 0)
maxIndent = atoi(maxIndentParam.c_str());
if (maxIndent < 40)
isOptionError(arg, errorInfo);
else if (maxIndent > 120)
isOptionError(arg, errorInfo);
else
formatter.setMaxInStatementIndentLength(maxIndent);
}
// end depreciated options ////////////////////////////////////////////////////////////////////
#ifdef ASTYLE_LIB
// End of options used by GUI /////////////////////////////////////////////////////////////////
else
{
return false;
}
return true;
#else
// Options used by only console ///////////////////////////////////////////////////////////////
else if (isOption(arg, "n", "suffix=none"))
{
console.setNoBackup(true);
}
else if (isParamOption(arg, "suffix="))
{
string suffixParam = getParam(arg, "suffix=");
if (suffixParam.length() > 0)
{
console.setOrigSuffix(suffixParam);
}
}
else if (isParamOption(arg, "exclude="))
{
string suffixParam = getParam(arg, "exclude=");
if (suffixParam.length() > 0)
console.updateExcludeVector(suffixParam);
}
else if (isOption(arg, "r", "R") || isOption(arg, "recursive"))
{
console.setIsRecursive(true);
}
else if (isOption(arg, "dry-run"))
{
console.setIsDryRun(true);
}
else if (isOption(arg, "Z", "preserve-date"))
{
console.setPreserveDate(true);
}
else if (isOption(arg, "v", "verbose"))
{
console.setIsVerbose(true);
}
else if (isOption(arg, "Q", "formatted"))
{
console.setIsFormattedOnly(true);
}
else if (isOption(arg, "q", "quiet"))
{
console.setIsQuiet(true);
}
else if (isOption(arg, "i", "ignore-exclude-errors"))
{
console.setIgnoreExcludeErrors(true);
}
else if (isOption(arg, "xi", "ignore-exclude-errors-x"))
{
console.setIgnoreExcludeErrorsAndDisplay(true);
}
else if (isOption(arg, "X", "errors-to-stdout"))
{
console.setErrorStream(&cout);
}
else if (isOption(arg, "lineend=windows"))
{
formatter.setLineEndFormat(LINEEND_WINDOWS);
}
else if (isOption(arg, "lineend=linux"))
{
formatter.setLineEndFormat(LINEEND_LINUX);
}
else if (isOption(arg, "lineend=macold"))
{
formatter.setLineEndFormat(LINEEND_MACOLD);
}
else if (isParamOption(arg, "z"))
{
int lineendType = 0;
string lineendParam = getParam(arg, "z");
if (lineendParam.length() > 0)
lineendType = atoi(lineendParam.c_str());
if (lineendType < 1 || lineendType > 3)
isOptionError(arg, errorInfo);
else if (lineendType == 1)
formatter.setLineEndFormat(LINEEND_WINDOWS);
else if (lineendType == 2)
formatter.setLineEndFormat(LINEEND_LINUX);
else if (lineendType == 3)
formatter.setLineEndFormat(LINEEND_MACOLD);
}
else
{
return false;
}
return true;
#endif
} // End of parseOptionContinued function
// Parse options from the option file.
void ASOptions::importOptions(stringstream& in, vector<string>& optionsVector)
{
char ch;
bool isInQuote = false;
char quoteChar = ' ';
string currentToken;
while (in)
{
currentToken = "";
do
{
in.get(ch);
if (in.eof())
break;
// treat '#' as line comments
if (ch == '#')
while (in)
{
in.get(ch);
if (ch == '\n' || ch == '\r')
break;
}
// break options on new-lines, tabs, commas, or spaces
// remove quotes from output
if (in.eof() || ch == '\n' || ch == '\r' || ch == '\t' || ch == ',')
break;
if (ch == ' ' && !isInQuote)
break;
if (ch == quoteChar && isInQuote)
break;
if (ch == '"' || ch == '\'')
{
isInQuote = true;
quoteChar = ch;
continue;
}
currentToken.append(1, ch);
}
while (in);
if (currentToken.length() != 0)
optionsVector.emplace_back(currentToken);
isInQuote = false;
}
}
string ASOptions::getOptionErrors() const
{
return optionErrors.str();
}
string ASOptions::getParam(const string& arg, const char* op)
{
return arg.substr(strlen(op));
}
string ASOptions::getParam(const string& arg, const char* op1, const char* op2)
{
return isParamOption(arg, op1) ? getParam(arg, op1) : getParam(arg, op2);
}
bool ASOptions::isOption(const string& arg, const char* op)
{
return arg == op;
}
bool ASOptions::isOption(const string& arg, const char* op1, const char* op2)
{
return (isOption(arg, op1) || isOption(arg, op2));
}
void ASOptions::isOptionError(const string& arg, const string& errorInfo)
{
if (optionErrors.str().length() == 0)
optionErrors << errorInfo << endl; // need main error message
optionErrors << "\t" << arg << endl;
}
bool ASOptions::isParamOption(const string& arg, const char* option)
{
bool retVal = arg.compare(0, strlen(option), option) == 0;
// if comparing for short option, 2nd char of arg must be numeric
if (retVal && strlen(option) == 1 && arg.length() > 1)
if (!isdigit((unsigned char) arg[1]))
retVal = false;
return retVal;
}
bool ASOptions::isParamOption(const string& arg, const char* option1, const char* option2)
{
return isParamOption(arg, option1) || isParamOption(arg, option2);
}
//----------------------------------------------------------------------------
// ASEncoding class
//----------------------------------------------------------------------------
// Return true if an int is big endian.
bool ASEncoding::getBigEndian() const
{
char16_t word = 0x0001;
char* byte = reinterpret_cast<char*>(&word);
return (byte[0] ? false : true);
}
// Swap the two low order bytes of a 16 bit integer value.
int ASEncoding::swap16bit(int value) const
{
return (((value & 0xff) << 8) | ((value & 0xff00) >> 8));
}
// Return the length of a utf-16 C string.
// The length is in number of char16_t.
size_t ASEncoding::utf16len(const utf16* utf16In) const
{
size_t length = 0;
while (*utf16In++ != '\0')
length++;
return length;
}
// Adapted from SciTE UniConversion.cxx.
// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
// Modified for Artistic Style by Jim Pattee.
// Compute the length of an output utf-8 file given a utf-16 file.
// Input inLen is the size in BYTES (not wchar_t).
size_t ASEncoding::utf8LengthFromUtf16(const char* utf16In, size_t inLen, bool isBigEndian) const
{
size_t len = 0;
size_t wcharLen = (inLen / 2) + (inLen % 2);
const char16_t* uptr = reinterpret_cast<const char16_t*>(utf16In);
for (size_t i = 0; i < wcharLen;)
{
size_t uch = isBigEndian ? swap16bit(uptr[i]) : uptr[i];
if (uch < 0x80)
len++;
else if (uch < 0x800)
len += 2;
else if ((uch >= SURROGATE_LEAD_FIRST) && (uch <= SURROGATE_LEAD_LAST))
{
len += 4;
i++;
}
else
len += 3;
i++;
}
return len;
}
// Adapted from SciTE Utf8_16.cxx.
// Copyright (C) 2002 Scott Kirkwood.
// Modified for Artistic Style by Jim Pattee.
// Convert a utf-8 file to utf-16.
size_t ASEncoding::utf8ToUtf16(char* utf8In, size_t inLen, bool isBigEndian, char* utf16Out) const
{
int nCur = 0;
ubyte* pRead = reinterpret_cast<ubyte*>(utf8In);
utf16* pCur = reinterpret_cast<utf16*>(utf16Out);
const ubyte* pEnd = pRead + inLen;
const utf16* pCurStart = pCur;
eState state = eStart;
// the BOM will automatically be converted to utf-16
while (pRead < pEnd)
{
switch (state)
{
case eStart:
if ((0xF0 & *pRead) == 0xF0)
{
nCur = (0x7 & *pRead) << 18;
state = eSecondOf4Bytes;
}
else if ((0xE0 & *pRead) == 0xE0)
{
nCur = (~0xE0 & *pRead) << 12;
state = ePenultimate;
}
else if ((0xC0 & *pRead) == 0xC0)
{
nCur = (~0xC0 & *pRead) << 6;
state = eFinal;
}
else
{
nCur = *pRead;
state = eStart;
}
break;
case eSecondOf4Bytes:
nCur |= (0x3F & *pRead) << 12;
state = ePenultimate;
break;
case ePenultimate:
nCur |= (0x3F & *pRead) << 6;
state = eFinal;
break;
case eFinal:
nCur |= (0x3F & *pRead);
state = eStart;
break;
// no default case is needed
}
++pRead;
if (state == eStart)
{
int codePoint = nCur;
if (codePoint >= SURROGATE_FIRST_VALUE)
{
codePoint -= SURROGATE_FIRST_VALUE;
int lead = (codePoint >> 10) + SURROGATE_LEAD_FIRST;
*pCur++ = static_cast<utf16>(isBigEndian ? swap16bit(lead) : lead);
int trail = (codePoint & 0x3ff) + SURROGATE_TRAIL_FIRST;
*pCur++ = static_cast<utf16>(isBigEndian ? swap16bit(trail) : trail);
}
else
*pCur++ = static_cast<utf16>(isBigEndian ? swap16bit(codePoint) : codePoint);
}
}
// return value is the output length in BYTES (not wchar_t)
return (pCur - pCurStart) * 2;
}
// Adapted from SciTE UniConversion.cxx.
// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
// Modified for Artistic Style by Jim Pattee.
// Compute the length of an output utf-16 file given a utf-8 file.
// Return value is the size in BYTES (not wchar_t).
size_t ASEncoding::utf16LengthFromUtf8(const char* utf8In, size_t len) const
{
size_t ulen = 0;
size_t charLen;
for (size_t i = 0; i < len;)
{
unsigned char ch = static_cast<unsigned char>(utf8In[i]);
if (ch < 0x80)
charLen = 1;
else if (ch < 0x80 + 0x40 + 0x20)
charLen = 2;
else if (ch < 0x80 + 0x40 + 0x20 + 0x10)
charLen = 3;
else
{
charLen = 4;
ulen++;
}
i += charLen;
ulen++;
}
// return value is the length in bytes (not wchar_t)
return ulen * 2;
}
// Adapted from SciTE Utf8_16.cxx.
// Copyright (C) 2002 Scott Kirkwood.
// Modified for Artistic Style by Jim Pattee.
// Convert a utf-16 file to utf-8.
size_t ASEncoding::utf16ToUtf8(char* utf16In, size_t inLen, bool isBigEndian,
bool firstBlock, char* utf8Out) const
{
int nCur16 = 0;
int nCur = 0;
ubyte* pRead = reinterpret_cast<ubyte*>(utf16In);
ubyte* pCur = reinterpret_cast<ubyte*>(utf8Out);
const ubyte* pEnd = pRead + inLen;
const ubyte* pCurStart = pCur;
static eState state = eStart; // state is retained for subsequent blocks
if (firstBlock)
state = eStart;
// the BOM will automatically be converted to utf-8
while (pRead < pEnd)
{
switch (state)
{
case eStart:
if (pRead >= pEnd)
{
++pRead;
break;
}
if (isBigEndian)
{
nCur16 = static_cast<utf16>(*pRead++ << 8);
nCur16 |= static_cast<utf16>(*pRead);
}
else
{
nCur16 = *pRead++;
nCur16 |= static_cast<utf16>(*pRead << 8);
}
if (nCur16 >= SURROGATE_LEAD_FIRST && nCur16 <= SURROGATE_LEAD_LAST)
{
++pRead;
int trail;
if (isBigEndian)
{
trail = static_cast<utf16>(*pRead++ << 8);
trail |= static_cast<utf16>(*pRead);
}
else
{
trail = *pRead++;
trail |= static_cast<utf16>(*pRead << 8);
}
nCur16 = (((nCur16 & 0x3ff) << 10) | (trail & 0x3ff)) + SURROGATE_FIRST_VALUE;
}
++pRead;
if (nCur16 < 0x80)
{
nCur = static_cast<ubyte>(nCur16 & 0xFF);
state = eStart;
}
else if (nCur16 < 0x800)
{
nCur = static_cast<ubyte>(0xC0 | (nCur16 >> 6));
state = eFinal;
}
else if (nCur16 < SURROGATE_FIRST_VALUE)
{
nCur = static_cast<ubyte>(0xE0 | (nCur16 >> 12));
state = ePenultimate;
}
else
{
nCur = static_cast<ubyte>(0xF0 | (nCur16 >> 18));
state = eSecondOf4Bytes;
}
break;
case eSecondOf4Bytes:
nCur = static_cast<ubyte>(0x80 | ((nCur16 >> 12) & 0x3F));
state = ePenultimate;
break;
case ePenultimate:
nCur = static_cast<ubyte>(0x80 | ((nCur16 >> 6) & 0x3F));
state = eFinal;
break;
case eFinal:
nCur = static_cast<ubyte>(0x80 | (nCur16 & 0x3F));
state = eStart;
break;
// no default case is needed
}
*pCur++ = static_cast<ubyte>(nCur);
}
return pCur - pCurStart;
}
//----------------------------------------------------------------------------
} // namespace astyle
//----------------------------------------------------------------------------
using namespace astyle;
//----------------------------------------------------------------------------
// ASTYLE_JNI functions for Java library builds
//----------------------------------------------------------------------------
#ifdef ASTYLE_JNI
// called by a java program to get the version number
// the function name is constructed from method names in the calling java program
extern "C" EXPORT
jstring STDCALL Java_AStyleInterface_AStyleGetVersion(JNIEnv* env, jclass)
{
return env->NewStringUTF(g_version);
}
// called by a java program to format the source code
// the function name is constructed from method names in the calling java program
extern "C" EXPORT
jstring STDCALL Java_AStyleInterface_AStyleMain(JNIEnv* env,
jobject obj,
jstring textInJava,
jstring optionsJava)
{
g_env = env; // make object available globally
g_obj = obj; // make object available globally
jstring textErr = env->NewStringUTF(""); // zero length text returned if an error occurs
// get the method ID
jclass cls = env->GetObjectClass(obj);
g_mid = env->GetMethodID(cls, "ErrorHandler", "(ILjava/lang/String;)V");
if (g_mid == nullptr)
{
cout << "Cannot find java method ErrorHandler" << endl;
return textErr;
}
// convert jstring to char*
const char* textIn = env->GetStringUTFChars(textInJava, nullptr);
const char* options = env->GetStringUTFChars(optionsJava, nullptr);
// call the C++ formatting function
char* textOut = AStyleMain(textIn, options, javaErrorHandler, javaMemoryAlloc);
// if an error message occurred it was displayed by errorHandler
if (textOut == nullptr)
return textErr;
// release memory
jstring textOutJava = env->NewStringUTF(textOut);
delete[] textOut;
env->ReleaseStringUTFChars(textInJava, textIn);
env->ReleaseStringUTFChars(optionsJava, options);
return textOutJava;
}
// Call the Java error handler
void STDCALL javaErrorHandler(int errorNumber, const char* errorMessage)
{
jstring errorMessageJava = g_env->NewStringUTF(errorMessage);
g_env->CallVoidMethod(g_obj, g_mid, errorNumber, errorMessageJava);
}
// Allocate memory for the formatted text
char* STDCALL javaMemoryAlloc(unsigned long memoryNeeded)
{
// error condition is checked after return from AStyleMain
char* buffer = new (nothrow) char[memoryNeeded];
return buffer;
}
#endif // ASTYLE_JNI
//----------------------------------------------------------------------------
// ASTYLE_LIB functions for library builds
//----------------------------------------------------------------------------
#ifdef ASTYLE_LIB
//----------------------------------------------------------------------------
// ASTYLE_LIB entry point for AStyleMainUtf16 library builds
//----------------------------------------------------------------------------
/*
* IMPORTANT Visual C DLL linker for WIN32 must have the additional options:
* /EXPORT:AStyleMain=_AStyleMain@16
* /EXPORT:AStyleMainUtf16=_AStyleMainUtf16@16
* /EXPORT:AStyleGetVersion=_AStyleGetVersion@0
* No /EXPORT is required for x64
*/
extern "C" EXPORT char16_t* STDCALL AStyleMainUtf16(const char16_t* pSourceIn, // the source to be formatted
const char16_t* pOptions, // AStyle options
fpError fpErrorHandler, // error handler function
fpAlloc fpMemoryAlloc) // memory allocation function
{
if (fpErrorHandler == nullptr) // cannot display a message if no error handler
return nullptr;
if (pSourceIn == nullptr)
{
fpErrorHandler(101, "No pointer to source input.");
return nullptr;
}
if (pOptions == nullptr)
{
fpErrorHandler(102, "No pointer to AStyle options.");
return nullptr;
}
if (fpMemoryAlloc == nullptr)
{
fpErrorHandler(103, "No pointer to memory allocation function.");
return nullptr;
}
#ifndef _WIN32
// check size of char16_t on Linux
int sizeCheck = 2;
if (sizeof(char16_t) != sizeCheck)
{
fpErrorHandler(104, "char16_t is not the correct size.");
return nullptr;
}
#endif
ASLibrary library;
char16_t* utf16Out = library.formatUtf16(pSourceIn, pOptions, fpErrorHandler, fpMemoryAlloc);
return utf16Out;
}
//----------------------------------------------------------------------------
// ASTYLE_LIB entry point for library builds
//----------------------------------------------------------------------------
/*
* IMPORTANT Visual C DLL linker for WIN32 must have the additional options:
* /EXPORT:AStyleMain=_AStyleMain@16
* /EXPORT:AStyleMainUtf16=_AStyleMainUtf16@16
* /EXPORT:AStyleGetVersion=_AStyleGetVersion@0
* No /EXPORT is required for x64
*/
extern "C" EXPORT char* STDCALL AStyleMain(const char* pSourceIn, // the source to be formatted
const char* pOptions, // AStyle options
fpError fpErrorHandler, // error handler function
fpAlloc fpMemoryAlloc) // memory allocation function
{
if (fpErrorHandler == nullptr) // cannot display a message if no error handler
return nullptr;
if (pSourceIn == nullptr)
{
fpErrorHandler(101, "No pointer to source input.");
return nullptr;
}
if (pOptions == nullptr)
{
fpErrorHandler(102, "No pointer to AStyle options.");
return nullptr;
}
if (fpMemoryAlloc == nullptr)
{
fpErrorHandler(103, "No pointer to memory allocation function.");
return nullptr;
}
ASFormatter formatter;
ASOptions options(formatter);
vector<string> optionsVector;
stringstream opt(pOptions);
options.importOptions(opt, optionsVector);
bool ok = options.parseOptions(optionsVector, "Invalid Artistic Style options:");
if (!ok)
fpErrorHandler(130, options.getOptionErrors().c_str());
stringstream in(pSourceIn);
ASStreamIterator<stringstream> streamIterator(&in);
ostringstream out;
formatter.init(&streamIterator);
while (formatter.hasMoreLines())
{
out << formatter.nextLine();
if (formatter.hasMoreLines())
out << streamIterator.getOutputEOL();
else
{
// this can happen if the file if missing a closing brace and break-blocks is requested
if (formatter.getIsLineReady())
{
out << streamIterator.getOutputEOL();
out << formatter.nextLine();
}
}
}
size_t textSizeOut = out.str().length();
char* pTextOut = fpMemoryAlloc((long) textSizeOut + 1); // call memory allocation function
if (pTextOut == nullptr)
{
fpErrorHandler(120, "Allocation failure on output.");
return nullptr;
}
strcpy(pTextOut, out.str().c_str());
#ifndef NDEBUG
// The checksum is an assert in the console build and ASFormatter.
// This error returns the incorrectly formatted file to the editor.
// This is done to allow the file to be saved for debugging purposes.
if (formatter.getChecksumDiff() != 0)
fpErrorHandler(220,
"Checksum error.\n"
"The incorrectly formatted file will be returned for debugging.");
#endif
return pTextOut;
}
extern "C" EXPORT const char* STDCALL AStyleGetVersion(void)
{
return g_version;
}
// ASTYLECON_LIB is defined to exclude "main" from the test programs
#elif !defined(ASTYLECON_LIB)
//----------------------------------------------------------------------------
// main function for ASConsole build
//----------------------------------------------------------------------------
int main(int argc, char** argv)
{
// create objects
ASFormatter formatter;
unique_ptr<ASConsole> console(new ASConsole(formatter));
// process command line and option files
// build the vectors fileNameVector, optionsVector, and fileOptionsVector
vector<string> argvOptions;
argvOptions = console->getArgvOptions(argc, argv);
console->processOptions(argvOptions);
// if no files have been given, use cin for input and cout for output
if (!console->fileNameVectorIsEmpty())
console->processFiles();
else
console->formatCinToCout();
return EXIT_SUCCESS;
}
#endif // ASTYLE_LIB