//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
// copyright            : (C) 2014 Eran Ifrah
// file name            : astyle_main.cpp
//
// -------------------------------------------------------------------------
// A
//              _____           _      _     _ _
//             /  __ \         | |    | |   (_) |
//             | /  \/ ___   __| | ___| |    _| |_ ___
//             | |    / _ \ / _  |/ _ \ |   | | __/ _ )
//             | \__/\ (_) | (_| |  __/ |___| | ||  __/
//              \____/\___/ \__,_|\___\_____/_|\__\___|
//
//                                                  F i l e
//
//    This program is free software; you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation; either version 2 of the License, or
//    (at your option) any later version.
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *   astyle_main.cpp
 *
 *   Copyright (C) 2006-2011 by Jim Pattee <jimp03@email.com>
 *   Copyright (C) 1998-2002 by Tal Davidson
 *   <http://www.gnu.org/licenses/lgpl-3.0.html>
 *
 *   This file is a part of Artistic Style - an indentation and
 *   reformatting tool for C, C++, C# and Java source files.
 *   <http://astyle.sourceforge.net>
 *
 *   Artistic Style is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   Artistic Style is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with Artistic Style.  If not, see <http://www.gnu.org/licenses/>.
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 */

#include "astyle_main.h"

#include <algorithm>
#include <iostream>
#include <fstream>
#include <sstream>
#include <cstdlib>
#include <errno.h>

// includes for recursive getFileNames() function
#ifdef _WIN32
#undef UNICODE // use ASCII windows functions
#include <windows.h>
#else
#include <dirent.h>
#include <sys/stat.h>
#ifdef __VMS
#include <unixlib.h>
#include <rms.h>
#include <ssdef.h>
#include <stsdef.h>
#include <lib$routines.h>
#include <starlet.h>
#endif /* __VMS */
#endif

#ifdef __DMC__
#include <locale.h>
#endif

// turn off MinGW automatic file globbing
// this CANNOT be in the astyle namespace
#ifndef ASTYLE_LIB
int _CRT_glob = 0;
#endif

namespace astyle
{

#ifdef _WIN32
char g_fileSeparator = '\\';
bool g_isCaseSensitive = false;
#else
char g_fileSeparator = '/';
bool g_isCaseSensitive = true;
#endif

// console build variables
#ifndef ASTYLE_LIB
ostream* _err = &cerr;       // direct error messages to cerr
ASConsole* g_console = NULL; // class to encapsulate console variables
#endif

#ifdef ASTYLE_JNI
// java library build variables
JNIEnv* g_env;
jobject g_obj;
jmethodID g_mid;
#endif

const char* g_version = "2.03 beta";

//-----------------------------------------------------------------------------
// ASStreamIterator class
// typename will be istringstream for GUI and istream otherwise
//-----------------------------------------------------------------------------

template <typename T> ASStreamIterator<T>::ASStreamIterator(T* in)
{
    inStream = in;
    buffer.reserve(200);
    eolWindows = 0;
    eolLinux = 0;
    eolMacOld = 0;
    outputEOL[0] = '\0';
    peekStart = 0;
    prevLineDeleted = false;
    checkForEmptyLine = false;
}

template <typename T> ASStreamIterator<T>::~ASStreamIterator() {}

/**
 * read the input stream, delete any end of line characters,
 *     and build a string that contains the input line.
 *
 * @return        string containing the next input line minus any end of line characters
 */
template <typename T> string ASStreamIterator<T>::nextLine(bool emptyLineWasDeleted)
{
    // verify that the current position is correct
    assert(peekStart == 0);

    // a deleted line may be replaced if break-blocks is requested
    // this sets up the compare to check for a replaced empty line
    if(prevLineDeleted) {
        prevLineDeleted = false;
        checkForEmptyLine = true;
    }
    if(!emptyLineWasDeleted)
        prevBuffer = buffer;
    else
        prevLineDeleted = true;

    // read the next record
    buffer.clear();
    char ch;
    inStream->get(ch);

    while(!inStream->eof() && ch != '\n' && ch != '\r') {
        buffer.append(1, ch);
        inStream->get(ch);
    }

    if(inStream->eof()) {
        return buffer;
    }

    int peekCh = inStream->peek();

    // find input end-of-line characters
    if(!inStream->eof()) {
        if(ch == '\r') // CR+LF is windows otherwise Mac OS 9
        {
            if(peekCh == '\n') {
                inStream->get();
                eolWindows++;
            } else
                eolMacOld++;
        } else // LF is Linux, allow for improbable LF/CR
        {
            if(peekCh == '\r') {
                inStream->get();
                eolWindows++;
            } else
                eolLinux++;
        }
    } else {
        inStream->clear();
    }

    // set output end of line characters
    if(eolWindows >= eolLinux) {
        if(eolWindows >= eolMacOld)
            strcpy(outputEOL, "\r\n"); // Windows (CR+LF)
        else
            strcpy(outputEOL, "\r"); // MacOld (CR)
    } else if(eolLinux >= eolMacOld)
        strcpy(outputEOL, "\n"); // Linux (LF)
    else
        strcpy(outputEOL, "\r"); // MacOld (CR)

    return buffer;
}

// save the current position and get the next line
// this can be called for multiple reads
// when finished peeking you MUST call peekReset()
// call this function from ASFormatter ONLY
template <typename T> string ASStreamIterator<T>::peekNextLine()
{
    assert(hasMoreLines());
    string nextLine_;
    char ch;

    if(peekStart == 0) 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) /////////////  changed  //////////
            inStream->get();
    }

    return nextLine_;
}

// reset current position and EOF for peekNextLine()
template <typename T> void ASStreamIterator<T>::peekReset()
{
    assert(peekStart != 0);
    inStream->clear();
    inStream->seekg(peekStart);
    peekStart = 0;
}

// save the last input line after input has reached EOF
template <typename T> void ASStreamIterator<T>::saveLastInputLine()
{
    assert(inStream->eof());
    prevBuffer = buffer;
}

// check for a change in line ends
template <typename T> bool ASStreamIterator<T>::getLineEndChange(int lineEndFormat) const
{
    assert(lineEndFormat == LINEEND_DEFAULT || lineEndFormat == LINEEND_WINDOWS || lineEndFormat == LINEEND_LINUX ||
           lineEndFormat == LINEEND_MACOLD);

    bool lineEndChange = false;
    if(lineEndFormat == LINEEND_WINDOWS)
        lineEndChange = (eolLinux + eolMacOld != 0);
    else if(lineEndFormat == LINEEND_LINUX)
        lineEndChange = (eolWindows + eolMacOld != 0);
    else if(lineEndFormat == LINEEND_MACOLD)
        lineEndChange = (eolWindows + eolLinux != 0);
    else {
        if(eolWindows > 0)
            lineEndChange = (eolLinux + eolMacOld != 0);
        else if(eolLinux > 0)
            lineEndChange = (eolWindows + eolMacOld != 0);
        else if(eolMacOld > 0)
            lineEndChange = (eolWindows + eolLinux != 0);
    }
    return lineEndChange;
}

#ifndef ASTYLE_LIB
//-----------------------------------------------------------------------------
// ASConsole class
// main function will be included only in the console build
//-----------------------------------------------------------------------------

