// astyle_main.cpp // Copyright (c) 2018 by Jim Pattee . // 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 #include #include // needed by some compilers #include #include #include // includes for recursive getFileNames() function #ifdef _WIN32 #undef UNICODE // use ASCII windows functions #include #else #include #include #include #include #ifdef __VMS #include #include #include #include #include #include #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 ASStreamIterator::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 ASStreamIterator::~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 int ASStreamIterator::getStreamLength() const { return static_cast(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 string ASStreamIterator::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 string ASStreamIterator::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 void ASStreamIterator::peekReset() { assert(peekStart != 0); inStream->clear(); inStream->seekg(peekStart); peekStart = 0; } // save the last input line after input has reached EOF template void ASStreamIterator::saveLastInputLine() { assert(inStream->eof()); prevBuffer = buffer; } // return position of the get pointer template streamoff ASStreamIterator::tellg() { return inStream->tellg(); } // check for a change in line ends template bool ASStreamIterator::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 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 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 ASConsole::getExcludeHitsVector() const { return excludeHitsVector; } // for unit testing vector ASConsole::getExcludeVector() const { return excludeVector; } // for unit testing vector ASConsole::getFileName() const { return fileName; } // for unit testing vector ASConsole::getFileNameVector() const { return fileNameVector; } // for unit testing vector 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 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 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 ASConsole::getArgvOptions(int argc, char** argv) { if (argc > 0) astyleExePath = getFullPathName(argv[0]); vector 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& 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(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(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& wildcards) { vector 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& wildcards) { struct dirent* entry; // entry from readdir() struct stat statbuf; // entry from stat() vector subDirectory; // sub directories of this directory // errno is defined in 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 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& 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(<); 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 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(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(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(utf16Out)) + 1) * sizeof(char16_t)); return reinterpret_cast(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(const_cast(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& optionsVector, const string& errorInfo) { vector::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& 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(&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 // 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(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(utf8In); utf16* pCur = reinterpret_cast(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(isBigEndian ? swap16bit(lead) : lead); int trail = (codePoint & 0x3ff) + SURROGATE_TRAIL_FIRST; *pCur++ = static_cast(isBigEndian ? swap16bit(trail) : trail); } else *pCur++ = static_cast(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 // 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(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(utf16In); ubyte* pCur = reinterpret_cast(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(*pRead++ << 8); nCur16 |= static_cast(*pRead); } else { nCur16 = *pRead++; nCur16 |= static_cast(*pRead << 8); } if (nCur16 >= SURROGATE_LEAD_FIRST && nCur16 <= SURROGATE_LEAD_LAST) { ++pRead; int trail; if (isBigEndian) { trail = static_cast(*pRead++ << 8); trail |= static_cast(*pRead); } else { trail = *pRead++; trail |= static_cast(*pRead << 8); } nCur16 = (((nCur16 & 0x3ff) << 10) | (trail & 0x3ff)) + SURROGATE_FIRST_VALUE; } ++pRead; if (nCur16 < 0x80) { nCur = static_cast(nCur16 & 0xFF); state = eStart; } else if (nCur16 < 0x800) { nCur = static_cast(0xC0 | (nCur16 >> 6)); state = eFinal; } else if (nCur16 < SURROGATE_FIRST_VALUE) { nCur = static_cast(0xE0 | (nCur16 >> 12)); state = ePenultimate; } else { nCur = static_cast(0xF0 | (nCur16 >> 18)); state = eSecondOf4Bytes; } break; case eSecondOf4Bytes: nCur = static_cast(0x80 | ((nCur16 >> 12) & 0x3F)); state = ePenultimate; break; case ePenultimate: nCur = static_cast(0x80 | ((nCur16 >> 6) & 0x3F)); state = eFinal; break; case eFinal: nCur = static_cast(0x80 | (nCur16 & 0x3F)); state = eStart; break; // no default case is needed } *pCur++ = static_cast(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 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 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 console(new ASConsole(formatter)); // process command line and option files // build the vectors fileNameVector, optionsVector, and fileOptionsVector vector 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