// 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 ouput
    int inLength = 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;
                } else if(lineEnd == LINEEND_LF) {
                    outStr += inStr[pos + 1]; // Delete the CR
                    pos++;
                    continue;
                } else {
                    outStr += inStr[pos]; // Do not change
                    outStr += inStr[pos + 1];
                    pos++;
                    continue;
                }
            } else {
                // CR
                if(lineEnd == LINEEND_CRLF) {
                    outStr += inStr[pos]; // Insert the CR
                    outStr += '\n';       // Insert the LF
                    continue;
                } else if(lineEnd == LINEEND_LF) {
                    outStr += '\n'; // Insert the LF
                    continue;
                } else {
                    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;
            } else if(lineEnd == LINEEND_CR) {
                outStr += '\r'; // Insert the CR
                continue;
            } else {
                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(strcmp(outputEOL, "\r\n") == 0) lineEndFormat = LINEEND_WINDOWS;
    if(strcmp(outputEOL, "\n") == 0) lineEndFormat = LINEEND_LINUX;
    if(strcmp(outputEOL, "\r") == 0) 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 >= 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
{
    (*_err) << _("\nArtistic Style has terminated") << endl;
    exit(EXIT_FAILURE);
}

// error exit with a message
void ASConsole::error(const char* why, const char* what) const
{
    (*_err) << 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 like TextWrangler (Mac).
 * Do NOT display any console messages when this function is used.
 */
void ASConsole::formatCinToCout()
{
    verifyCinPeek();
#ifdef _WIN32
    // Assure that redirected cin is all Windows line ends.
    // TODO: The following can be removed when tellg() is removed.
    // Windows line ends must be used on the input. If not, a problem occurs
    // when tellg() statements are used. The tellg() will be out of sequence
    // with the get() statements. The following file conversion could be
    // eliminated if the tellg() statements were removed.
    istream* streamIn = &cin;
    ostringstream out;
    char buf[65536]; // 64 KB
    while(!streamIn->eof()) {
        streamIn->read(buf, sizeof(buf) - 1);
        buf[streamIn->gcount()] = '\0';
        out << buf;
    }
    convertLineEnds(out, LINEEND_WINDOWS);
    stringstream in(out.str().c_str());
    ASStreamIterator<stringstream> streamIterator(&in); // create iterator for converted input

    // Must specify LF line ends for Windows. This will output CRLF on the terminal.
    // Specifying CRLF line ends will output CRCRLF (2 CRs) on the terminal.
    // There is no way I know to change the line ends on Windows redirection.
    LineEndFormat lineEndFormat = LINEEND_LF;
#else
    // Linux can handle any line end.
    ASStreamIterator<istream> streamIterator(&cin); // create iterator for cin
    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 bracket and break-blocks is requested
            if(formatter.getIsLineReady()) {
                setOutputEOL(lineEndFormat, streamIterator.getOutputEOL());
                cout << outputEOL;
                cout << formatter.nextLine();
            }
        }
    }
    cout.flush();
}

/**
 * Verify that tellg() works with cin.
 * This will fail if cin is piped to AStyle "cat txt.cpp | ./astyled".
 * But is OK for redirection "./astyled < txt.cpp".
 */
void ASConsole::verifyCinPeek() const
{
    int currPos = static_cast<int>(cin.tellg());
    if(currPos == -1) {
        (*_err) << _("Cannot process the input stream") << endl;
        error();
    }
}

/**
 * Open input file, format it, and close the output.
 *
 * @param fileName_     The path and name of the file to be processed.
 */
void ASConsole::formatFile(const string& fileName_)
{
    stringstream in;
    ostringstream out;
    FileEncoding encoding = readFile(fileName_, in);

    // Unless a specific language mode has been set, set the language mode
    // according to the file's suffix.
    if(!formatter.getModeManuallySet()) {
        if(stringEndsWith(fileName_, string(".java")))
            formatter.setJavaStyle();
        else if(stringEndsWith(fileName_, string(".cs")))
            formatter.setSharpStyle();
        else
            formatter.setCStyle();
    }

    // set line end format
    string nextLine;          // next output line
    filesAreIdentical = true; // input and output files are identical
    LineEndFormat lineEndFormat = formatter.getLineEndFormat();
    initializeOutputEOL(lineEndFormat);
    // do this AFTER setting the file mode
    ASStreamIterator<stringstream> streamIterator(&in);
    formatter.init(&streamIterator);

    // format the file
    while(formatter.hasMoreLines()) {
        nextLine = formatter.nextLine();
        out << nextLine;
        linesOut++;
        if(formatter.hasMoreLines()) {
            setOutputEOL(lineEndFormat, streamIterator.getOutputEOL());
            out << outputEOL;
        } else {
            streamIterator.saveLastInputLine(); // to compare the last input line
            // this can happen if the file if missing a closing bracket 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)) {
        writeFile(fileName_, encoding, out);
        printMsg(_("Formatted  %s\n"), displayName);
        filesFormatted++;
    } else {
        if(!isFormattedOnly) printMsg(_("Unchanged  %s\n"), displayName);
        filesUnchanged++;
    }

    assert(formatter.getChecksumDiff() == 0);
}

// build a vector of argv options
// the program path argv[0] is excluded
vector<string> ASConsole::getArgvOptions(int argc, char** argv) const
{
    vector<string> argvOptions;
    for(int i = 1; i < argc; i++) {
        argvOptions.push_back(string(argv[i]));
    }
    return argvOptions;
}

// for unit testing
vector<bool> ASConsole::getExcludeHitsVector() { return excludeHitsVector; }

// for unit testing
vector<string> ASConsole::getExcludeVector() { return excludeVector; }

// for unit testing
vector<string> ASConsole::getFileName() { return fileName; }

// for unit testing
vector<string> ASConsole::getFileNameVector() { return fileNameVector; }

// for unit testing
vector<string> ASConsole::getFileOptionsVector() { return fileOptionsVector; }

int ASConsole::getFilesUnchanged() { return filesUnchanged; }

int ASConsole::getFilesFormatted() { return filesFormatted; }

bool ASConsole::getIgnoreExcludeErrors() { return ignoreExcludeErrors; }

bool ASConsole::getIgnoreExcludeErrorsDisplay() { return ignoreExcludeErrorsDisplay; }

bool ASConsole::getIsFormattedOnly() { return isFormattedOnly; }

string ASConsole::getLanguageID() const { return localizer.getLanguageID(); }

bool ASConsole::getIsQuiet() { return isQuiet; }

bool ASConsole::getIsRecursive() { return isRecursive; }

bool ASConsole::getIsVerbose() { return isVerbose; }

bool ASConsole::getLineEndsMixed() { return lineEndsMixed; }

bool ASConsole::getNoBackup() { return noBackup; }

string ASConsole::getOptionsFileName() { return optionsFileName; }

bool ASConsole::getOptionsFileRequired() { return optionsFileRequired; }

// for unit testing
vector<string> ASConsole::getOptionsVector() { return optionsVector; }

string ASConsole::getOrigSuffix() { return origSuffix; }

string ASConsole::getParam(const string& arg, const char* op) { return arg.substr(strlen(op)); }

bool ASConsole::getPreserveDate() { return preserveDate; }

// initialize output end of line
void ASConsole::initializeOutputEOL(LineEndFormat lineEndFormat)
{
    assert(lineEndFormat == LINEEND_DEFAULT || lineEndFormat == LINEEND_WINDOWS || lineEndFormat == LINEEND_LINUX ||
           lineEndFormat == LINEEND_MACOLD);

    outputEOL[0] = '\0';   // current line end
    prevEOL[0] = '\0';     // previous line end
    lineEndsMixed = false; // output has mixed line ends, LINEEND_DEFAULT only

    if(lineEndFormat == LINEEND_WINDOWS)
        strcpy(outputEOL, "\r\n");
    else if(lineEndFormat == LINEEND_LINUX)
        strcpy(outputEOL, "\n");
    else if(lineEndFormat == LINEEND_MACOLD)
        strcpy(outputEOL, "\r");
    else
        outputEOL[0] = '\0';
}

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 input file", fileName_.c_str());
    char* data = new (nothrow) char[blockSize];
    if(!data) error("Cannot allocate memory for input file", fileName_.c_str());
    fin.read(data, sizeof(data));
    if(fin.bad()) error("Cannot read input file", fileName_.c_str());
    size_t dataSize = static_cast<size_t>(fin.gcount());
    FileEncoding encoding = detectEncoding(data, dataSize);
    if(encoding == UTF_32BE || encoding == UTF_32LE) error(_("Cannot process UTF-32 encoding"), fileName_.c_str());
    bool firstBlock = true;
    while(dataSize) {
        if(encoding == UTF_16LE || encoding == UTF_16BE) {
            // convert utf-16 to utf-8
            size_t utf8Size = Utf8Length(data, dataSize, encoding);
            char* utf8Out = new (nothrow) char[utf8Size];
            if(!utf8Out) error("Cannot allocate memory for utf-8 conversion", fileName_.c_str());
            size_t utf8Len = Utf16ToUtf8(data, dataSize, encoding, firstBlock, utf8Out);
            assert(utf8Len == utf8Size);
            in << string(utf8Out, utf8Len);
            delete[] utf8Out;
        } else
            in << string(data, dataSize);
        fin.read(data, sizeof(data));
        if(fin.bad()) error("Cannot read input file", fileName_.c_str());
        dataSize = static_cast<size_t>(fin.gcount());
        firstBlock = false;
    }
    fin.close();
    delete[] data;
    return encoding;
}

void ASConsole::setIgnoreExcludeErrors(bool state) { ignoreExcludeErrors = state; }

void ASConsole::setIgnoreExcludeErrorsAndDisplay(bool state)
{
    ignoreExcludeErrors = state;
    ignoreExcludeErrorsDisplay = state;
}

void ASConsole::setIsFormattedOnly(bool state) { isFormattedOnly = state; }

void ASConsole::setIsQuiet(bool state) { isQuiet = state; }

void ASConsole::setIsRecursive(bool state) { isRecursive = state; }

void ASConsole::setIsVerbose(bool state) { isVerbose = state; }

void ASConsole::setNoBackup(bool state) { noBackup = state; }

void ASConsole::setOptionsFileName(string name) { optionsFileName = name; }

void ASConsole::setOptionsFileRequired(bool state) { optionsFileRequired = state; }

void ASConsole::setOrigSuffix(string suffix) { origSuffix = suffix; }

void ASConsole::setPreserveDate(bool state) { preserveDate = state; }

// set outputEOL variable
void ASConsole::setOutputEOL(LineEndFormat lineEndFormat, const char* currentEOL)
{
    if(lineEndFormat == LINEEND_DEFAULT) {
        strcpy(outputEOL, currentEOL);
        if(strlen(prevEOL) == 0) strcpy(prevEOL, outputEOL);
        if(strcmp(prevEOL, outputEOL) != 0) {
            lineEndsMixed = true;
            filesAreIdentical = false;
            strcpy(prevEOL, outputEOL);
        }
    } else {
        strcpy(prevEOL, currentEOL);
        if(strcmp(prevEOL, outputEOL) != 0) 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,
                  NULL,
                  lastError,
                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
                  (LPSTR)&msgBuf,
                  0,
                  NULL);
    // Display the string.
    (*_err) << "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 wildcard      The wildcard to be processed (e.g. *.cpp).
 */
void ASConsole::getFileNames(const string& directory, const string& wildcard)
{
    vector<string> subDirectory;  // sub directories of directory
    WIN32_FIND_DATA findFileData; // for FindFirstFile and FindNextFile

    // Find the first file in the directory
    // Find will get at least "." and "..".
    string firstFile = directory + "\\*";
    HANDLE hFind = FindFirstFile(firstFile.c_str(), &findFileData);

    if(hFind == INVALID_HANDLE_VALUE) {
        // Error (3) The system cannot find the path specified.
        // Error (123) The filename, directory name, or volume label syntax is incorrect.
        // ::FindClose(hFind); before exiting
        displayLastError();
        error(_("Cannot open directory"), directory.c_str());
    }

    // save files and sub directories
    do {
        // skip hidden or read only
        if(findFileData.cFileName[0] == '.' || (findFileData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ||
           (findFileData.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
            continue;

        // is this a sub directory
        if(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            if(!isRecursive) continue;
            // if a sub directory and recursive, save sub directory
            string subDirectoryPath = directory + g_fileSeparator + findFileData.cFileName;
            if(isPathExclued(subDirectoryPath))
                printMsg(_("Exclude  %s\n"), subDirectoryPath.substr(mainDirectoryLength));
            else
                subDirectory.push_back(subDirectoryPath);
            continue;
        }

        // save the file name
        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
        if(wildcmp(wildcard.c_str(), findFileData.cFileName)) {
            if(isExcluded)
                printMsg(_("Exclude  %s\n"), filePathName.substr(mainDirectoryLength));
            else
                fileName.push_back(filePathName);
        }
    } 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(unsigned i = 0; i < subDirectory.size(); i++)
        getFileNames(subDirectory[i], wildcard);

    return;
}

/**
 * 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(), NULL, NULL, 0);
    char* outBuf = new (nothrow) char[outSize];
    if(outBuf == NULL) return number;
    ::GetNumberFormat(lcid, 0, number.c_str(), NULL, outBuf, outSize);
    string formattedNum(outBuf);
    delete[] outBuf;
    // remove the decimal
    int decSize = ::GetLocaleInfo(lcid, LOCALE_SDECIMAL, NULL, 0);
    char* decBuf = new (nothrow) char[decSize];
    if(decBuf == NULL) 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;
}

#else // not _WIN32

/**
 * 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
{
    char* currdir = getenv("PWD");
    if(currdir == NULL) 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 wildcard      The wildcard to be processed (e.g. *.cpp).
 */
void ASConsole::getFileNames(const string& directory, const string& wildcard)
{
    struct dirent* entry;        // entry from readdir()
    struct stat statbuf;         // entry from stat()
    vector<string> subDirectory; // sub directories of this directory

    // errno is defined in <errno.h> and is set for errors in opendir, readdir, or stat
    errno = 0;

    DIR* dp = opendir(directory.c_str());
    if(dp == NULL) 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)) != NULL) {
        // 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.push_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
            if(wildcmp(wildcard.c_str(), entry->d_name)) {
                if(isExcluded)
                    printMsg(_("Exclude  %s\n"), entryFilepath.substr(mainDirectoryLength));
                else
                    fileName.push_back(entryFilepath);
            }
        }
    }

    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[firstEntry], &fileName[fileName.size()]);

    // 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], wildcard);
    }

    return;
}

/**
 * LINUX function to get locale information and call getNumberFormat.
 * This formats positive integers only, no float.
 *
 * @param num		The number to be formatted.
 * @param			For compatability with the Windows function.
 * @return			The formatted number.
 */
string ASConsole::getNumberFormat(int num, size_t) 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;
}

#endif // _WIN32

// get individual file names from the command-line file path
void ASConsole::getFilePaths(string& filePath)
{
    fileName.clear();
    targetDirectory = string();
    targetFilename = string();

    // 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;

    // clear exclude hits vector
    for(size_t ix = 0; ix < excludeHitsVector.size(); ix++)
        excludeHitsVector[ix] = false;

    // 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();
    }

    // display directory name for wildcard processing
    if(hasWildcard) {
        printSeparatingLine();
        printMsg(_("Directory  %s\n"), targetDirectory + g_fileSeparator + targetFilename);
    }

    // create a vector of paths and file names to process
    if(hasWildcard || isRecursive)
        getFileNames(targetDirectory, targetFilename);
    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.push_back(entryFilepath);
    }

    // check for unprocessed excludes
    bool excludeErr = false;
    for(size_t ix = 0; ix < excludeHitsVector.size(); ix++) {
        if(excludeHitsVector[ix] == false) {
            excludeErr = true;
            if(!ignoreExcludeErrorsDisplay) {
                if(ignoreExcludeErrors)
                    printMsg(_("Exclude (unmatched)  %s\n"), excludeVector[ix].c_str());
                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();
}

bool ASConsole::fileNameVectorIsEmpty() { return fileNameVector.empty(); }

bool ASConsole::isOption(const string& arg, const char* op) { return arg.compare(op) == 0; }

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
{
    (*_err) << endl;
    (*_err) << "                            Artistic Style " << g_version << endl;
    (*_err) << "                         Maintained by: Jim Pattee\n";
    (*_err) << "                       Original Author: Tal Davidson\n";
    (*_err) << endl;
    (*_err) << "Usage  :  astyle [options] Source1.cpp Source2.cpp  [...]\n";
    (*_err) << "          astyle [options] < Original > Beautified\n";
    (*_err) << endl;
    (*_err) << "When indenting a specific file, the resulting indented file RETAINS the\n";
    (*_err) << "original file-name. The original pre-indented file is renamed, with a\n";
    (*_err) << "suffix of \".orig\" added to the original filename.\n";
    (*_err) << endl;
    (*_err) << "Wildcards (* and ?) may be used in the filename.\n";
    (*_err) << "A \'recursive\' option can process directories recursively.\n";
    (*_err) << endl;
    (*_err) << "By default, astyle is set up to indent C/C++/C#/Java files, with four\n";
    (*_err) << "spaces per indent, a maximal indentation of 40 spaces inside continuous\n";
    (*_err) << "statements, a minimum indentation of eight spaces inside conditional\n";
    (*_err) << "statements, and NO formatting options.\n";
    (*_err) << endl;
    (*_err) << "Option's Format:\n";
    (*_err) << "----------------\n";
    (*_err) << "    Long options (starting with '--') must be written one at a time.\n";
    (*_err) << "    Short options (starting with '-') may be appended together.\n";
    (*_err) << "    Thus, -bps4 is the same as -b -p -s4.\n";
    (*_err) << endl;
    (*_err) << "Default options file:\n";
    (*_err) << "---------------------\n";
    (*_err) << "    Artistic Style looks for a default options file in the\n";
    (*_err) << "    following order:\n";
    (*_err) << "    1. The contents of the ARTISTIC_STYLE_OPTIONS environment\n";
    (*_err) << "       variable if it exists.\n";
    (*_err) << "    2. The file called .astylerc in the directory pointed to by the\n";
    (*_err) << "       HOME environment variable ( i.e. $HOME/.astylerc ).\n";
    (*_err) << "    3. The file called astylerc in the directory pointed to by the\n";
    (*_err) << "       USERPROFILE environment variable ( i.e. %USERPROFILE%\\astylerc ).\n";
    (*_err) << "    If a default options file is found, the options in this file\n";
    (*_err) << "    will be parsed BEFORE the command-line options.\n";
    (*_err) << "    Long options within the default option file may be written without\n";
    (*_err) << "    the preliminary '--'.\n";
    (*_err) << endl;
    (*_err) << "Bracket Style Options:\n";
    (*_err) << "----------------------\n";
    (*_err) << "    --style=allman  OR  --style=ansi  OR  --style=bsd\n";
    (*_err) << "	    OR  --style=break  OR  -A1\n";
    (*_err) << "    Allman style formatting/indenting.\n";
    (*_err) << "    Broken brackets.\n";
    (*_err) << endl;
    (*_err) << "    --style=java  OR  --style=attach  OR  -A2\n";
    (*_err) << "    Java style formatting/indenting.\n";
    (*_err) << "    Attached brackets.\n";
    (*_err) << endl;
    (*_err) << "    --style=kr  OR  --style=k&r  OR  --style=k/r  OR  -A3\n";
    (*_err) << "    Kernighan & Ritchie style formatting/indenting.\n";
    (*_err) << "    Linux brackets.\n";
    (*_err) << endl;
    (*_err) << "    --style=stroustrup  OR  -A4\n";
    (*_err) << "    Stroustrup style formatting/indenting.\n";
    (*_err) << "    Stroustrup brackets.\n";
    (*_err) << endl;
    (*_err) << "    --style=whitesmith  OR  -A5\n";
    (*_err) << "    Whitesmith style formatting/indenting.\n";
    (*_err) << "    Broken, indented brackets.\n";
    (*_err) << "    Indented class blocks and switch blocks.\n";
    (*_err) << endl;
    (*_err) << "    --style=banner  OR  -A6\n";
    (*_err) << "    Banner style formatting/indenting.\n";
    (*_err) << "    Attached, indented brackets.\n";
    (*_err) << "    Indented class blocks and switch blocks.\n";
    (*_err) << endl;
    (*_err) << "    --style=gnu  OR  -A7\n";
    (*_err) << "    GNU style formatting/indenting.\n";
    (*_err) << "    Broken brackets, indented blocks.\n";
    (*_err) << endl;
    (*_err) << "    --style=linux  OR  -A8\n";
    (*_err) << "    Linux style formatting/indenting.\n";
    (*_err) << "    Linux brackets, minimum conditional indent is one-half indent.\n";
    (*_err) << endl;
    (*_err) << "    --style=horstmann  OR  -A9\n";
    (*_err) << "    Horstmann style formatting/indenting.\n";
    (*_err) << "    Run-in brackets, indented switches.\n";
    (*_err) << endl;
    (*_err) << "    --style=1tbs  OR  --style=otbs  OR  -A10\n";
    (*_err) << "    One True Brace Style formatting/indenting.\n";
    (*_err) << "    Linux brackets, add brackets to all conditionals.\n";
    (*_err) << endl;
    (*_err) << "    --style=pico  OR  -A11\n";
    (*_err) << "    Pico style formatting/indenting.\n";
    (*_err) << "    Run-in opening brackets and attached closing brackets.\n";
    (*_err) << "    Uses keep one line blocks and keep one line statements.\n";
    (*_err) << endl;
    (*_err) << "    --style=lisp  OR  -A12\n";
    (*_err) << "    Lisp style formatting/indenting.\n";
    (*_err) << "    Attached opening brackets and attached closing brackets.\n";
    (*_err) << "    Uses keep one line statements.\n";
    (*_err) << endl;
    (*_err) << "Tab Options:\n";
    (*_err) << "------------\n";
    (*_err) << "    default indent option\n";
    (*_err) << "    If no indentation option is set, the default\n";
    (*_err) << "    option of 4 spaces per indent will be used.\n";
    (*_err) << endl;
    (*_err) << "    --indent=spaces=#  OR  -s#\n";
    (*_err) << "    Indent using # spaces per indent. Not specifying #\n";
    (*_err) << "    will result in a default of 4 spaces per indent.\n";
    (*_err) << endl;
    (*_err) << "    --indent=tab  OR  --indent=tab=#  OR  -t  OR  -t#\n";
    (*_err) << "    Indent using tab characters, assuming that each\n";
    (*_err) << "    indent is # spaces long. Not specifying # will result\n";
    (*_err) << "    in a default assumption of 4 spaces per indent.\n";
    (*_err) << endl;
    (*_err) << "    --indent=force-tab=#  OR  -T#\n";
    (*_err) << "    Indent using tab characters, assuming that each\n";
    (*_err) << "    indent is # spaces long. Force tabs to be used in areas\n";
    (*_err) << "    AStyle would prefer to use spaces.\n";
    (*_err) << endl;
    (*_err) << "    --indent=force-tab-x=#  OR  -xT#\n";
    (*_err) << "    Allows the tab length to be set to a length that is different\n";
    (*_err) << "    from the indent length. This may cause the indentation to be\n";
    (*_err) << "    a mix of both spaces and tabs. This option sets the tab length.\n";
    (*_err) << endl;
    (*_err) << "Old Bracket Options (depreciated):\n";
    (*_err) << "----------------------------------\n";
    (*_err) << "The following bracket options have been depreciated and\n";
    (*_err) << "will be removed in a future release.\n";
    (*_err) << "Use the above Bracket Style Options instead.\n";
    (*_err) << endl;
    (*_err) << "    --brackets=break  OR  -b  (depreciated)\n";
    (*_err) << "    Break brackets from pre-block code (i.e. ANSI C/C++ style).\n";
    (*_err) << endl;
    (*_err) << "    --brackets=attach  OR  -a  (depreciated)\n";
    (*_err) << "    Attach brackets to pre-block code (i.e. Java/K&R style).\n";
    (*_err) << endl;
    (*_err) << "    --brackets=linux  OR  -l  (depreciated)\n";
    (*_err) << "    Break definition-block brackets and attach command-block\n";
    (*_err) << "    brackets.\n";
    (*_err) << endl;
    (*_err) << "    --brackets=stroustrup  OR  -u  (depreciated)\n";
    (*_err) << "    Attach all brackets except function definition brackets.\n";
    (*_err) << endl;
    (*_err) << "    --brackets=run-in  OR  -g  (depreciated)\n";
    (*_err) << "    Break brackets from pre-block code, but allow following\n";
    (*_err) << "    run-in statements on the same line as an opening bracket.\n";
    (*_err) << endl;
    (*_err) << "    --brackets=horstmann\n";
    (*_err) << "    THIS IS NO LONGER A VALID OPTION.\n";
    (*_err) << "    Use style=horstmann instead.\n";
    (*_err) << endl;
    (*_err) << "Indentation options:\n";
    (*_err) << "--------------------\n";
    (*_err) << "    --indent-classes  OR  -C\n";
    (*_err) << "    Indent 'class' blocks, so that the inner 'public:',\n";
    (*_err) << "    'protected:' and 'private: headers are indented in\n";
    (*_err) << "    relation to the class block.\n";
    (*_err) << endl;
    (*_err) << "    --indent-switches  OR  -S\n";
    (*_err) << "    Indent 'switch' blocks, so that the inner 'case XXX:'\n";
    (*_err) << "    headers are indented in relation to the switch block.\n";
    (*_err) << endl;
    (*_err) << "    --indent-cases  OR  -K\n";
    (*_err) << "    Indent case blocks from the 'case XXX:' headers.\n";
    (*_err) << "    Case statements not enclosed in blocks are NOT indented.\n";
    (*_err) << endl;
    (*_err) << "    --indent-brackets  OR  -B\n";
    (*_err) << "    THIS IS NO LONGER A VALID OPTION.\n";
    (*_err) << "    Use style=whitesmith or style=banner instead.\n";
    (*_err) << endl;
    (*_err) << "    --indent-blocks  OR  -G\n";
    (*_err) << "    THIS IS NO LONGER A VALID OPTION.\n";
    (*_err) << "    Use style=gnu instead.\n";
    (*_err) << endl;
    (*_err) << "    --indent-namespaces  OR  -N\n";
    (*_err) << "    Indent the contents of namespace blocks.\n";
    (*_err) << endl;
    (*_err) << "    --indent-labels  OR  -L\n";
    (*_err) << "    Indent labels so that they appear one indent less than\n";
    (*_err) << "    the current indentation level, rather than being\n";
    (*_err) << "    flushed completely to the left (which is the default).\n";
    (*_err) << endl;
    (*_err) << "    --indent-preprocessor  OR  -w\n";
    (*_err) << "    Indent multi-line #define statements.\n";
    (*_err) << endl;
    (*_err) << "    --indent-col1-comments  OR  -Y\n";
    (*_err) << "    Indent line comments that start in column one.\n";
    (*_err) << endl;
    (*_err) << "    --min-conditional-indent=#  OR  -m#\n";
    (*_err) << "    Indent a minimal # spaces in a continuous conditional\n";
    (*_err) << "    belonging to a conditional header.\n";
    (*_err) << "    The valid values are:\n";
    (*_err) << "    0 - no minimal indent.\n";
    (*_err) << "    1 - indent at least one additional indent.\n";
    (*_err) << "    2 - indent at least two additional indents.\n";
    (*_err) << "    3 - indent at least one-half an additional indent.\n";
    (*_err) << "    The default value is 2, two additional indents.\n";
    (*_err) << endl;
    (*_err) << "    --max-instatement-indent=#  OR  -M#\n";
    (*_err) << "    Indent a maximal # spaces in a continuous statement,\n";
    (*_err) << "    relative to the previous line.\n";
    (*_err) << "    The valid values are 40 thru 120.\n";
    (*_err) << "    The default value is 40.\n";
    (*_err) << endl;
    (*_err) << "Padding options:\n";
    (*_err) << "--------------------\n";
    (*_err) << "    --break-blocks  OR  -f\n";
    (*_err) << "    Insert empty lines around unrelated blocks, labels, classes, ...\n";
    (*_err) << endl;
    (*_err) << "    --break-blocks=all  OR  -F\n";
    (*_err) << "    Like --break-blocks, except also insert empty lines \n";
    (*_err) << "    around closing headers (e.g. 'else', 'catch', ...).\n";
    (*_err) << endl;
    (*_err) << "    --pad-oper  OR  -p\n";
    (*_err) << "    Insert space padding around operators.\n";
    (*_err) << endl;
    (*_err) << "    --pad-paren  OR  -P\n";
    (*_err) << "    Insert space padding around parenthesis on both the outside\n";
    (*_err) << "    and the inside.\n";
    (*_err) << endl;
    (*_err) << "    --pad-paren-out  OR  -d\n";
    (*_err) << "    Insert space padding around parenthesis on the outside only.\n";
    (*_err) << endl;
    (*_err) << "    --pad-paren-in  OR  -D\n";
    (*_err) << "    Insert space padding around parenthesis on the inside only.\n";
    (*_err) << endl;
    (*_err) << "    --pad-header  OR  -H\n";
    (*_err) << "    Insert space padding after paren headers (e.g. 'if', 'for'...).\n";
    (*_err) << endl;
    (*_err) << "    --unpad-paren  OR  -U\n";
    (*_err) << "    Remove unnecessary space padding around parenthesis. This\n";
    (*_err) << "    can be used in combination with the 'pad' options above.\n";
    (*_err) << endl;
    (*_err) << "    --delete-empty-lines  OR  -xd\n";
    (*_err) << "    Delete empty lines within a function or method.\n";
    (*_err) << "    It will NOT delete lines added by the break-blocks options.\n";
    (*_err) << endl;
    (*_err) << "    --fill-empty-lines  OR  -E\n";
    (*_err) << "    Fill empty lines with the white space of their\n";
    (*_err) << "    previous lines.\n";
    (*_err) << endl;
    (*_err) << "    --align-pointer=type    OR  -k1\n";
    (*_err) << "    --align-pointer=middle  OR  -k2\n";
    (*_err) << "    --align-pointer=name    OR  -k3\n";
    (*_err) << "    Attach a pointer or reference operator (* or &) to either\n";
    (*_err) << "    the operator type (left), middle, or operator name (right).\n";
    (*_err) << "    To align the reference separately use --align-reference.\n";
    (*_err) << endl;
    (*_err) << "    --align-reference=none    OR  -W0\n";
    (*_err) << "    --align-reference=type    OR  -W1\n";
    (*_err) << "    --align-reference=middle  OR  -W2\n";
    (*_err) << "    --align-reference=name    OR  -W3\n";
    (*_err) << "    Attach a reference operator (&) to either\n";
    (*_err) << "    the operator type (left), middle, or operator name (right).\n";
    (*_err) << "    If not set, follow pointer alignment.\n";
    (*_err) << endl;
    (*_err) << "Formatting options:\n";
    (*_err) << "-------------------\n";
    (*_err) << "    --break-closing-brackets  OR  -y\n";
    (*_err) << "    Break brackets before closing headers (e.g. 'else', 'catch', ...).\n";
    (*_err) << "    Use with --brackets=attach, --brackets=linux, \n";
    (*_err) << "    or --brackets=stroustrup.\n";
    (*_err) << endl;
    (*_err) << "    --break-elseifs  OR  -e\n";
    (*_err) << "    Break 'else if()' statements into two different lines.\n";
    (*_err) << endl;
    (*_err) << "    --add-brackets  OR  -j\n";
    (*_err) << "    Add brackets to unbracketed one line conditional statements.\n";
    (*_err) << endl;
    (*_err) << "    --add-one-line-brackets  OR  -J\n";
    (*_err) << "    Add one line brackets to unbracketed one line conditional\n";
    (*_err) << "    statements.\n";
    (*_err) << endl;
    (*_err) << "    --keep-one-line-blocks  OR  -O\n";
    (*_err) << "    Don't break blocks residing completely on one line.\n";
    (*_err) << endl;
    (*_err) << "    --keep-one-line-statements  OR  -o\n";
    (*_err) << "    Don't break lines containing multiple statements into\n";
    (*_err) << "    multiple single-statement lines.\n";
    (*_err) << endl;
    (*_err) << "    --convert-tabs  OR  -c\n";
    (*_err) << "    Convert tabs to the appropriate number of spaces.\n";
    (*_err) << endl;
    (*_err) << "    --close-templates  OR  -xy\n";
    (*_err) << "    Close ending angle brackets on template definitions.\n";
    (*_err) << endl;
    (*_err) << "    --max-code-length=#    OR  -xC#\n";
    (*_err) << "    --break-after-logical  OR  -xL\n";
    (*_err) << "    max-code-length=# will break the line if it exceeds more than\n";
    (*_err) << "    # characters. The valid values are 50 thru 200.\n";
    (*_err) << "    If the line contains logical conditionals they will be placed\n";
    (*_err) << "    first on the new line. The option break-after-logical will\n";
    (*_err) << "    cause the logical conditional to be placed last on the\n";
    (*_err) << "    previous line.\n";
    (*_err) << endl;
    (*_err) << "    --mode=c\n";
    (*_err) << "    Indent a C or C++ source file (this is the default).\n";
    (*_err) << endl;
    (*_err) << "    --mode=java\n";
    (*_err) << "    Indent a Java source file.\n";
    (*_err) << endl;
    (*_err) << "    --mode=cs\n";
    (*_err) << "    Indent a C# source file.\n";
    (*_err) << endl;
    (*_err) << "Other options:\n";
    (*_err) << "--------------\n";
    (*_err) << "    --suffix=####\n";
    (*_err) << "    Append the suffix #### instead of '.orig' to original filename.\n";
    (*_err) << endl;
    (*_err) << "    --suffix=none  OR  -n\n";
    (*_err) << "    Do not retain a backup of the original file.\n";
    (*_err) << endl;
    (*_err) << "    --recursive  OR  -r  OR  -R\n";
    (*_err) << "    Process subdirectories recursively.\n";
    (*_err) << endl;
    (*_err) << "    --exclude=####\n";
    (*_err) << "    Specify a file or directory #### to be excluded from processing.\n";
    (*_err) << endl;
    (*_err) << "    ignore-exclude-errors  OR  -i\n";
    (*_err) << "    Allow processing to continue if there are errors in the exclude=###\n";
    (*_err) << "    options. It will display the unmatched excludes.\n";
    (*_err) << endl;
    (*_err) << "    ignore-exclude-errors-x  OR  -xi\n";
    (*_err) << "    Allow processing to continue if there are errors in the exclude=###\n";
    (*_err) << "    options. It will NOT display the unmatched excludes.\n";
    (*_err) << endl;
    (*_err) << "    --errors-to-stdout  OR  -X\n";
    (*_err) << "    Print errors and help information to standard-output rather than\n";
    (*_err) << "    to standard-error.\n";
    (*_err) << endl;
    (*_err) << "    --preserve-date  OR  -Z\n";
    (*_err) << "    The date and time modified will not be changed in the formatted file.\n";
    (*_err) << endl;
    (*_err) << "    --verbose  OR  -v\n";
    (*_err) << "    Verbose mode. Extra informational messages will be displayed.\n";
    (*_err) << endl;
    (*_err) << "    --formatted  OR  -Q\n";
    (*_err) << "    Formatted display mode. Display only the files that have been formatted.\n";
    (*_err) << endl;
    (*_err) << "    --quiet  OR  -q\n";
    (*_err) << "    Quiet mode. Suppress all output except error messages.\n";
    (*_err) << endl;
    (*_err) << "    --lineend=windows  OR  -z1\n";
    (*_err) << "    --lineend=linux    OR  -z2\n";
    (*_err) << "    --lineend=macold   OR  -z3\n";
    (*_err) << "    Force use of the specified line end style. Valid options\n";
    (*_err) << "    are windows (CRLF), linux (LF), and macold (CR).\n";
    (*_err) << endl;
    (*_err) << "Command Line Only:\n";
    (*_err) << "------------------\n";
    (*_err) << "    --options=####\n";
    (*_err) << "    Specify an options file #### to read and use.\n";
    (*_err) << endl;
    (*_err) << "    --options=none\n";
    (*_err) << "    Disable the default options file.\n";
    (*_err) << "    Only the command-line parameters will be used.\n";
    (*_err) << endl;
    (*_err) << "    --ascii  OR  -I\n";
    (*_err) << "    The displayed output will be ascii characters only.\n";
    (*_err) << endl;
    (*_err) << "    --version  OR  -V\n";
    (*_err) << "    Print version number.\n";
    (*_err) << endl;
    (*_err) << "    --help  OR  -h  OR  -?\n";
    (*_err) << "    Print this help message.\n";
    (*_err) << 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(size_t i = 0; i < fileNameVector.size(); i++) {
        getFilePaths(fileNameVector[i]);

        // loop thru fileName vector formatting the files
        for(size_t j = 0; j < fileName.size(); j++)
            formatFile(fileName[j]);
    }

    // files are processed, display stats
    if(isVerbose) printVerboseStats(startTime);
}

// process options from the command line and options file
// build the vectors fileNameVector, excludeVector, optionsVector, and fileOptionsVector
void ASConsole::processOptions(vector<string>& argvOptions)
{
    string arg;
    bool ok = true;
    bool shouldParseOptionsFile = true;

    // get command line options
    for(size_t i = 0; i < argvOptions.size(); i++) {
        arg = argvOptions[i];

        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")) {
            shouldParseOptionsFile = false;
        } else if(isParamOption(arg, "--options=")) {
            optionsFileName = getParam(arg, "--options=");
            optionsFileRequired = true;
            if(optionsFileName.compare("") == 0) setOptionsFileName(" ");
        } else if(isOption(arg, "-h") || isOption(arg, "--help") || isOption(arg, "-?")) {
            printHelp();
            exit(EXIT_SUCCESS);
        } else if(isOption(arg, "-V") || isOption(arg, "--version")) {
            (*_err) << "Artistic Style Version " << g_version << endl;
            exit(EXIT_SUCCESS);
        } else if(arg[0] == '-') {
            optionsVector.push_back(arg);
        } else // file-name
        {
            standardizePath(arg);
            fileNameVector.push_back(arg);
        }
    }

    // get options file path and name
    if(shouldParseOptionsFile) {
        if(optionsFileName.compare("") == 0) {
            char* env = getenv("ARTISTIC_STYLE_OPTIONS");
            if(env != NULL) setOptionsFileName(env);
        }
        if(optionsFileName.compare("") == 0) {
            char* env = getenv("HOME");
            if(env != NULL) setOptionsFileName(string(env) + "/.astylerc");
        }
        if(optionsFileName.compare("") == 0) {
            char* env = getenv("USERPROFILE");
            if(env != NULL) setOptionsFileName(string(env) + "/astylerc");
        }
        if(optionsFileName.compare("") != 0) standardizePath(optionsFileName);
    }

    // create the options file vector and parse the options for errors
    ASOptions options(formatter);
    if(optionsFileName.compare("") != 0) {
        ifstream optionsIn(optionsFileName.c_str());
        if(optionsIn) {
            options.importOptions(optionsIn, fileOptionsVector);
            ok = options.parseOptions(fileOptionsVector, string(_("Invalid option file options:")));
        } else {
            if(optionsFileRequired) error(_("Cannot open options file"), optionsFileName.c_str());
            optionsFileName.clear();
        }
        optionsIn.close();
    }
    if(!ok) {
        (*_err) << options.getOptionErrors() << endl;
        (*_err) << _("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) {
        (*_err) << options.getOptionErrors() << endl;
        (*_err) << _("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
{
    remove(fileName_);
    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
{
    rename(oldFileName, newFileName);
    // if file still exists the remove needs more time - retry
    if(errno == EEXIST) {
        errno = 0;
        waitForRemove(newFileName);
        rename(oldFileName, newFileName);
    }
    if(errno) {
        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;
    }
    //  The following was removed in release 2.02 - jimp
    //	// remove separator from the end
    //	if (path[path.length()-1] == g_fileSeparator)
    //		path.erase(path.length()-1, 1);
    // 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
    struct tm* ptr;
    time_t lt;
    char str[20];
    lt = time(NULL);
    ptr = localtime(&lt);
    strftime(str, 20, "%x", ptr);
    // print the header
    printf("Artistic Style %s     %s\n", g_version, str);
    // print options file
    if(!optionsFileName.empty()) printf(_("Using default options file %s\n"), optionsFileName.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();
    float secs = (stopTime - startTime) / float(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;
        int minsec = int(secs + .5);
        printf(_("%d min %d sec   "), min, minsec);
    }

    string lines = getNumberFormat(linesOut);
    printf(_("%s lines\n"), lines.c_str());
}

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;
}

// Swap the two low order bytes of an integer value
// and convert 8 bit encoding to 16 bit.
int ASConsole::swap8to16bit(int value) const { return (((value & 0xFF) << 8) + (value >> 8)); }

// Swap the two low order bytes of a 16 bit integer value.
int ASConsole::swap16bit(int value) const { return (((value & 0xff) << 8) | ((value & 0xff00) >> 8)); }

void ASConsole::updateExcludeVector(string suffixParam)
{
    excludeVector.push_back(suffixParam);
    standardizePath(excludeVector.back(), true);
    excludeHitsVector.push_back(false);
}

// Adapted from SciTE UniConversion.cxx.
// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
// Modified for Artistic Style by Jim Pattee.
//
// Compute the length of an output utf-8 file given a utf-16 file.
// Input tlen is the size in BYTES (not wchar_t).
size_t ASConsole::Utf8Length(const char* data, size_t tlen, FileEncoding encoding) const
{
    enum { SURROGATE_LEAD_FIRST = 0xD800 };
    enum { SURROGATE_TRAIL_LAST = 0xDFFF };

    size_t len = 0;
    size_t wcharLen = tlen / 2;
    const short* uptr = reinterpret_cast<const short*>(data);
    for(size_t i = 0; i < wcharLen && uptr[i];) {
        size_t uch = encoding == UTF_16BE ? swap16bit(uptr[i]) : uptr[i];
        if(uch < 0x80) {
            len++;
        } else if(uch < 0x800) {
            len += 2;
        } else if((uch >= SURROGATE_LEAD_FIRST) && (uch <= SURROGATE_TRAIL_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.
size_t ASConsole::Utf8ToUtf16(char* utf8In, size_t inLen, FileEncoding encoding, char* utf16Out) const
{
    typedef unsigned short utf16; // 16 bits
    typedef unsigned char ubyte;  // 8 bits
    enum { SURROGATE_LEAD_FIRST = 0xD800 };
    enum { SURROGATE_LEAD_LAST = 0xDBFF };
    enum { SURROGATE_TRAIL_FIRST = 0xDC00 };
    enum { SURROGATE_TRAIL_LAST = 0xDFFF };
    enum { SURROGATE_FIRST_VALUE = 0x10000 };
    enum eState { eStart, eSecondOf4Bytes, ePenultimate, eFinal };

    int nCur = 0;
    ubyte* pRead = reinterpret_cast<ubyte*>(utf8In);
    utf16* pCur = reinterpret_cast<utf16*>(utf16Out);
    const ubyte* pEnd = pRead + inLen;
    const utf16* pCurStart = pCur;
    eState eState = eStart;

    // the BOM will automaticallt be converted to utf-16
    while(pRead < pEnd) {
        switch(eState) {
        case eStart:
            if((0xF0 & *pRead) == 0xF0) {
                nCur = (0x7 & *pRead) << 18;
                eState = eSecondOf4Bytes;
            } else if((0xE0 & *pRead) == 0xE0) {
                nCur = (~0xE0 & *pRead) << 12;
                eState = ePenultimate;
            } else if((0xC0 & *pRead) == 0xC0) {
                nCur = (~0xC0 & *pRead) << 6;
                eState = eFinal;
            } else {
                nCur = *pRead;
                eState = eStart;
            }
            break;
        case eSecondOf4Bytes:
            nCur |= (0x3F & *pRead) << 12;
            eState = ePenultimate;
            break;
        case ePenultimate:
            nCur |= (0x3F & *pRead) << 6;
            eState = eFinal;
            break;
        case eFinal:
            nCur |= (0x3F & *pRead);
            eState = eStart;
            break;
        default:
            error("Bad eState value", "Utf8ToUtf16()");
        }
        ++pRead;

        if(eState == eStart) {
            int codePoint = nCur;
            if(codePoint >= SURROGATE_FIRST_VALUE) {
                codePoint -= SURROGATE_FIRST_VALUE;
                int lead = (codePoint >> 10) + SURROGATE_LEAD_FIRST;
                *pCur++ = static_cast<utf16>((encoding == UTF_16BE) ? swap8to16bit(lead) : lead);
                int trail = (codePoint & 0x3ff) + SURROGATE_TRAIL_FIRST;
                *pCur++ = static_cast<utf16>((encoding == UTF_16BE) ? swap8to16bit(trail) : trail);
            } else {
                *pCur++ = static_cast<utf16>((encoding == UTF_16BE) ? swap8to16bit(codePoint) : codePoint);
            }
        }
    }
    // return value is the output length in BYTES (not wchar_t)
    return (pCur - pCurStart) * 2;
}

// Adapted from SciTE UniConversion.cxx.
// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
// Modified for Artistic Style by Jim Pattee.
//
// Compute the length of an output utf-16 file given a utf-8 file.
// Return value is the size in BYTES (not wchar_t).
size_t ASConsole::Utf16Length(const char* data, size_t len) const
{
    size_t ulen = 0;
    size_t charLen;
    for(size_t i = 0; i < len;) {
        unsigned char ch = static_cast<unsigned char>(data[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.
size_t ASConsole::Utf16ToUtf8(char* utf16In, size_t inLen, FileEncoding encoding, bool firstBlock, char* utf8Out) const
{
    typedef unsigned short utf16; // 16 bits
    typedef unsigned char ubyte;  // 8 bits
    enum { SURROGATE_LEAD_FIRST = 0xD800 };
    enum { SURROGATE_LEAD_LAST = 0xDBFF };
    enum { SURROGATE_TRAIL_FIRST = 0xDC00 };
    enum { SURROGATE_TRAIL_LAST = 0xDFFF };
    enum { SURROGATE_FIRST_VALUE = 0x10000 };
    enum eState { eStart, eSecondOf4Bytes, ePenultimate, eFinal };

    int nCur16 = 0;
    int nCur = 0;
    ubyte* pRead = reinterpret_cast<ubyte*>(utf16In);
    ubyte* pCur = reinterpret_cast<ubyte*>(utf8Out);
    const ubyte* pEnd = pRead + inLen;
    const ubyte* pCurStart = pCur;
    static eState eState = eStart; // eState is retained for subsequent blocks
    if(firstBlock) eState = eStart;

    // the BOM will automaticallt be converted to utf-8
    while(pRead < pEnd) {
        switch(eState) {
        case eStart:
            if(pRead >= pEnd) {
                ++pRead;
                break;
            }
            if(encoding == UTF_16LE) {
                nCur16 = *pRead++;
                nCur16 |= static_cast<utf16>(*pRead << 8);
            } else {
                nCur16 = static_cast<utf16>(*pRead++ << 8);
                nCur16 |= static_cast<utf16>(*pRead);
            }
            if(nCur16 >= SURROGATE_LEAD_FIRST && nCur16 <= SURROGATE_LEAD_LAST) {
                ++pRead;
                int trail;
                if(encoding == UTF_16LE) {
                    trail = *pRead++;
                    trail |= static_cast<utf16>(*pRead << 8);
                } else {
                    trail = static_cast<utf16>(*pRead++ << 8);
                    trail |= static_cast<utf16>(*pRead);
                }
                nCur16 = (((nCur16 & 0x3ff) << 10) | (trail & 0x3ff)) + SURROGATE_FIRST_VALUE;
            }
            ++pRead;

            if(nCur16 < 0x80) {
                nCur = static_cast<ubyte>(nCur16 & 0xFF);
                eState = eStart;
            } else if(nCur16 < 0x800) {
                nCur = static_cast<ubyte>(0xC0 | (nCur16 >> 6));
                eState = eFinal;
            } else if(nCur16 < SURROGATE_FIRST_VALUE) {
                nCur = static_cast<ubyte>(0xE0 | (nCur16 >> 12));
                eState = ePenultimate;
            } else {
                nCur = static_cast<ubyte>(0xF0 | (nCur16 >> 18));
                eState = eSecondOf4Bytes;
            }
            break;
        case eSecondOf4Bytes:
            nCur = static_cast<ubyte>(0x80 | ((nCur16 >> 12) & 0x3F));
            eState = ePenultimate;
            break;
        case ePenultimate:
            nCur = static_cast<ubyte>(0x80 | ((nCur16 >> 6) & 0x3F));
            eState = eFinal;
            break;
        case eFinal:
            nCur = static_cast<ubyte>(0x80 | (nCur16 & 0x3F));
            eState = eStart;
            break;
        default:
            error("Bad eState value", "Utf16ToUtf8()");
        }
        *pCur++ = static_cast<ubyte>(nCur);
    }
    return pCur - pCurStart;
}

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 = NULL, * mp = NULL;
    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
        size_t utf16Size = Utf16Length(out.str().c_str(), out.str().length());
        char* utf16Out = new char[utf16Size];
        size_t utf16Len = Utf8ToUtf16(const_cast<char*>(out.str().c_str()), out.str().length(), encoding, 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 recoginze 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");
            (*_err) << "*********  Cannot preserve file date" << endl;
        }
    }
}

#endif // ASConsole:

//-----------------------------------------------------------------------------
// ASOptions class
// used by both console and library builds
//-----------------------------------------------------------------------------

/**
 * parse the options vector
 * optionsVector can be either a fileOptionsVector (options file) or an optionsVector (command line)
 *
 * @return        true if no errors, false if errors
 */
bool ASOptions::parseOptions(vector<string>& optionsVector, const string& errorInfo)
{
    vector<string>::iterator option;
    string arg, 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, "style=allman") || isOption(arg, "style=ansi") || isOption(arg, "style=bsd") ||
       isOption(arg, "style=break")) {
        formatter.setFormattingStyle(STYLE_ALLMAN);
    } else if(isOption(arg, "style=java") || isOption(arg, "style=attach")) {
        formatter.setFormattingStyle(STYLE_JAVA);
    } else if(isOption(arg, "style=k&r") || isOption(arg, "style=kr") || isOption(arg, "style=k/r")) {
        formatter.setFormattingStyle(STYLE_KR);
    } else if(isOption(arg, "style=stroustrup")) {
        formatter.setFormattingStyle(STYLE_STROUSTRUP);
    } else if(isOption(arg, "style=whitesmith")) {
        formatter.setFormattingStyle(STYLE_WHITESMITH);
    } else if(isOption(arg, "style=banner")) {
        formatter.setFormattingStyle(STYLE_BANNER);
    } else if(isOption(arg, "style=gnu")) {
        formatter.setFormattingStyle(STYLE_GNU);
    } else if(isOption(arg, "style=linux")) {
        formatter.setFormattingStyle(STYLE_LINUX);
    } else if(isOption(arg, "style=horstmann")) {
        formatter.setFormattingStyle(STYLE_HORSTMANN);
    } else if(isOption(arg, "style=1tbs") || isOption(arg, "style=otbs")) {
        formatter.setFormattingStyle(STYLE_1TBS);
    } else if(isOption(arg, "style=pico")) {
        formatter.setFormattingStyle(STYLE_PICO);
    } else if(isOption(arg, "style=lisp") || isOption(arg, "style=python")) {
        formatter.setFormattingStyle(STYLE_LISP);
    } else if(isParamOption(arg, "A")) {
        int style = 0;
        string styleParam = getParam(arg, "A");
        if(styleParam.length() > 0) style = atoi(styleParam.c_str());
        if(style == 1)
            formatter.setFormattingStyle(STYLE_ALLMAN);
        else if(style == 2)
            formatter.setFormattingStyle(STYLE_JAVA);
        else if(style == 3)
            formatter.setFormattingStyle(STYLE_KR);
        else if(style == 4)
            formatter.setFormattingStyle(STYLE_STROUSTRUP);
        else if(style == 5)
            formatter.setFormattingStyle(STYLE_WHITESMITH);
        else if(style == 6)
            formatter.setFormattingStyle(STYLE_BANNER);
        else if(style == 7)
            formatter.setFormattingStyle(STYLE_GNU);
        else if(style == 8)
            formatter.setFormattingStyle(STYLE_LINUX);
        else if(style == 9)
            formatter.setFormattingStyle(STYLE_HORSTMANN);
        else if(style == 10)
            formatter.setFormattingStyle(STYLE_1TBS);
        else if(style == 11)
            formatter.setFormattingStyle(STYLE_PICO);
        else if(style == 12)
            formatter.setFormattingStyle(STYLE_LISP);
        else
            isOptionError(arg, errorInfo);
    }
    // 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, "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-instatement-indent=")) {
        int maxIndent = 40;
        string maxIndentParam = getParam(arg, "M", "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);
    } else if(isOption(arg, "N", "indent-namespaces")) {
        formatter.setNamespaceIndent(true);
    } else if(isOption(arg, "C", "indent-classes")) {
        formatter.setClassIndent(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, "L", "indent-labels")) {
        formatter.setLabelIndent(true);
    } else if(isOption(arg, "y", "break-closing-brackets")) {
        formatter.setBreakClosingHeaderBracketsMode(true);
    } else if(isOption(arg, "b", "brackets=break")) {
        formatter.setBracketFormatMode(BREAK_MODE);
    } else if(isOption(arg, "a", "brackets=attach")) {
        formatter.setBracketFormatMode(ATTACH_MODE);
    } else if(isOption(arg, "l", "brackets=linux")) {
        formatter.setBracketFormatMode(LINUX_MODE);
    } else if(isOption(arg, "u", "brackets=stroustrup")) {
        formatter.setBracketFormatMode(STROUSTRUP_MODE);
    } else if(isOption(arg, "g", "brackets=run-in")) {
        formatter.setBracketFormatMode(RUN_IN_MODE);
    } else if(isOption(arg, "O", "keep-one-line-blocks")) {
        formatter.setBreakOneLineBlocksMode(false);
    } else if(isOption(arg, "o", "keep-one-line-statements")) {
        formatter.setSingleStatementsMode(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, "xe", "delete-empty-lines")) {
        formatter.setDeleteEmptyLinesMode(true);
    } else if(isOption(arg, "E", "fill-empty-lines")) {
        formatter.setEmptyLineFill(true);
    } else if(isOption(arg, "w", "indent-preprocessor")) {
        formatter.setPreprocessorIndent(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, "j", "add-brackets")) {
        formatter.setAddBracketsMode(true);
    } else if(isOption(arg, "J", "add-one-line-brackets")) {
        formatter.setAddOneLineBracketsMode(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);
    }
    // depreciated options release 2.02 ///////////////////////////////////////////////////////////////////////////////
    else if(isOption(arg, "brackets=horstmann")) {
        isOptionError(arg, errorInfo);
    } else if(isOption(arg, "B", "indent-brackets")) {
        isOptionError(arg, errorInfo);
    } else if(isOption(arg, "G", "indent-blocks")) {
        isOptionError(arg, errorInfo);
    }
// end depreciated options ////////////////////////////////////////////////////////////////////////////////////////
#ifdef ASTYLE_LIB
    // End of options used by GUI /////////////////////////////////////////////////////////////////////////////////////
    else
        isOptionError(arg, errorInfo);
#else
    // Options used by only console ///////////////////////////////////////////////////////////////////////////////////
    else if(isOption(arg, "n", "suffix=none")) {
        g_console->setNoBackup(true);
    } else if(isParamOption(arg, "suffix=")) {
        string suffixParam = getParam(arg, "suffix=");
        if(suffixParam.length() > 0) {
            g_console->setOrigSuffix(suffixParam);
        }
    } else if(isParamOption(arg, "exclude=")) {
        string suffixParam = getParam(arg, "exclude=");
        if(suffixParam.length() > 0) g_console->updateExcludeVector(suffixParam);
    } else if(isOption(arg, "r", "R") || isOption(arg, "recursive")) {
        g_console->setIsRecursive(true);
    } else if(isOption(arg, "Z", "preserve-date")) {
        g_console->setPreserveDate(true);
    } else if(isOption(arg, "v", "verbose")) {
        g_console->setIsVerbose(true);
    } else if(isOption(arg, "Q", "formatted")) {
        g_console->setIsFormattedOnly(true);
    } else if(isOption(arg, "q", "quiet")) {
        g_console->setIsQuiet(true);
    } else if(isOption(arg, "i", "ignore-exclude-errors")) {
        g_console->setIgnoreExcludeErrors(true);
    } else if(isOption(arg, "xi", "ignore-exclude-errors-x")) {
        g_console->setIgnoreExcludeErrorsAndDisplay(true);
    } else if(isOption(arg, "X", "errors-to-stdout")) {
        _err = &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
        isOptionError(arg, errorInfo);
#endif
} // End of parseOption function

void ASOptions::importOptions(istream& in, vector<string>& optionsVector)
{
    char ch;
    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') break;
                }

            // break options on spaces, tabs, commas, or new-lines
            if(in.eof() || ch == ' ' || ch == '\t' || ch == ',' || ch == '\n')
                break;
            else
                currentToken.append(1, ch);

        } while(in);

        if(currentToken.length() != 0) optionsVector.push_back(currentToken);
    }
}

string ASOptions::getOptionErrors() { 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.compare(op) == 0; }

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 << 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);
}

// *******************   end of ASOptions functions   *********************************************

} // end of namespace astyle

// *******************   end of astyle namespace    ***********************************************

using namespace astyle;

#ifdef ASTYLE_JNI
// *************************   JNI functions   ****************************************************
// 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 == 0) {
        cout << "Cannot find java method ErrorHandler" << endl;
        return textErr;
    }

    // convert jstring to char*
    const char* textIn = env->GetStringUTFChars(textInJava, NULL);
    const char* options = env->GetStringUTFChars(optionsJava, NULL);

    // call the C++ formatting function
    char* textOut = AStyleMain(textIn, options, javaErrorHandler, javaMemoryAlloc);
    // if an error message occurred it was displayed by errorHandler
    if(textOut == NULL) 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

#ifdef ASTYLE_LIB
// *************************   GUI functions   ****************************************************
/*
 * IMPORTANT VC DLL linker for WIN32 must have the parameter  /EXPORT:AStyleMain=_AStyleMain@16
 *                                                            /EXPORT:AStyleGetVersion=_AStyleGetVersion@0
 * No /EXPORT is required for x64
 */
extern "C" EXPORT char* STDCALL AStyleMain(const char* pSourceIn,  // pointer to the source to be formatted
                                           const char* pOptions,   // pointer to AStyle options, separated by \n
                                           fpError fpErrorHandler, // pointer to error handler function
                                           fpAlloc fpMemoryAlloc)  // pointer to memory allocation function
{
    if(fpErrorHandler == NULL) // cannot display a message if no error handler
        return NULL;

    if(pSourceIn == NULL) {
        fpErrorHandler(101, "No pointer to source input.");
        return NULL;
    }
    if(pOptions == NULL) {
        fpErrorHandler(102, "No pointer to AStyle options.");
        return NULL;
    }
    if(fpMemoryAlloc == NULL) {
        fpErrorHandler(103, "No pointer to memory allocation function.");
        return NULL;
    }

    ASFormatter formatter;
    ASOptions options(formatter);

    vector<string> optionsVector;
    istringstream opt(pOptions);

    options.importOptions(opt, optionsVector);

    bool ok = options.parseOptions(optionsVector, "Invalid Artistic Style options:");

    if(!ok) fpErrorHandler(210, options.getOptionErrors().c_str());

    istringstream in(pSourceIn);
    ASStreamIterator<istringstream> 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 bracket and break-blocks is requested
            if(formatter.getIsLineReady()) {
                out << streamIterator.getOutputEOL();
                out << formatter.nextLine();
            }
        }
    }

    unsigned long textSizeOut = out.str().length();
    char* pTextOut = fpMemoryAlloc(textSizeOut + 1); // call memory allocation function
                                                     //    pTextOut = NULL;           // for testing
    if(pTextOut == NULL) {
        fpErrorHandler(110, "Allocation failure on output.");
        return NULL;
    }

    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   ****************************************************

int main(int argc, char** argv)
{
    // create objects
    ASFormatter formatter;
    g_console = new ASConsole(formatter);

    // process command line and options file
    // build the vectors fileNameVector, optionsVector, and fileOptionsVector
    vector<string> argvOptions;
    argvOptions = g_console->getArgvOptions(argc, argv);
    g_console->processOptions(argvOptions);

    // if no files have been given, use cin for input and cout for output
    if(g_console->fileNameVectorIsEmpty()) {
        g_console->formatCinToCout();
        return EXIT_SUCCESS;
    }

    // process entries in the fileNameVector
    g_console->processFiles();

    delete g_console;
    return EXIT_SUCCESS;
}

#endif // ASTYLE_LIB
