<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the  "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Base header file.  Must be first.
#include <xalanc/Include/PlatformDefinitions.hpp>

#if defined(XALAN_CLASSIC_IOSTREAMS)
#include <iostream.h>
#else
#include <iostream>
#endif

#include <cstdio>

XALAN_USING_STD(cerr)
XALAN_USING_STD(cout)
XALAN_USING_STD(endl)

#include "xercesc/util/PlatformUtils.hpp"

#include "xercesc/parsers/XercesDOMParser.hpp"

#include "xalanc/PlatformSupport/XalanMemoryManagerDefault.hpp"

#include "xalanc/XercesParserLiaison/XercesParserLiaison.hpp"
#include "xalanc/XercesParserLiaison/XercesDOMSupport.hpp"

#include "xalanc/XalanTransformer/XalanTransformer.hpp"
#include "xalanc/XalanTransformer/XercesDOMWrapperParsedSource.hpp"

// HARNESS HEADERS...
#include "xalanc/Harness/XalanFileUtility.hpp"
#include "xalanc/Harness/XalanDiagnosticMemoryManager.hpp"
#include "xalanc/Harness/XalanXMLFileReporter.hpp"

//#define XALAN_VQ_SPECIAL_TRACE
#if defined(XALAN_VQ_SPECIAL_TRACE)
#include "C:/Program Files/Rational/Quantify/pure.h"
#endif

// Just hoist everything...
XALAN_CPP_NAMESPACE_USE

// This is here for memory leak testing.
#if !defined(NDEBUG) && defined(_MSC_VER)
#include <crtdbg.h>
#endif

XALAN_USING_XERCES(MemoryManager)

void
setHelp(XalanFileUtility&   h)
{
    h.args.getHelpStream() << endl
         << "conf dir [-sub -out -gold -source (XST | XPL | DOM)]"
         << endl
         << endl
         << "dir		(base directory for testcases)"
         << endl
         << "-sub dir	(specific directory)"
         << endl
         << "-out dir	(base directory for output)"
         << endl
         << "-gold dir	(base directory for gold files)"
         << endl
         << "-src type	(parsed source; XalanSourceTree(d), XercesParserLiasion, XercesDOM)"
         << endl;
}

static const char* const    excludeStylesheets[] =
{
//  "output22.xsl",     // Excluded because it outputs EBCDIC
    0
};

inline bool
checkForExclusion(
            const XalanDOMString&   currentFile,
            MemoryManager&          theMemoryManager)
{
    for (size_t i = 0; excludeStylesheets[i] != 0; ++i)
    {
        if (currentFile == XalanDOMString(excludeStylesheets[i], theMemoryManager))
        {
            return true;
        }
    }

    return false;
}

int
parseWithTransformer(
            int                             sourceType,
            XalanTransformer&               xalan,
            const XSLTInputSource&          xmlInput,
            const XalanCompiledStylesheet*  styleSheet,
            const XSLTResultTarget&         output,
            XalanXMLFileReporter&           logFile,
            XalanFileUtility&               h)
{
    const XalanParsedSource* parsedSource = 0;

    MemoryManagerType& mgr = h.getMemoryManager();

    int theResult = 0;

    // Parse the XML source accordingly.
    //
    if (sourceType != 0 )
    {
        theResult = xalan.parseSource(xmlInput, parsedSource, true);
        h.data.xmlFormat = XalanDOMString("XercesParserLiasion", mgr);
    }
    else
    {
        theResult = xalan.parseSource(xmlInput, parsedSource, false);
        h.data.xmlFormat = XalanDOMString("XalanSourceTree", mgr);
    }

    // If the source was parsed correctly then perform the transform else report the failure.
    //
    if (parsedSource == 0)
    {
        // Report the failure and be sure to increment fail count.
        //
        cout << "ParseWTransformer - Failed to parse source document for " << h.data.testOrFile << endl;
        ++h.data.fail;
        XalanDOMString tmp("Failed to parse source document.  ", mgr);

        tmp.append(xalan.getLastError());

        logFile.logErrorResult(h.data.testOrFile,  tmp );
    }
    else
    {
        theResult = xalan.transform(*parsedSource, styleSheet, output);
        xalan.destroyParsedSource(parsedSource);
    }

    return theResult;
}

int
parseWithXerces(
            XalanTransformer&               xalan,
            const XSLTInputSource&          xmlInput,
            const XalanCompiledStylesheet*  styleSheet,
            const XSLTResultTarget&         output,
            XalanXMLFileReporter&           logFile,
            XalanFileUtility&               h)
{
    XALAN_USING_XERCES(XercesDOMParser)
    XALAN_USING_XERCES(DOMDocument)

    MemoryManagerType& mgr = h.getMemoryManager();

    h.data.xmlFormat = XalanDOMString("Xerces_DOM", mgr);

    XercesDOMParser  theParser(0, &mgr);

#if XERCES_VERSION_MAJOR < 3
    theParser.setDoValidation(true);
#else
    theParser.setValidationScheme(XercesDOMParser::Val_Auto);
#endif

    theParser.setDoNamespaces(true);

    theParser.parse(xmlInput);

    DOMDocument* const theDOM = theParser.getDocument();

    theDOM->normalize();

    XercesDOMSupport    theDOMSupport(mgr);
    XercesParserLiaison theParserLiaison(mgr);

    int theResult = 0;

    try
    {
        const XercesDOMWrapperParsedSource  parsedSource(
                    theDOM,
                    theParserLiaison,
                    theDOMSupport,
                    XalanDOMString(xmlInput.getSystemId(), mgr),
                    mgr);

        theResult = xalan.transform(parsedSource, styleSheet, output);
    }
    catch(...)
    {
        // Report the failure and be sure to increment fail count.
        //
        cout << "parseWXerces - Failed to parse source document for " << h.data.testOrFile << endl;
        ++h.data.fail;
        XalanDOMString resultString("Failed to parse source document.  ", mgr);
        resultString.append( xalan.getLastError());
        logFile.logErrorResult(h.data.testOrFile,  resultString);
    }

    return theResult;
}

int
runTests(
            int             argc,
            char*           argv[],
            MemoryManager&  theMemoryManager)
{
    int theResult = 0;

    try
    {
        XalanFileUtility    h(theMemoryManager);

        // Set the program help string,  then get the command line parameters.
        //
        setHelp(h);

        if (h.getParams(argc, argv, "CONF-RESULTS") == true)
        {
            XalanTransformer    xalan(theMemoryManager);

            // Get drive designation for final analysis and generate Unique name for results log.
            //
            XalanDOMString  drive(theMemoryManager);            // This is used to get stylesheet for final analysis
            h.getDrive(drive);
            const XalanDOMString  resultFilePrefix("conf", theMemoryManager);       // This & UniqRunid used for log file name.
            XalanDOMString  UniqRunid(theMemoryManager);
            h.generateUniqRunid(UniqRunid);
            XalanDOMString  resultsFile(drive, theMemoryManager);
            resultsFile += h.args.output;
            resultsFile += resultFilePrefix;
            resultsFile += UniqRunid;
            resultsFile += XalanFileUtility::s_xmlSuffix;

            // Open results log, and do some initialization of result data.
            //
            XalanXMLFileReporter    logFile(theMemoryManager, resultsFile);
            logFile.logTestFileInit("Conformance Testing:");
            h.data.xmlFormat = XalanDOMString("NotSet", theMemoryManager);

            // Get the list of Directories that are below conf and iterate through them
            //

            // Flag indicates directory found. Used in conjunction with -sub cmd-line arg.
            bool    foundDir = false;

            typedef XalanFileUtility::FileNameVectorType        FileNameVectorType;

            FileNameVectorType  dirs(theMemoryManager);
            h.getDirectoryNames(h.args.base, dirs);

            int theResult = 0;

            for(FileNameVectorType::size_type j = 0;
                    j < dirs.size() && theResult == 0;
                        ++j)
            {
                // Skip all but the specified directory if the -sub cmd-line option was used.
                //
                const XalanDOMString&   currentDir = dirs[j];

                if ((length(h.args.sub) == 0 ||
                     equals(currentDir, h.args.sub) == true) &&
                    currentDir[0] != XalanUnicode::charFullStop)
                {
                    // Check that output directory is there.
                    //
                    XalanDOMString  theOutputDir(theMemoryManager);
                    theOutputDir = h.args.output;
                    theOutputDir += currentDir;

                    h.checkAndCreateDir(theOutputDir);

                    // Indicate that directory was processed and get test files from the directory
                    //
                    foundDir = true;
                    FileNameVectorType files(theMemoryManager);
                    h.getTestFileNames(h.args.base, currentDir, true, files);

                    // Log directory in results log and process it's files.
                    //
                    logFile.logTestCaseInit(currentDir);

                    for(FileNameVectorType::size_type i = 0; i < files.size(); i++)
                    {
                        XalanXMLFileReporter::Hashtable attrs(theMemoryManager);

                        const XalanDOMString&   currentFile = files[i];
                        if (checkForExclusion(currentFile, theMemoryManager))
                            continue;

                        h.data.testOrFile = currentFile;
                        XalanDOMString  theXSLFile( h.args.base, theMemoryManager);
                        theXSLFile += currentDir;
                        theXSLFile += XalanFileUtility::s_pathSep;
                        theXSLFile += currentFile;

                        // Check and see if the .xml file exists. If not skip this .xsl file and continue.
                        bool fileStatus = true;
                        XalanDOMString  theXMLFile(theMemoryManager);
                        h.generateFileName(theXSLFile, "xml", theXMLFile, &fileStatus);
                        if (!fileStatus)
                            continue;

                        h.data.xmlFileURL = theXMLFile;
                        h.data.xslFileURL = theXSLFile;

                        XalanDOMString  theGoldFile(h.args.gold, theMemoryManager);
                        theGoldFile += currentDir;
                        theGoldFile += XalanFileUtility::s_pathSep;
                        theGoldFile += currentFile;

                        h.generateFileName(theGoldFile, "out", theGoldFile);

                        XalanDOMString  outbase (h.args.output, theMemoryManager);
                        outbase += currentDir;
                        outbase += XalanFileUtility::s_pathSep;
                        outbase += currentFile;
                        XalanDOMString  theOutputFile(theMemoryManager);
                        h.generateFileName(outbase, "out", theOutputFile);

                        const XSLTInputSource   xslInputSource(theXSLFile, theMemoryManager);
                        const XSLTInputSource   xmlInputSource(theXMLFile, theMemoryManager);
                        const XSLTResultTarget  resultFile(theOutputFile, theMemoryManager);

                        // Parsing(compile) the XSL stylesheet and report the results..
                        //
                        const XalanCompiledStylesheet*  compiledSS = 0;
                        xalan.compileStylesheet(xslInputSource, compiledSS);
                        if (compiledSS == 0 )
                        {
                            // Report the failure and be sure to increment fail count.
                            //
                            CharVectorType  theVector(theMemoryManager);

                            TranscodeToLocalCodePage(currentFile, theVector);

                            cout << "Failed to parse stylesheet for "
                                 << theVector
                                 << endl;
                            h.data.fail += 1;
                            XalanDOMString tmp("Failed to parse stylesheet.  ", theMemoryManager);
                            tmp += XalanDOMString(xalan.getLastError(), theMemoryManager);
                            logFile.logErrorResult(currentFile, theMemoryManager);
                            continue;
                        }

                        // Parse the Source XML based on input parameters, and then perform transform.
                        //
                        switch (h.args.source)
                        {
                            case 0:
                            case 1:
                                theResult = parseWithTransformer(h.args.source, xalan, xmlInputSource, compiledSS, resultFile, logFile, h);
                                break;

                            case 2:
                                theResult = parseWithXerces(xalan, xmlInputSource, compiledSS, resultFile, logFile, h);
                                break;
                        }

                        // Check and report results...  Then delete compiled stylesheet.
                        //
                        h.checkResults(theOutputFile, theGoldFile, logFile);
                        xalan.destroyStylesheet(compiledSS);
                    }

                    logFile.logTestCaseClose("Done", "Pass");
                }
            }

            // Check to see if -sub cmd-line directory was processed correctly.
            //
            if (!foundDir)
            {
                CharVectorType  vect(theMemoryManager);
                TranscodeToLocalCodePage(h.args.sub, vect);

                cout << "Specified test directory: \"" << c_str(vect) << "\" not found" << endl;
            }
            else if (theResult != 0)
            {
                cout << "An unexpected tranformer error occurred.  The error code is "
                     << theResult
                     << "\n"
                     << "The error message is \""
                     << xalan.getLastError()
                     << endl;
            }

            h.reportPassFail(logFile, UniqRunid);
            logFile.logTestFileClose("Conformance ", "Done");
            logFile.close();

            h.analyzeResults(xalan, resultsFile);
        }
    }
    catch(const XalanDiagnosticMemoryManager::LockException&)
    {
        cerr << "An attempt was made to allocate memory "
                "from a locked XalanDiagnosticMemoryManager "
                "instance!"
             << endl
             << endl;

        theResult = -1;
    }
    catch(...)
    {
        cerr << "Initialization of testing harness failed!" << endl << endl;
    }

    return theResult;
}

int
main(
            int     argc,
            char*   argv[])
{
#if !defined(NDEBUG) && defined(_MSC_VER)
    _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
    _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
#endif

#if defined(XALAN_VQ_SPECIAL_TRACE)
    QuantifyStopRecordingData();
    QuantifyClearData();
#endif

    int theResult = 0;

    try
    {
        XALAN_USING_XERCES(XMLPlatformUtils)
        XALAN_USING_XERCES(XMLUni)

        XalanMemoryManagerDefault       theGlobalMemoryManager;
        XalanDiagnosticMemoryManager    theDiagnosticMemoryManager(theGlobalMemoryManager, true, &cerr);
        XalanMemoryManagerDefault       theTestingMemoryManager;

        // Call the static initializers for xerces and xalan, and create a transformer
        //
        XMLPlatformUtils::Initialize(
            XMLUni::fgXercescDefaultLocale,
            0,
            0,
            &theDiagnosticMemoryManager,
            true);

        XalanTransformer::initialize(theDiagnosticMemoryManager);

        theDiagnosticMemoryManager.lock();

        {
            theResult = runTests(argc, argv, theTestingMemoryManager);
        }

        theDiagnosticMemoryManager.unlock();

        XalanTransformer::terminate();

        XMLPlatformUtils::Terminate();

        XalanTransformer::ICUCleanUp();
    }
    catch(...)
    {
        cerr << "Initialization failed!" << endl << endl;

        theResult = -1;
    }

    return theResult;
}
<commit_msg>Update to track Xerces-C API change.<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the  "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Base header file.  Must be first.
#include <xalanc/Include/PlatformDefinitions.hpp>

#if defined(XALAN_CLASSIC_IOSTREAMS)
#include <iostream.h>
#else
#include <iostream>
#endif

#include <cstdio>

XALAN_USING_STD(cerr)
XALAN_USING_STD(cout)
XALAN_USING_STD(endl)

#include "xercesc/util/PlatformUtils.hpp"

#include "xercesc/parsers/XercesDOMParser.hpp"

#include "xalanc/PlatformSupport/XalanMemoryManagerDefault.hpp"

#include "xalanc/XercesParserLiaison/XercesParserLiaison.hpp"
#include "xalanc/XercesParserLiaison/XercesDOMSupport.hpp"

#include "xalanc/XalanTransformer/XalanTransformer.hpp"
#include "xalanc/XalanTransformer/XercesDOMWrapperParsedSource.hpp"

// HARNESS HEADERS...
#include "xalanc/Harness/XalanFileUtility.hpp"
#include "xalanc/Harness/XalanDiagnosticMemoryManager.hpp"
#include "xalanc/Harness/XalanXMLFileReporter.hpp"

//#define XALAN_VQ_SPECIAL_TRACE
#if defined(XALAN_VQ_SPECIAL_TRACE)
#include "C:/Program Files/Rational/Quantify/pure.h"
#endif

// Just hoist everything...
XALAN_CPP_NAMESPACE_USE

// This is here for memory leak testing.
#if !defined(NDEBUG) && defined(_MSC_VER)
#include <crtdbg.h>
#endif

XALAN_USING_XERCES(MemoryManager)

void
setHelp(XalanFileUtility&   h)
{
    h.args.getHelpStream() << endl
         << "conf dir [-sub -out -gold -source (XST | XPL | DOM)]"
         << endl
         << endl
         << "dir		(base directory for testcases)"
         << endl
         << "-sub dir	(specific directory)"
         << endl
         << "-out dir	(base directory for output)"
         << endl
         << "-gold dir	(base directory for gold files)"
         << endl
         << "-src type	(parsed source; XalanSourceTree(d), XercesParserLiasion, XercesDOM)"
         << endl;
}

static const char* const    excludeStylesheets[] =
{
//  "output22.xsl",     // Excluded because it outputs EBCDIC
    0
};

inline bool
checkForExclusion(
            const XalanDOMString&   currentFile,
            MemoryManager&          theMemoryManager)
{
    for (size_t i = 0; excludeStylesheets[i] != 0; ++i)
    {
        if (currentFile == XalanDOMString(excludeStylesheets[i], theMemoryManager))
        {
            return true;
        }
    }

    return false;
}

int
parseWithTransformer(
            int                             sourceType,
            XalanTransformer&               xalan,
            const XSLTInputSource&          xmlInput,
            const XalanCompiledStylesheet*  styleSheet,
            const XSLTResultTarget&         output,
            XalanXMLFileReporter&           logFile,
            XalanFileUtility&               h)
{
    const XalanParsedSource* parsedSource = 0;

    MemoryManagerType& mgr = h.getMemoryManager();

    int theResult = 0;

    // Parse the XML source accordingly.
    //
    if (sourceType != 0 )
    {
        theResult = xalan.parseSource(xmlInput, parsedSource, true);
        h.data.xmlFormat = XalanDOMString("XercesParserLiasion", mgr);
    }
    else
    {
        theResult = xalan.parseSource(xmlInput, parsedSource, false);
        h.data.xmlFormat = XalanDOMString("XalanSourceTree", mgr);
    }

    // If the source was parsed correctly then perform the transform else report the failure.
    //
    if (parsedSource == 0)
    {
        // Report the failure and be sure to increment fail count.
        //
        cout << "ParseWTransformer - Failed to parse source document for " << h.data.testOrFile << endl;
        ++h.data.fail;
        XalanDOMString tmp("Failed to parse source document.  ", mgr);

        tmp.append(xalan.getLastError());

        logFile.logErrorResult(h.data.testOrFile,  tmp );
    }
    else
    {
        theResult = xalan.transform(*parsedSource, styleSheet, output);
        xalan.destroyParsedSource(parsedSource);
    }

    return theResult;
}

int
parseWithXerces(
            XalanTransformer&               xalan,
            const XSLTInputSource&          xmlInput,
            const XalanCompiledStylesheet*  styleSheet,
            const XSLTResultTarget&         output,
            XalanXMLFileReporter&           logFile,
            XalanFileUtility&               h)
{
    XALAN_USING_XERCES(XercesDOMParser)
    XALAN_USING_XERCES(DOMDocument)

    MemoryManagerType& mgr = h.getMemoryManager();

    h.data.xmlFormat = XalanDOMString("Xerces_DOM", mgr);

    XercesDOMParser  theParser(0, &mgr);

#if XERCES_VERSION_MAJOR < 3
    theParser.setDoValidation(true);
#else
    theParser.setValidationScheme(XercesDOMParser::Val_Auto);
#endif

    theParser.setDoNamespaces(true);

    theParser.parse(xmlInput);

    DOMDocument* const theDOM = theParser.getDocument();

    theDOM->normalize();

    XercesDOMSupport    theDOMSupport(mgr);
    XercesParserLiaison theParserLiaison(mgr);

    int theResult = 0;

    try
    {
        const XercesDOMWrapperParsedSource  parsedSource(
                    theDOM,
                    theParserLiaison,
                    theDOMSupport,
                    XalanDOMString(xmlInput.getSystemId(), mgr),
                    mgr);

        theResult = xalan.transform(parsedSource, styleSheet, output);
    }
    catch(...)
    {
        // Report the failure and be sure to increment fail count.
        //
        cout << "parseWXerces - Failed to parse source document for " << h.data.testOrFile << endl;
        ++h.data.fail;
        XalanDOMString resultString("Failed to parse source document.  ", mgr);
        resultString.append( xalan.getLastError());
        logFile.logErrorResult(h.data.testOrFile,  resultString);
    }

    return theResult;
}

int
runTests(
            int             argc,
            char*           argv[],
            MemoryManager&  theMemoryManager)
{
    int theResult = 0;

    try
    {
        XalanFileUtility    h(theMemoryManager);

        // Set the program help string,  then get the command line parameters.
        //
        setHelp(h);

        if (h.getParams(argc, argv, "CONF-RESULTS") == true)
        {
            XalanTransformer    xalan(theMemoryManager);

            // Get drive designation for final analysis and generate Unique name for results log.
            //
            XalanDOMString  drive(theMemoryManager);            // This is used to get stylesheet for final analysis
            h.getDrive(drive);
            const XalanDOMString  resultFilePrefix("conf", theMemoryManager);       // This & UniqRunid used for log file name.
            XalanDOMString  UniqRunid(theMemoryManager);
            h.generateUniqRunid(UniqRunid);
            XalanDOMString  resultsFile(drive, theMemoryManager);
            resultsFile += h.args.output;
            resultsFile += resultFilePrefix;
            resultsFile += UniqRunid;
            resultsFile += XalanFileUtility::s_xmlSuffix;

            // Open results log, and do some initialization of result data.
            //
            XalanXMLFileReporter    logFile(theMemoryManager, resultsFile);
            logFile.logTestFileInit("Conformance Testing:");
            h.data.xmlFormat = XalanDOMString("NotSet", theMemoryManager);

            // Get the list of Directories that are below conf and iterate through them
            //

            // Flag indicates directory found. Used in conjunction with -sub cmd-line arg.
            bool    foundDir = false;

            typedef XalanFileUtility::FileNameVectorType        FileNameVectorType;

            FileNameVectorType  dirs(theMemoryManager);
            h.getDirectoryNames(h.args.base, dirs);

            int theResult = 0;

            for(FileNameVectorType::size_type j = 0;
                    j < dirs.size() && theResult == 0;
                        ++j)
            {
                // Skip all but the specified directory if the -sub cmd-line option was used.
                //
                const XalanDOMString&   currentDir = dirs[j];

                if ((length(h.args.sub) == 0 ||
                     equals(currentDir, h.args.sub) == true) &&
                    currentDir[0] != XalanUnicode::charFullStop)
                {
                    // Check that output directory is there.
                    //
                    XalanDOMString  theOutputDir(theMemoryManager);
                    theOutputDir = h.args.output;
                    theOutputDir += currentDir;

                    h.checkAndCreateDir(theOutputDir);

                    // Indicate that directory was processed and get test files from the directory
                    //
                    foundDir = true;
                    FileNameVectorType files(theMemoryManager);
                    h.getTestFileNames(h.args.base, currentDir, true, files);

                    // Log directory in results log and process it's files.
                    //
                    logFile.logTestCaseInit(currentDir);

                    for(FileNameVectorType::size_type i = 0; i < files.size(); i++)
                    {
                        XalanXMLFileReporter::Hashtable attrs(theMemoryManager);

                        const XalanDOMString&   currentFile = files[i];
                        if (checkForExclusion(currentFile, theMemoryManager))
                            continue;

                        h.data.testOrFile = currentFile;
                        XalanDOMString  theXSLFile( h.args.base, theMemoryManager);
                        theXSLFile += currentDir;
                        theXSLFile += XalanFileUtility::s_pathSep;
                        theXSLFile += currentFile;

                        // Check and see if the .xml file exists. If not skip this .xsl file and continue.
                        bool fileStatus = true;
                        XalanDOMString  theXMLFile(theMemoryManager);
                        h.generateFileName(theXSLFile, "xml", theXMLFile, &fileStatus);
                        if (!fileStatus)
                            continue;

                        h.data.xmlFileURL = theXMLFile;
                        h.data.xslFileURL = theXSLFile;

                        XalanDOMString  theGoldFile(h.args.gold, theMemoryManager);
                        theGoldFile += currentDir;
                        theGoldFile += XalanFileUtility::s_pathSep;
                        theGoldFile += currentFile;

                        h.generateFileName(theGoldFile, "out", theGoldFile);

                        XalanDOMString  outbase (h.args.output, theMemoryManager);
                        outbase += currentDir;
                        outbase += XalanFileUtility::s_pathSep;
                        outbase += currentFile;
                        XalanDOMString  theOutputFile(theMemoryManager);
                        h.generateFileName(outbase, "out", theOutputFile);

                        const XSLTInputSource   xslInputSource(theXSLFile, theMemoryManager);
                        const XSLTInputSource   xmlInputSource(theXMLFile, theMemoryManager);
                        const XSLTResultTarget  resultFile(theOutputFile, theMemoryManager);

                        // Parsing(compile) the XSL stylesheet and report the results..
                        //
                        const XalanCompiledStylesheet*  compiledSS = 0;
                        xalan.compileStylesheet(xslInputSource, compiledSS);
                        if (compiledSS == 0 )
                        {
                            // Report the failure and be sure to increment fail count.
                            //
                            CharVectorType  theVector(theMemoryManager);

                            TranscodeToLocalCodePage(currentFile, theVector);

                            cout << "Failed to parse stylesheet for "
                                 << theVector
                                 << endl;
                            h.data.fail += 1;
                            XalanDOMString tmp("Failed to parse stylesheet.  ", theMemoryManager);
                            tmp += XalanDOMString(xalan.getLastError(), theMemoryManager);
                            logFile.logErrorResult(currentFile, theMemoryManager);
                            continue;
                        }

                        // Parse the Source XML based on input parameters, and then perform transform.
                        //
                        switch (h.args.source)
                        {
                            case 0:
                            case 1:
                                theResult = parseWithTransformer(h.args.source, xalan, xmlInputSource, compiledSS, resultFile, logFile, h);
                                break;

                            case 2:
                                theResult = parseWithXerces(xalan, xmlInputSource, compiledSS, resultFile, logFile, h);
                                break;
                        }

                        // Check and report results...  Then delete compiled stylesheet.
                        //
                        h.checkResults(theOutputFile, theGoldFile, logFile);
                        xalan.destroyStylesheet(compiledSS);
                    }

                    logFile.logTestCaseClose("Done", "Pass");
                }
            }

            // Check to see if -sub cmd-line directory was processed correctly.
            //
            if (!foundDir)
            {
                CharVectorType  vect(theMemoryManager);
                TranscodeToLocalCodePage(h.args.sub, vect);

                cout << "Specified test directory: \"" << c_str(vect) << "\" not found" << endl;
            }
            else if (theResult != 0)
            {
                cout << "An unexpected tranformer error occurred.  The error code is "
                     << theResult
                     << "\n"
                     << "The error message is \""
                     << xalan.getLastError()
                     << endl;
            }

            h.reportPassFail(logFile, UniqRunid);
            logFile.logTestFileClose("Conformance ", "Done");
            logFile.close();

            h.analyzeResults(xalan, resultsFile);
        }
    }
    catch(const XalanDiagnosticMemoryManager::LockException&)
    {
        cerr << "An attempt was made to allocate memory "
                "from a locked XalanDiagnosticMemoryManager "
                "instance!"
             << endl
             << endl;

        theResult = -1;
    }
    catch(...)
    {
        cerr << "Initialization of testing harness failed!" << endl << endl;
    }

    return theResult;
}

int
main(
            int     argc,
            char*   argv[])
{
#if !defined(NDEBUG) && defined(_MSC_VER)
    _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
    _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
#endif

#if defined(XALAN_VQ_SPECIAL_TRACE)
    QuantifyStopRecordingData();
    QuantifyClearData();
#endif

    int theResult = 0;

    try
    {
        XALAN_USING_XERCES(XMLPlatformUtils)
        XALAN_USING_XERCES(XMLUni)

        XalanMemoryManagerDefault       theGlobalMemoryManager;
        XalanDiagnosticMemoryManager    theDiagnosticMemoryManager(theGlobalMemoryManager, true, &cerr);
        XalanMemoryManagerDefault       theTestingMemoryManager;

        // Call the static initializers for xerces and xalan, and create a transformer
        //
        XMLPlatformUtils::Initialize(
            XMLUni::fgXercescDefaultLocale,
            0,
            0,
#if XERCES_VERSION_MAJOR >= 3
            &theDiagnosticMemoryManager);
#else
            &theDiagnosticMemoryManager,
            true);
#endif

        XalanTransformer::initialize(theDiagnosticMemoryManager);

        theDiagnosticMemoryManager.lock();

        {
            theResult = runTests(argc, argv, theTestingMemoryManager);
        }

        theDiagnosticMemoryManager.unlock();

        XalanTransformer::terminate();

        XMLPlatformUtils::Terminate();

        XalanTransformer::ICUCleanUp();
    }
    catch(...)
    {
        cerr << "Initialization failed!" << endl << endl;

        theResult = -1;
    }

    return theResult;
}
<|endoftext|>


<commit_before>// Basic GLnexus command-line interface for use on one compute node

#include <iostream>
#include <exception>
#include <fstream>
#include <getopt.h>
#include <sstream>
#include "vcf.h"
#include "hfile.h"
#include "service.h"
#include "unifier.h"
#include "BCFKeyValueData.h"
#include "RocksKeyValue.h"
#include "ctpl_stl.h"
#include "spdlog/spdlog.h"
#include "cli_utils.h"

using namespace std;

auto console = spdlog::stderr_logger_mt("GLnexus");
GLnexus::Status s;
#define H(desc,expr) \
    s = expr; \
    if (s.bad()) { \
        console->error() << "Failed to " << desc << ": " << s.str(); \
        return 1; \
    }

// Perform all the separate GLnexus operations in one go.
// return 0 on success, 1 on failure.
static int all_steps(const vector<string> &vcf_files,
                     const string &bedfilename,
                     const string &config_preset,
                     int nr_threads,
                     bool debug,
                     bool iter_compare,
                     size_t bucket_size) {
    GLnexus::Status s;
    GLnexus::unifier_config unifier_cfg;
    GLnexus::genotyper_config genotyper_cfg;

    if (vcf_files.empty()) {
        console->error() << "No source GVCF files specified";
        return 1;
    }

    H("load unifier/genotyper configuration",
        GLnexus::cli::utils::load_config_preset(console, config_preset, unifier_cfg, genotyper_cfg));

    // initilize empty database
    string dbpath("GLnexus.DB");
    vector<pair<string,size_t> > contigs;
    H("initializing database", GLnexus::cli::utils::db_init(console, dbpath, vcf_files[0], contigs,
                                                            bucket_size));

    {
        // sanity check, see that we can get the contigs back
        vector<pair<string,size_t> > contigs_dbg;
        H("Reading the contigs back from DB",
          GLnexus::cli::utils::db_get_contigs(console, dbpath, contigs_dbg));
        if (contigs_dbg != contigs)
            return GLnexus::Status::Invalid("error, contigs read from DB do not match originals");
    }

    // Load the GVCFs into the database
    {
        // use an empty range filter
        vector<GLnexus::range> ranges;
        H("bulk load into DB",
          GLnexus::cli::utils::db_bulk_load(console, nr_threads, vcf_files, dbpath, ranges, contigs, false));
    }

    if (iter_compare) {
        H("compare database iteration methods",
          GLnexus::cli::utils::compare_db_itertion_algorithms(console, dbpath, 50));
    }

    // discover alleles
    // TODO: if bedfilename is empty, fill ranges with all contigs
    // TODO: overlap allele discovery with final compactions. have db_bulk_load output a RocksKeyValue pointer which we can reuse
    vector<GLnexus::range> ranges;
    H("parsing the bed file", GLnexus::cli::utils::parse_bed_file(console, bedfilename, contigs, ranges));
    GLnexus::discovered_alleles dsals;
    unsigned sample_count = 0;
    H("discover alleles",
      GLnexus::cli::utils::discover_alleles(console, nr_threads, dbpath, ranges, contigs, dsals, sample_count));
    if (debug) {
        string filename("/tmp/dsals.yml");
        console->info() << "Writing discovered alleles as YAML to " << filename;
        H("serialize discovered alleles to a file",
          GLnexus::cli::utils::yaml_write_discovered_alleles_to_file(dsals, contigs, sample_count, filename));

        string filename_cflat("/tmp/dsals.cflat");
        console->info() << "Writing discovered alleles in serialized capnproto to " << filename
                        << ", verifying that it is readable";
        H("Verify that the cflat file is readable",
          GLnexus::capnp_discover_alleles_verify(sample_count, contigs, dsals, filename_cflat));
    }

    // partition dsals by contig to reduce peak memory usage in the unifier
    std::vector<GLnexus::discovered_alleles> dsals_by_contig(contigs.size());
    for (auto p = dsals.begin(); p != dsals.end(); dsals.erase(p++)) {
        UNPAIR(*p, al, dai);
        assert(al.pos.rid >= 0 && al.pos.rid < contigs.size());
        dsals_by_contig[al.pos.rid][al] = dai;
    }

    // unify sites
    // we could parallelize over dsals_by_contig although this might increase memory usage.
    vector<GLnexus::unified_site> sites;
    for (auto& dsals_i : dsals_by_contig) {
        H("unify sites",
          GLnexus::cli::utils::unify_sites(console, unifier_cfg, ranges, contigs, dsals_i, sample_count, sites));
    }
    console->info() << "unified to " << sites.size() << " sites";
    if (debug) {
        string filename("/tmp/sites.yml");
        console->info() << "Writing unified sites as YAML to " << filename;
        H("write unified sites to file",
          GLnexus::cli::utils::write_unified_sites_to_file(sites, contigs, filename));

        string filename_cflat("/tmp/sites.cflat");
        console->info() << "Writing unified sites in serialized capnproto to " << filename
                        << ", verifying that it is readable";
        H("Verify that the cflat file is readable",
          GLnexus::capnp_unified_sites_verify(sites, filename_cflat));
    }

    // genotype
    genotyper_cfg.output_residuals = debug;
    vector<string> hdr_lines = { ("##GLnexusConfigPreset="+config_preset) };
    string outfile("-");
    H("Genotyping",
      GLnexus::cli::utils::genotype(console, nr_threads, dbpath, genotyper_cfg, sites, hdr_lines, outfile));

    return 0;
}

void help(const char* prog) {
    cerr << "usage: " << prog << " [options] /vcf/file/1 .. /vcf/file/N" << endl
         << "Joint genotype all source gVCF files, and generate a project VCF file" << endl
         << "on standard out. The source files must be in GVCF format." << endl
         << "Options:" << endl
         << "  --help, -h           print this help message" << endl
         << "  --config X, -c X     configuration setting" << endl
         << "  --bed FILE, -b FILE  path to three-column BED file" << endl
         << "  --list, -l           given files contain lists of gVCF filenames one per line" << endl
         << "  --bucket_size INT, -x INT  set the bucket size" << endl
         << "  --debug, -d          create additional file outputs for diagnostics/debugging" << endl
         << "  --iter_compare, -i   compare different implementations of database iteration" << endl;
}

// Expected usage:
//    glnexus [vcf files]
//
int main(int argc, char *argv[]) {
    GLnexus::Status s;
    spdlog::set_level(spdlog::level::info);
    spdlog::set_pattern("[%t] %+");
    console->info() << "glnexus_cli " << GIT_REVISION;

    if (argc < 2) {
        help(argv[0]);
        return 1;
    }

    static struct option long_options[] = {
        {"help", no_argument, 0, 'h'},
        {"bed", required_argument, 0, 'b'},
        {"config", required_argument, 0, 'c'},
        {"list", no_argument, 0, 'l'},
        {"bucket_size", required_argument, 0, 'x'},
        {"debug", no_argument, 0, 'd'},
        {"iter_compare", no_argument, 0, 'i'},
        {0, 0, 0, 0}
    };

    int c;
    string config_preset = "test";
    bool list_of_files = false;
    bool debug = false;
    bool iter_compare = false;
    string bedfilename;
    int nr_threads = std::thread::hardware_concurrency();
    size_t bucket_size = GLnexus::BCFKeyValueData::default_bucket_size;

    while (-1 != (c = getopt_long(argc, argv, "hb:dIx:",
                                  long_options, nullptr))) {
        switch (c) {
            case 'b':
                bedfilename = string(optarg);
                if (bedfilename.size() == 0) {
                    cerr <<  "invalid BED filename" << endl;
                    return 1;
                }
                break;

            case 'l':
                list_of_files = true;
                break;

            case 'c':
                config_preset = string(optarg);
                break;

            case 'd':
                debug = true;
                break;

            case 'h':
            case '?':
                help(argv[0]);
                exit(1);
                break;

            case 'i':
                iter_compare = true;
                break;

            case 'x':
                bucket_size = strtoul(optarg, nullptr, 10);
                if (bucket_size == 0 || bucket_size > 1000000000) {
                    cerr << "bucket size should be in (1,1e9]" << endl;
                    return 1;
                }
                break;

            default:
                abort ();
        }
    }

    if (optind > argc-1) {
        help(argv[0]);
        return 1;
    }

    vector<string> vcf_files, vcf_files_precursor;
    for (int i=optind; i < argc; i++) {
        vcf_files_precursor.push_back(string(argv[i]));
    }

    if (list_of_files) {
        for (const string& fn : vcf_files_precursor) {
            string gvcf;
            ifstream infile(fn);
            while (getline(infile, gvcf)) {
                vcf_files.push_back(gvcf);
            }
            if (infile.bad() || !infile.eof()) {
                H("reading input file list", GLnexus::Status::IOError("reading", bedfilename));
            }
        }
    } else {
        vcf_files = vcf_files_precursor;
    }

    return all_steps(vcf_files, bedfilename, config_preset, nr_threads, debug, iter_compare, bucket_size);
}
<commit_msg>record DNAnexus job ID in pVCF header if applicable<commit_after>// Basic GLnexus command-line interface for use on one compute node

#include <iostream>
#include <exception>
#include <fstream>
#include <getopt.h>
#include <sstream>
#include <cstdlib>
#include "vcf.h"
#include "hfile.h"
#include "service.h"
#include "unifier.h"
#include "BCFKeyValueData.h"
#include "RocksKeyValue.h"
#include "ctpl_stl.h"
#include "spdlog/spdlog.h"
#include "cli_utils.h"

using namespace std;

auto console = spdlog::stderr_logger_mt("GLnexus");
GLnexus::Status s;
#define H(desc,expr) \
    s = expr; \
    if (s.bad()) { \
        console->error() << "Failed to " << desc << ": " << s.str(); \
        return 1; \
    }

// Perform all the separate GLnexus operations in one go.
// return 0 on success, 1 on failure.
static int all_steps(const vector<string> &vcf_files,
                     const string &bedfilename,
                     const string &config_preset,
                     int nr_threads,
                     bool debug,
                     bool iter_compare,
                     size_t bucket_size) {
    GLnexus::Status s;
    GLnexus::unifier_config unifier_cfg;
    GLnexus::genotyper_config genotyper_cfg;

    if (vcf_files.empty()) {
        console->error() << "No source GVCF files specified";
        return 1;
    }

    H("load unifier/genotyper configuration",
        GLnexus::cli::utils::load_config_preset(console, config_preset, unifier_cfg, genotyper_cfg));

    // initilize empty database
    string dbpath("GLnexus.DB");
    vector<pair<string,size_t> > contigs;
    H("initializing database", GLnexus::cli::utils::db_init(console, dbpath, vcf_files[0], contigs,
                                                            bucket_size));

    {
        // sanity check, see that we can get the contigs back
        vector<pair<string,size_t> > contigs_dbg;
        H("Reading the contigs back from DB",
          GLnexus::cli::utils::db_get_contigs(console, dbpath, contigs_dbg));
        if (contigs_dbg != contigs)
            return GLnexus::Status::Invalid("error, contigs read from DB do not match originals");
    }

    // Load the GVCFs into the database
    {
        // use an empty range filter
        vector<GLnexus::range> ranges;
        H("bulk load into DB",
          GLnexus::cli::utils::db_bulk_load(console, nr_threads, vcf_files, dbpath, ranges, contigs, false));
    }

    if (iter_compare) {
        H("compare database iteration methods",
          GLnexus::cli::utils::compare_db_itertion_algorithms(console, dbpath, 50));
    }

    // discover alleles
    // TODO: if bedfilename is empty, fill ranges with all contigs
    // TODO: overlap allele discovery with final compactions. have db_bulk_load output a RocksKeyValue pointer which we can reuse
    vector<GLnexus::range> ranges;
    H("parsing the bed file", GLnexus::cli::utils::parse_bed_file(console, bedfilename, contigs, ranges));
    GLnexus::discovered_alleles dsals;
    unsigned sample_count = 0;
    H("discover alleles",
      GLnexus::cli::utils::discover_alleles(console, nr_threads, dbpath, ranges, contigs, dsals, sample_count));
    if (debug) {
        string filename("/tmp/dsals.yml");
        console->info() << "Writing discovered alleles as YAML to " << filename;
        H("serialize discovered alleles to a file",
          GLnexus::cli::utils::yaml_write_discovered_alleles_to_file(dsals, contigs, sample_count, filename));

        string filename_cflat("/tmp/dsals.cflat");
        console->info() << "Writing discovered alleles in serialized capnproto to " << filename
                        << ", verifying that it is readable";
        H("Verify that the cflat file is readable",
          GLnexus::capnp_discover_alleles_verify(sample_count, contigs, dsals, filename_cflat));
    }

    // partition dsals by contig to reduce peak memory usage in the unifier
    std::vector<GLnexus::discovered_alleles> dsals_by_contig(contigs.size());
    for (auto p = dsals.begin(); p != dsals.end(); dsals.erase(p++)) {
        UNPAIR(*p, al, dai);
        assert(al.pos.rid >= 0 && al.pos.rid < contigs.size());
        dsals_by_contig[al.pos.rid][al] = dai;
    }

    // unify sites
    // we could parallelize over dsals_by_contig although this might increase memory usage.
    vector<GLnexus::unified_site> sites;
    for (auto& dsals_i : dsals_by_contig) {
        H("unify sites",
          GLnexus::cli::utils::unify_sites(console, unifier_cfg, ranges, contigs, dsals_i, sample_count, sites));
    }
    console->info() << "unified to " << sites.size() << " sites";
    if (debug) {
        string filename("/tmp/sites.yml");
        console->info() << "Writing unified sites as YAML to " << filename;
        H("write unified sites to file",
          GLnexus::cli::utils::write_unified_sites_to_file(sites, contigs, filename));

        string filename_cflat("/tmp/sites.cflat");
        console->info() << "Writing unified sites in serialized capnproto to " << filename
                        << ", verifying that it is readable";
        H("Verify that the cflat file is readable",
          GLnexus::capnp_unified_sites_verify(sites, filename_cflat));
    }

    // genotype
    genotyper_cfg.output_residuals = debug;
    vector<string> hdr_lines = { ("##GLnexusConfigPreset="+config_preset) };
    auto DX_JOB_ID = std::getenv("DX_JOB_ID");
    if (DX_JOB_ID) {
        // if running in DNAnexus, record job ID in header
        hdr_lines.push_back(string("##DX_JOB_ID=")+DX_JOB_ID);
    }
    string outfile("-");
    H("Genotyping",
      GLnexus::cli::utils::genotype(console, nr_threads, dbpath, genotyper_cfg, sites, hdr_lines, outfile));

    return 0;
}

void help(const char* prog) {
    cerr << "usage: " << prog << " [options] /vcf/file/1 .. /vcf/file/N" << endl
         << "Joint genotype all source gVCF files, and generate a project VCF file" << endl
         << "on standard out. The source files must be in GVCF format." << endl
         << "Options:" << endl
         << "  --help, -h           print this help message" << endl
         << "  --config X, -c X     configuration setting" << endl
         << "  --bed FILE, -b FILE  path to three-column BED file" << endl
         << "  --list, -l           given files contain lists of gVCF filenames one per line" << endl
         << "  --bucket_size INT, -x INT  set the bucket size" << endl
         << "  --debug, -d          create additional file outputs for diagnostics/debugging" << endl
         << "  --iter_compare, -i   compare different implementations of database iteration" << endl;
}

// Expected usage:
//    glnexus [vcf files]
//
int main(int argc, char *argv[]) {
    GLnexus::Status s;
    spdlog::set_level(spdlog::level::info);
    spdlog::set_pattern("[%t] %+");
    console->info() << "glnexus_cli " << GIT_REVISION;

    if (argc < 2) {
        help(argv[0]);
        return 1;
    }

    static struct option long_options[] = {
        {"help", no_argument, 0, 'h'},
        {"bed", required_argument, 0, 'b'},
        {"config", required_argument, 0, 'c'},
        {"list", no_argument, 0, 'l'},
        {"bucket_size", required_argument, 0, 'x'},
        {"debug", no_argument, 0, 'd'},
        {"iter_compare", no_argument, 0, 'i'},
        {0, 0, 0, 0}
    };

    int c;
    string config_preset = "test";
    bool list_of_files = false;
    bool debug = false;
    bool iter_compare = false;
    string bedfilename;
    int nr_threads = std::thread::hardware_concurrency();
    size_t bucket_size = GLnexus::BCFKeyValueData::default_bucket_size;

    while (-1 != (c = getopt_long(argc, argv, "hb:dIx:",
                                  long_options, nullptr))) {
        switch (c) {
            case 'b':
                bedfilename = string(optarg);
                if (bedfilename.size() == 0) {
                    cerr <<  "invalid BED filename" << endl;
                    return 1;
                }
                break;

            case 'l':
                list_of_files = true;
                break;

            case 'c':
                config_preset = string(optarg);
                break;

            case 'd':
                debug = true;
                break;

            case 'h':
            case '?':
                help(argv[0]);
                exit(1);
                break;

            case 'i':
                iter_compare = true;
                break;

            case 'x':
                bucket_size = strtoul(optarg, nullptr, 10);
                if (bucket_size == 0 || bucket_size > 1000000000) {
                    cerr << "bucket size should be in (1,1e9]" << endl;
                    return 1;
                }
                break;

            default:
                abort ();
        }
    }

    if (optind > argc-1) {
        help(argv[0]);
        return 1;
    }

    vector<string> vcf_files, vcf_files_precursor;
    for (int i=optind; i < argc; i++) {
        vcf_files_precursor.push_back(string(argv[i]));
    }

    if (list_of_files) {
        for (const string& fn : vcf_files_precursor) {
            string gvcf;
            ifstream infile(fn);
            while (getline(infile, gvcf)) {
                vcf_files.push_back(gvcf);
            }
            if (infile.bad() || !infile.eof()) {
                H("reading input file list", GLnexus::Status::IOError("reading", bedfilename));
            }
        }
    } else {
        vcf_files = vcf_files_precursor;
    }

    return all_steps(vcf_files, bedfilename, config_preset, nr_threads, debug, iter_compare, bucket_size);
}
<|endoftext|>


<commit_before>/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

#include <iostream>
#include <strstream>
#include <stdio.h>
#include <direct.h>

#if !defined(XALAN_NO_NAMESPACES)
	using std::cerr;
	using std::cout;
	using std::cin;
	using std::endl;
	using std::ifstream;
	using std::ios_base;
	using std::ostrstream;
	using std::string;
#endif

// XERCES HEADERS...
//	Are included by HarnessInit.hpp

// XALAN HEADERS...
//	Are included by FileUtility.hpp

// HARNESS HEADERS...
#include <XMLFileReporter.hpp>
#include <FileUtility.hpp>
#include <HarnessInit.hpp>

#if defined(XALAN_NO_NAMESPACES)
	typedef map<XalanDOMString, XalanDOMString, less<XalanDOMString> >	Hashtable;
#else
	typedef std::map<XalanDOMString, XalanDOMString>  Hashtable;
#endif

// This is here for memory leak testing.
#if !defined(NDEBUG) && defined(_MSC_VER)
#include <crtdbg.h>
#endif

const char* const 	excludeStylesheets[] =
{
	"output22.xsl",
	0
};

FileUtility		futil;

void
printArgOptions()
{
	cerr << endl
		 << "conf dirname [-category -out -gold]"
		 << endl
		 << endl
		 << "dirname		(base directory for testcases)"
		 << endl
		 << "-category dirname (specific directory)"
		 << endl
		 << "-out dirname	(base directory for output)"
		 << endl
		 << "-gold dirname	(base directory for gold files)"
		 << endl;
}

bool
getParams(int argc,
		  const char*	argv[],
		  FileUtility& f,
		  XalanDOMString& baseDir,
		  XalanDOMString& outDir,
		  XalanDOMString& goldRoot,
		  XalanDOMString& category)
{
	bool fSuccess = true;	// Used to continue argument loop
	bool fsetOut = true;	// Set default output directory, set to false if data is provided
	bool fsetGold = true;	// Set default gold directory, set to false if data is provided

	// Insure that required "-base" argument is there.
	if (argc == 1 || argv[1][0] == '-')
	{
		printArgOptions();
		return false;
	}
	else
	{
		if (f.checkDir(pathSep + XalanDOMString(argv[1])))
		{
			assign(baseDir, XalanDOMString(argv[1]));
			insert(baseDir, 0, pathSep);
		}
		else
		{
			cout << endl << "Given base directory \"" << argv[1] << "\" does not exist" << endl;
			printArgOptions();
			return false;
		}
	}

	// Get the rest of the arguments in any order.
	for (int i = 2; i < argc && fSuccess == true; ++i)
	{
		if(!stricmp("-out", argv[i]))
		{
			++i;
			if(i < argc && argv[i][0] != '-')
			{
				assign(outDir, XalanDOMString(argv[i]));
				insert(outDir, 0, XalanDOMString("\\"));
				append(outDir, XalanDOMString("\\"));
				f.checkAndCreateDir(outDir);
				fsetOut = false;
			}
			else
			{
				printArgOptions();
				fSuccess = false;
			}
		}
		else if(!stricmp("-gold", argv[i]))
		{
			++i;
			if(i < argc && argv[i][0] != '-')
			{
				assign(goldRoot, XalanDOMString(argv[i]));
				insert(goldRoot, 0, pathSep);
				fsetGold = false;
			}
			else
			{
				printArgOptions();
				fSuccess = false;
			}
		}
		else if(!stricmp("-category", argv[i]))
		{
			++i;
			if(i < argc && argv[i][0] != '-')
			{
				assign(category, XalanDOMString(argv[i]));
			}
			else
			{
				printArgOptions();
				fSuccess = false;
			}
		}

		else
		{
			printArgOptions();
			fSuccess = false;
		}

	} // End of for-loop

	// Do we need to set the default output directory??
	if (fsetOut)
	{
		unsigned int ii = lastIndexOf(baseDir,charAt(pathSep,0));
		outDir = substring(baseDir, 0, ii+1);
		append(outDir,XalanDOMString("CONF-results"));
		f.checkAndCreateDir(outDir);
		append(outDir,pathSep);
	}
	if (fsetGold)
	{
		goldRoot = baseDir;
		append(goldRoot,XalanDOMString("-gold"));
		f.checkAndCreateDir(goldRoot);
		//append(goldRoot,pathSep);
	}

	// Add the path seperator to the end of the base directory
	// here after we've finished using it for all directory creation.

	append(baseDir,pathSep);
	append(goldRoot,pathSep);
	return fSuccess;
}

inline bool
checkForExclusion(XalanDOMString currentFile)
{
	for (int i=0; excludeStylesheets[i] != 0; i++)
	{
		if (equals(currentFile, XalanDOMString(excludeStylesheets[i])))
		{
			return true;
		}
	}

	return false;
}

int
main(
	 int			argc,
	 const char*	argv[])
{
#if !defined(NDEBUG) && defined(_MSC_VER)
	_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);

	_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
	_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
#endif

	Hashtable runAttrs;	// Attribute lists for conformance testing.

	XalanDOMString  category;	// Test all of base dir by default
	XalanDOMString  baseDir, outputRoot, goldRoot;

	if (getParams(argc, argv, futil, baseDir, outputRoot, goldRoot, category) == true)
	{
		// Call the static initializers for xerces and xalan, and create a transformer
		HarnessInit xmlPlatformUtils;
		XalanTransformer::initialize();
		XalanTransformer xalan;

		// Generate Unique Run id and processor info
		const XalanDOMString& UniqRunid = futil.GenerateUniqRunid();

		// Defined basic constants for file manipulation and open results file
		const XalanDOMString  drive(futil.getDrive());
		const XalanDOMString  resultFilePrefix("cpp");
		const XalanDOMString  resultsFile(drive + outputRoot + resultFilePrefix + UniqRunid + XMLSuffix);

		XMLFileReporter	logFile(resultsFile);
		logFile.logTestFileInit("Conformance Testing:");

		// Get the list of Directories that are below conf
		const FileNameVectorType	dirs = futil.getDirectoryNames(baseDir);

		for(FileNameVectorType::size_type	j = 0; j < dirs.size(); ++j)
		{
			const XalanDOMString currentDir(dirs[j]);

			// Run specific category of files from given directory
			if (length(category) > 0 && !equals(currentDir, category))
			{
				continue;
			}

			// Check that output directory is there.
			const XalanDOMString  theOutputDir = outputRoot + currentDir;
			futil.checkAndCreateDir(theOutputDir);

			// Get the files found in the test directory
			const FileNameVectorType files = futil.getTestFileNames(baseDir, currentDir, true);

			logFile.logTestCaseInit(currentDir);

			for(FileNameVectorType::size_type i = 0; i < files.size(); i++)
			{
				Hashtable attrs;
				const XalanDOMString currentFile(files[i]);
				futil.data.testOrFile = currentFile;

				if (checkForExclusion(currentFile))
					continue;

				const XalanDOMString  theXSLFile= baseDir + currentDir + pathSep + currentFile;
				const XalanDOMString  theXMLFile = futil.GenerateFileName(theXSLFile,"xml");
				futil.data.xmlFileURL = theXMLFile;
				futil.data.xslFileURL = theXSLFile;

				XalanDOMString  theGoldFile = goldRoot + currentDir + pathSep + currentFile;
				theGoldFile = futil.GenerateFileName(theGoldFile, "out");

				const XalanDOMString  outbase =  outputRoot + currentDir + pathSep + currentFile;
				const XalanDOMString  theOutputFile = futil.GenerateFileName(outbase, "out");

				const XSLTInputSource	xslInputSource(c_wstr(theXSLFile));
				const XSLTInputSource	xmlInputSource(c_wstr(theXMLFile));
				const XSLTResultTarget	resultFile(theOutputFile);

				//
				// Parsing(compile) the XSL stylesheet and report the results..
				//
				const XalanCompiledStylesheet*	compiledSS = 0;
				xalan.compileStylesheet(xslInputSource, compiledSS);
				if (compiledSS == 0 )
				{
					// Report the failure and be sure to increment fail count.
					cout << "Failed to PARSE stylesheet for " << currentFile << endl;
					futil.data.fail += 1;
					logFile.logErrorResult(currentFile, XalanDOMString("Failed to PARSE stylesheet."));
					continue;
				}

				//
				// Parsing the input XML and report the results..
				//
				const XalanParsedSource*	parsedSource = 0;
				xalan.parseSource(xmlInputSource, parsedSource);
				if (parsedSource == 0)
				{
					// Report the failure and be sure to increment fail count.
					cout << "Failed to PARSE source document for " << currentFile << endl;
					futil.data.fail += 1;
					logFile.logErrorResult(currentFile, XalanDOMString("Failed to PARSE source document."));
					continue;
				}

				//
				// Perform One transform using parsed stylesheet and parsed xml source, report results...
				//
				xalan.transform(*parsedSource, compiledSS, resultFile);

				futil.checkResults(theOutputFile,
								  theGoldFile,
								  logFile);

				xalan.destroyParsedSource(parsedSource);
				xalan.destroyStylesheet(compiledSS);

			}	//for files

			logFile.logTestCaseClose("Done", "Pass");

		}		//for directories

		futil.reportPassFail(logFile, UniqRunid);
		logFile.logTestFileClose("Conformance ", "Done");
		logFile.close();

		futil.analyzeResults(xalan, resultsFile);

	} //if getParams

	XalanTransformer::terminate();

	return 0;
}
<commit_msg>Updated call to analyzeResults for new signature.<commit_after>/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

#include <iostream>
#include <strstream>
#include <stdio.h>
#include <direct.h>

#if !defined(XALAN_NO_NAMESPACES)
	using std::cerr;
	using std::cout;
	using std::cin;
	using std::endl;
	using std::ifstream;
	using std::ios_base;
	using std::ostrstream;
	using std::string;
#endif

// XERCES HEADERS...
//	Are included by HarnessInit.hpp

// XALAN HEADERS...
//	Are included by FileUtility.hpp

// HARNESS HEADERS...
#include <XMLFileReporter.hpp>
#include <FileUtility.hpp>
#include <HarnessInit.hpp>

#if defined(XALAN_NO_NAMESPACES)
	typedef map<XalanDOMString, XalanDOMString, less<XalanDOMString> >	Hashtable;
#else
	typedef std::map<XalanDOMString, XalanDOMString>  Hashtable;
#endif

// This is here for memory leak testing.
#if !defined(NDEBUG) && defined(_MSC_VER)
#include <crtdbg.h>
#endif

const char* const 	excludeStylesheets[] =
{
	"output22.xsl",
	0
};

FileUtility		futil;

void
printArgOptions()
{
	cerr << endl
		 << "conf dirname [-category -out -gold]"
		 << endl
		 << endl
		 << "dirname		(base directory for testcases)"
		 << endl
		 << "-category dirname (specific directory)"
		 << endl
		 << "-out dirname	(base directory for output)"
		 << endl
		 << "-gold dirname	(base directory for gold files)"
		 << endl;
}

bool
getParams(int argc,
		  const char*	argv[],
		  FileUtility& f,
		  XalanDOMString& baseDir,
		  XalanDOMString& outDir,
		  XalanDOMString& goldRoot,
		  XalanDOMString& category)
{
	bool fSuccess = true;	// Used to continue argument loop
	bool fsetOut = true;	// Set default output directory, set to false if data is provided
	bool fsetGold = true;	// Set default gold directory, set to false if data is provided

	// Insure that required "-base" argument is there.
	if (argc == 1 || argv[1][0] == '-')
	{
		printArgOptions();
		return false;
	}
	else
	{
		if (f.checkDir(pathSep + XalanDOMString(argv[1])))
		{
			assign(baseDir, XalanDOMString(argv[1]));
			insert(baseDir, 0, pathSep);
		}
		else
		{
			cout << endl << "Given base directory \"" << argv[1] << "\" does not exist" << endl;
			printArgOptions();
			return false;
		}
	}

	// Get the rest of the arguments in any order.
	for (int i = 2; i < argc && fSuccess == true; ++i)
	{
		if(!stricmp("-out", argv[i]))
		{
			++i;
			if(i < argc && argv[i][0] != '-')
			{
				assign(outDir, XalanDOMString(argv[i]));
				insert(outDir, 0, XalanDOMString("\\"));
				append(outDir, XalanDOMString("\\"));
				f.checkAndCreateDir(outDir);
				fsetOut = false;
			}
			else
			{
				printArgOptions();
				fSuccess = false;
			}
		}
		else if(!stricmp("-gold", argv[i]))
		{
			++i;
			if(i < argc && argv[i][0] != '-')
			{
				assign(goldRoot, XalanDOMString(argv[i]));
				insert(goldRoot, 0, pathSep);
				fsetGold = false;
			}
			else
			{
				printArgOptions();
				fSuccess = false;
			}
		}
		else if(!stricmp("-category", argv[i]))
		{
			++i;
			if(i < argc && argv[i][0] != '-')
			{
				assign(category, XalanDOMString(argv[i]));
			}
			else
			{
				printArgOptions();
				fSuccess = false;
			}
		}

		else
		{
			printArgOptions();
			fSuccess = false;
		}

	} // End of for-loop

	// Do we need to set the default output directory??
	if (fsetOut)
	{
		unsigned int ii = lastIndexOf(baseDir,charAt(pathSep,0));
		outDir = substring(baseDir, 0, ii+1);
		append(outDir,XalanDOMString("CONF-results"));
		f.checkAndCreateDir(outDir);
		append(outDir,pathSep);
	}
	if (fsetGold)
	{
		goldRoot = baseDir;
		append(goldRoot,XalanDOMString("-gold"));
		f.checkAndCreateDir(goldRoot);
		//append(goldRoot,pathSep);
	}

	// Add the path seperator to the end of the base directory
	// here after we've finished using it for all directory creation.

	append(baseDir,pathSep);
	append(goldRoot,pathSep);
	return fSuccess;
}

inline bool
checkForExclusion(XalanDOMString currentFile)
{
	for (int i=0; excludeStylesheets[i] != 0; i++)
	{
		if (equals(currentFile, XalanDOMString(excludeStylesheets[i])))
		{
			return true;
		}
	}

	return false;
}

int
main(
	 int			argc,
	 const char*	argv[])
{
#if !defined(NDEBUG) && defined(_MSC_VER)
	_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);

	_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
	_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
#endif

	Hashtable runAttrs;	// Attribute lists for conformance testing.

	XalanDOMString  category;	// Test all of base dir by default
	XalanDOMString  baseDir, outputRoot, goldRoot;

	if (getParams(argc, argv, futil, baseDir, outputRoot, goldRoot, category) == true)
	{
		// Call the static initializers for xerces and xalan, and create a transformer
		HarnessInit xmlPlatformUtils;
		XalanTransformer::initialize();
		XalanTransformer xalan;

		// Generate Unique Run id and processor info
		const XalanDOMString& UniqRunid = futil.generateUniqRunid();

		// Defined basic constants for file manipulation and open results file
		const XalanDOMString  drive(futil.getDrive());
		const XalanDOMString  resultFilePrefix("cpp");
		const XalanDOMString  resultsFile(drive + outputRoot + resultFilePrefix + UniqRunid + XMLSuffix);

		XMLFileReporter	logFile(resultsFile);
		logFile.logTestFileInit("Conformance Testing:");

		// Get the list of Directories that are below conf
		const FileNameVectorType	dirs = futil.getDirectoryNames(baseDir);

		for(FileNameVectorType::size_type	j = 0; j < dirs.size(); ++j)
		{
			const XalanDOMString currentDir(dirs[j]);

			// Run specific category of files from given directory
			if (length(category) > 0 && !equals(currentDir, category))
			{
				continue;
			}

			// Check that output directory is there.
			const XalanDOMString  theOutputDir = outputRoot + currentDir;
			futil.checkAndCreateDir(theOutputDir);

			// Get the files found in the test directory
			const FileNameVectorType files = futil.getTestFileNames(baseDir, currentDir, true);

			logFile.logTestCaseInit(currentDir);

			for(FileNameVectorType::size_type i = 0; i < files.size(); i++)
			{
				Hashtable attrs;
				const XalanDOMString currentFile(files[i]);
				futil.data.testOrFile = currentFile;

				if (checkForExclusion(currentFile))
					continue;

				const XalanDOMString  theXSLFile = baseDir + currentDir + pathSep + currentFile;
				const XalanDOMString  theXMLFile = futil.generateFileName(theXSLFile,"xml");
				futil.data.xmlFileURL = theXMLFile;
				futil.data.xslFileURL = theXSLFile;

				XalanDOMString  theGoldFile = goldRoot + currentDir + pathSep + currentFile;
				theGoldFile = futil.generateFileName(theGoldFile, "out");

				const XalanDOMString  outbase =  outputRoot + currentDir + pathSep + currentFile;
				const XalanDOMString  theOutputFile = futil.generateFileName(outbase, "out");

				const XSLTInputSource	xslInputSource(c_wstr(theXSLFile));
				const XSLTInputSource	xmlInputSource(c_wstr(theXMLFile));
				const XSLTResultTarget	resultFile(theOutputFile);

				//
				// Parsing(compile) the XSL stylesheet and report the results..
				//
				const XalanCompiledStylesheet*	compiledSS = 0;
				xalan.compileStylesheet(xslInputSource, compiledSS);
				if (compiledSS == 0 )
				{
					// Report the failure and be sure to increment fail count.
					cout << "Failed to PARSE stylesheet for " << currentFile << endl;
					futil.data.fail += 1;
					logFile.logErrorResult(currentFile, XalanDOMString("Failed to PARSE stylesheet."));
					continue;
				}

				//
				// Parsing the input XML and report the results..
				//
				const XalanParsedSource*	parsedSource = 0;
				xalan.parseSource(xmlInputSource, parsedSource);
				if (parsedSource == 0)
				{
					// Report the failure and be sure to increment fail count.
					cout << "Failed to PARSE source document for " << currentFile << endl;
					futil.data.fail += 1;
					logFile.logErrorResult(currentFile, XalanDOMString("Failed to PARSE source document."));
					continue;
				}

				//
				// Perform One transform using parsed stylesheet and parsed xml source, report results...
				//
				xalan.transform(*parsedSource, compiledSS, resultFile);

				futil.checkResults(theOutputFile,
								  theGoldFile,
								  logFile);

				xalan.destroyParsedSource(parsedSource);
				xalan.destroyStylesheet(compiledSS);

			}	//for files

			logFile.logTestCaseClose("Done", "Pass");

		}		//for directories

		futil.reportPassFail(logFile, UniqRunid);
		logFile.logTestFileClose("Conformance ", "Done");
		logFile.close();

		futil.analyzeResults(xalan, baseDir, resultsFile);

	} //if getParams

	XalanTransformer::terminate();

	return 0;
}
<|endoftext|>


<commit_before>/* -*- coding: utf-8; mode: c++; tab-width: 3; indent-tabs-mode: nil -*-

Copyright 2015-2016 Raffaello D. Di Napoli

This file is part of Abaclade.

Abaclade 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.

Abaclade 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 Abaclade. If
not, see <http://www.gnu.org/licenses/>.
--------------------------------------------------------------------------------------------------*/

#include <abaclade.hxx>
#include <abaclade/coroutine.hxx>
#include <abaclade/net/tcp.hxx>
#include <abaclade/thread.hxx>

#if ABC_HOST_API_POSIX
   #include <arpa/inet.h> // inet_addr()
   #include <errno.h> // EINTR errno
   #include <sys/types.h> // sockaddr sockaddr_in
   #include <sys/socket.h> // accept4() bind() socket()
   #if ABC_HOST_API_FREEBSD
      #include <netinet/in.h>
   #endif
#elif ABC_HOST_API_WIN32
   #include <winsock2.h>
   #include <mswsock.h> // AcceptEx() GetAcceptExSockaddrs()
   #if ABC_HOST_CXX_MSC
      // Silence warnings from system header files.
      #pragma warning(push)

      // “'id' : conversion from 'type1' to 'type2', signed / unsigned mismatch”
      #pragma warning(disable: 4365)
   #endif
   #include <ws2tcpip.h>
   #include <mstcpip.h>
   #if ABC_HOST_CXX_MSC
      #pragma warning(pop)
   #endif
   #if _WIN32_WINNT == 0x0500
      // Additional header required for Windows 2000 IPv6 Tech Preview.
      #include <tpipv6.h>
   #endif
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////

namespace abc { namespace net { namespace tcp {

connection::connection(io::filedesc fd, ip::address && addrRemote, ip::port && portRemote) :
   m_bfrw(io::binary::make_readwriter(_std::move(fd))),
   m_addrRemote(_std::move(addrRemote)),
   m_portRemote(_std::move(portRemote)) {
}

connection::~connection() {
}

}}} //namespace abc::net::tcp

////////////////////////////////////////////////////////////////////////////////////////////////////

namespace abc { namespace net { namespace tcp {

server::server(ip::address const & addr, ip::port const & port, unsigned cBacklog /*= 5*/) :
   m_fdSocket(create_socket(addr.version())),
   m_ipversion(addr.version()) {
   ABC_TRACE_FUNC(this/*, addr, port*/, cBacklog);

#if ABC_HOST_API_POSIX
   typedef ::sockaddr sockaddr_type;
   typedef ::socklen_t socklen_type;
#elif ABC_HOST_API_WIN32
   typedef ::SOCKADDR sockaddr_type;
   typedef int socklen_type;
#else
   #error "TODO: HOST_API"
#endif
   sockaddr_type * psaServer;
   socklen_type cbServer;
   ::sockaddr_in saServer4;
   ::sockaddr_in6 saServer6;
   switch (m_ipversion.base()) {
      case ip::version::v4:
         psaServer = reinterpret_cast<sockaddr_type *>(&saServer4);
         cbServer = sizeof saServer4;
         memory::clear(&saServer4);
         saServer4.sin_family = AF_INET;
         memory::copy(
            reinterpret_cast<std::uint8_t *>(&saServer4.sin_addr.s_addr),
            addr.raw(),
            sizeof(ip::address::v4_type)
         );
         saServer4.sin_addr.s_addr = htonl(saServer4.sin_addr.s_addr);
         saServer4.sin_port = htons(port.number());
         break;
      case ip::version::v6:
         psaServer = reinterpret_cast<sockaddr_type *>(&saServer6);
         cbServer = sizeof saServer6;
         memory::clear(&saServer6);
         //saServer6.sin6_flowinfo = 0;
         saServer6.sin6_family = AF_INET6;
         memory::copy(saServer6.sin6_addr.s6_addr, addr.raw(), sizeof(ip::address::v6_type));
         saServer6.sin6_port = htons(port.number());
         break;
      ABC_SWITCH_WITHOUT_DEFAULT
   }
#if ABC_HOST_API_WIN32
   if (::bind(reinterpret_cast< ::SOCKET>(m_fdSocket.get()), psaServer, cbServer) < 0) {
      exception::throw_os_error(static_cast<errint_t>(::WSAGetLastError()));
   }
   if (::listen(reinterpret_cast< ::SOCKET>(m_fdSocket.get()), static_cast<int>(cBacklog)) < 0) {
      exception::throw_os_error(static_cast<errint_t>(::WSAGetLastError()));
   }
#else
   if (::bind(m_fdSocket.get(), psaServer, cbServer) < 0) {
      exception::throw_os_error();
   }
   if (::listen(m_fdSocket.get(), static_cast<int>(cBacklog)) < 0) {
      exception::throw_os_error();
   }
#endif
}

server::~server() {
#if ABC_HOST_API_WIN32
   ::WSACleanup();
#endif
}

_std::shared_ptr<connection> server::accept() {
   ABC_TRACE_FUNC(this);

   io::filedesc fdConnection;
   union sockaddr_any {
      ::sockaddr_in sa4;
      ::sockaddr_in6 sa6;
   };
   sockaddr_any * psaaRemote;
#if ABC_HOST_API_POSIX
   bool bAsync = (this_thread::coroutine_scheduler() != nullptr);
   ::socklen_t cbRemoteSockAddr;
   sockaddr_any saaRemote;
   psaaRemote = &saaRemote;
   switch (m_ipversion.base()) {
      case ip::version::v4:
         cbRemoteSockAddr = sizeof saaRemote.sa4;
         break;
      case ip::version::v6:
         cbRemoteSockAddr = sizeof saaRemote.sa6;
         break;
      ABC_SWITCH_WITHOUT_DEFAULT
   }
   for (;;) {
      ::socklen_t cb = cbRemoteSockAddr;
   #if ABC_HOST_API_DARWIN
      // accept4() is not available, so emulate it with accept() + fcntl().
      fdConnection = io::filedesc(
         ::accept(m_fdSocket.get(), reinterpret_cast< ::sockaddr *>(&saaRemote), &cb)
      );
      if (fdConnection) {
         /* Note that at this point there’s no hack that will ensure a fork()/exec() from another
         thread won’t leak the file descriptor. That’s the whole point of accept4(). */
         fdConnection.set_close_on_exec(true);
         if (bAsync) {
            fdConnection.set_nonblocking(true);
         }
      }
   #else
      int iFlags = SOCK_CLOEXEC;
      if (bAsync) {
         // Using coroutines, so make the client socket non-blocking.
         iFlags |= SOCK_NONBLOCK;
      }
      fdConnection = io::filedesc(
         ::accept4(m_fdSocket.get(), reinterpret_cast< ::sockaddr *>(&saaRemote), &cb, iFlags)
      );
   #endif
      if (fdConnection) {
         cbRemoteSockAddr = cb;
         break;
      }
      int iErr = errno;
      switch (iErr) {
         case EINTR:
            this_coroutine::interruption_point();
            break;
         case EAGAIN:
   #if EWOULDBLOCK != EAGAIN
         case EWOULDBLOCK:
   #endif
            // Wait for m_fdSocket. Accepting a connection is considered a read event.
            this_coroutine::sleep_until_fd_ready(m_fdSocket.get(), false);
            break;
         default:
            exception::throw_os_error(static_cast<errint_t>(iErr));
      }
   }
#elif ABC_HOST_API_WIN32
   // This weird structure is what ::AcceptEx() expects.
   static ::DWORD const sc_cbSockAddrBuf = sizeof(sockaddr_any) + 16;
   std::int8_t abBuf[sc_cbSockAddrBuf * 2];

   fdConnection = create_socket(m_ipversion);
   ::DWORD cbRead;
   io::overlapped ovl;
   ovl.Offset = 0;
   ovl.OffsetHigh = 0;
   m_fdSocket.bind_to_this_coroutine_scheduler_iocp();
   if (!::AcceptEx(
      reinterpret_cast< ::SOCKET>(m_fdSocket.get()),
      reinterpret_cast< ::SOCKET>(fdConnection.get()),
      abBuf, 0 /*don’t wait for data, just wait for a connection*/,
      sc_cbSockAddrBuf, sc_cbSockAddrBuf, &cbRead, &ovl
   )) {
      ::DWORD iErr = static_cast< ::DWORD>(::WSAGetLastError());
      if (iErr == ERROR_IO_PENDING) {
         this_coroutine::sleep_until_fd_ready(m_fdSocket.get(), false, &ovl);
         iErr = ovl.status();
         cbRead = ovl.transferred_size();
      }
      if (iErr != ERROR_SUCCESS) {
         exception::throw_os_error(iErr);
      }
   }

   // Parse the weird buffer.
   sockaddr_any * psaaLocal;
   int cbRemoteSockAddr, cbLocalSockAddr;
   ::GetAcceptExSockaddrs(
      abBuf, 0, sc_cbSockAddrBuf, sc_cbSockAddrBuf,
      reinterpret_cast< ::SOCKADDR **>(&psaaLocal), &cbLocalSockAddr,
      reinterpret_cast< ::SOCKADDR **>(&psaaRemote), &cbRemoteSockAddr
   );
#else
   #error "TODO: HOST_API"
#endif
   this_coroutine::interruption_point();

   ip::address addrRemote;
   ip::port portRemote;
   switch (m_ipversion.base()) {
      case ip::version::v4:
         if (cbRemoteSockAddr == sizeof(sockaddr_any::sa4)) {
            addrRemote = ip::address(
               ntohl(*reinterpret_cast<ip::address::v4_type *>(&psaaRemote->sa4.sin_addr.s_addr))
            );
            portRemote = ip::port(ntohs(psaaRemote->sa4.sin_port));
         }
         break;
      case ip::version::v6:
         if (cbRemoteSockAddr == sizeof(sockaddr_any::sa6)) {
            addrRemote = ip::address(
               *reinterpret_cast<ip::address::v6_type *>(&psaaRemote->sa6.sin6_addr.s6_addr)
            );
            portRemote = ip::port(ntohs(psaaRemote->sa6.sin6_port));
         }
         break;
      ABC_SWITCH_WITHOUT_DEFAULT
   }
   return _std::make_shared<connection>(
      _std::move(fdConnection), _std::move(addrRemote), _std::move(portRemote)
   );
}

/*static*/ io::filedesc server::create_socket(ip::version ipversion) {
   ABC_TRACE_FUNC(ipversion);

   if (ipversion == ip::version::any) {
      // TODO: provide more information in the exception.
      ABC_THROW(domain_error, ());
   }
   bool bAsync = (this_thread::coroutine_scheduler() != nullptr);
   int iFamily;
   switch (ipversion.base()) {
      case ip::version::v4:
         iFamily = AF_INET;
         break;
      case ip::version::v6:
         iFamily = AF_INET6;
         break;
      ABC_SWITCH_WITHOUT_DEFAULT
   }
   int iType = SOCK_STREAM;
#if ABC_HOST_API_POSIX
   #if !ABC_HOST_API_DARWIN
      iType |= SOCK_CLOEXEC;
      if (bAsync) {
         // Using coroutines, so make this socket non-blocking.
         iType |= SOCK_NONBLOCK;
      }
   #endif
   io::filedesc fd(::socket(iFamily, iType, 0));
   if (!fd) {
      exception::throw_os_error();
   }
   #if ABC_HOST_API_DARWIN
      /* Note that at this point there’s no hack that will ensure a fork()/exec() from another
      thread won’t leak the file descriptor. That’s the whole point of the extra SOCK_* flags. */
      fd.set_close_on_exec(true);
      if (bAsync) {
         fd.set_nonblocking(true);
      }
   #endif
   return _std::move(fd);
#elif ABC_HOST_API_WIN32 //if ABC_HOST_API_POSIX
   static std::uint8_t const sc_iWSAMajorVersion = 2, sc_iWSAMinorVersion = 2;
   ::WSADATA wsad;
   if (int iRet = ::WSAStartup(MAKEWORD(sc_iWSAMajorVersion, sc_iWSAMinorVersion), &wsad)) {
      exception::throw_os_error(static_cast<errint_t>(iRet));
   }
   if (
      LOBYTE(wsad.wVersion) != sc_iWSAMajorVersion || HIBYTE(wsad.wVersion) != sc_iWSAMinorVersion
   ) {
      // The loaded WinSock implementation does not support the requested version.
      ::WSACleanup();
      // TODO: use a better exception class.
      ABC_THROW(generic_error, ());
   }

   ::DWORD iFlags = 0;
   if (bAsync) {
      iFlags |= WSA_FLAG_OVERLAPPED;
   }
   #ifdef WSA_FLAG_NO_HANDLE_INHERIT
      iFlags |= WSA_FLAG_NO_HANDLE_INHERIT;
   #endif
   ::SOCKET sock = ::WSASocket(iFamily, iType, 0, nullptr, 0, iFlags);
   if (sock == INVALID_SOCKET) {
      exception::throw_os_error();
   }
   return io::filedesc(reinterpret_cast<io::filedesc_t>(sock));
#else //if ABC_HOST_API_POSIX … elif ABC_HOST_API_WIN32
   #error "TODO: HOST_API"
#endif //if ABC_HOST_API_POSIX … elif ABC_HOST_API_WIN32 … else
}

}}} //namespace abc::net::tcp
<commit_msg>Refactor and simplify abc::net::tcp::server::server()<commit_after>/* -*- coding: utf-8; mode: c++; tab-width: 3; indent-tabs-mode: nil -*-

Copyright 2015-2016 Raffaello D. Di Napoli

This file is part of Abaclade.

Abaclade 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.

Abaclade 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 Abaclade. If
not, see <http://www.gnu.org/licenses/>.
--------------------------------------------------------------------------------------------------*/

#include <abaclade.hxx>
#include <abaclade/coroutine.hxx>
#include <abaclade/net/tcp.hxx>
#include <abaclade/thread.hxx>

#if ABC_HOST_API_POSIX
   #include <arpa/inet.h> // inet_addr()
   #include <errno.h> // EINTR errno
   #include <sys/types.h> // sockaddr sockaddr_in
   #include <sys/socket.h> // accept4() bind() socket()
   #if ABC_HOST_API_FREEBSD
      #include <netinet/in.h>
   #endif
#elif ABC_HOST_API_WIN32
   #include <winsock2.h>
   #include <mswsock.h> // AcceptEx() GetAcceptExSockaddrs()
   #if ABC_HOST_CXX_MSC
      // Silence warnings from system header files.
      #pragma warning(push)

      // “'id' : conversion from 'type1' to 'type2', signed / unsigned mismatch”
      #pragma warning(disable: 4365)
   #endif
   #include <ws2tcpip.h>
   #include <mstcpip.h>
   #if ABC_HOST_CXX_MSC
      #pragma warning(pop)
   #endif
   #if _WIN32_WINNT == 0x0500
      // Additional header required for Windows 2000 IPv6 Tech Preview.
      #include <tpipv6.h>
   #endif
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////

namespace abc { namespace net { namespace tcp {

connection::connection(io::filedesc fd, ip::address && addrRemote, ip::port && portRemote) :
   m_bfrw(io::binary::make_readwriter(_std::move(fd))),
   m_addrRemote(_std::move(addrRemote)),
   m_portRemote(_std::move(portRemote)) {
}

connection::~connection() {
}

}}} //namespace abc::net::tcp

////////////////////////////////////////////////////////////////////////////////////////////////////

namespace abc { namespace net { namespace tcp {

union sockaddr_any {
   ::sockaddr_in sa4;
   ::sockaddr_in6 sa6;
};

server::server(ip::address const & addr, ip::port const & port, unsigned cBacklog /*= 5*/) :
   m_fdSocket(create_socket(addr.version())),
   m_ipversion(addr.version()) {
   ABC_TRACE_FUNC(this/*, addr, port*/, cBacklog);

#if ABC_HOST_API_POSIX
   ::socklen_t cbServerSockAddr;
#elif ABC_HOST_API_WIN32
   int cbServerSockAddr;
#else
   #error "TODO: HOST_API"
#endif
   sockaddr_any saaServer;
   switch (m_ipversion.base()) {
      case ip::version::v4:
         cbServerSockAddr = sizeof saaServer.sa4;
         memory::clear(&saaServer.sa4);
         saaServer.sa4.sin_family = AF_INET;
         memory::copy(
            reinterpret_cast<std::uint8_t *>(&saaServer.sa4.sin_addr.s_addr),
            addr.raw(),
            sizeof(ip::address::v4_type)
         );
         saaServer.sa4.sin_addr.s_addr = htonl(saaServer.sa4.sin_addr.s_addr);
         saaServer.sa4.sin_port = htons(port.number());
         break;
      case ip::version::v6:
         cbServerSockAddr = sizeof saaServer.sa6;
         memory::clear(&saaServer.sa6);
         //saaServer.sa6.sin6_flowinfo = 0;
         saaServer.sa6.sin6_family = AF_INET6;
         memory::copy(saaServer.sa6.sin6_addr.s6_addr, addr.raw(), sizeof(ip::address::v6_type));
         saaServer.sa6.sin6_port = htons(port.number());
         break;
      ABC_SWITCH_WITHOUT_DEFAULT
   }
#if ABC_HOST_API_WIN32
   if (
      ::bind(
         reinterpret_cast< ::SOCKET>(m_fdSocket.get()),
         reinterpret_cast< ::SOCKADDR *>(&saaServer), cbServerSockAddr
      ) < 0 ||
      ::listen(reinterpret_cast< ::SOCKET>(m_fdSocket.get()), static_cast<int>(cBacklog)) < 0
   ) {
      exception::throw_os_error(static_cast<errint_t>(::WSAGetLastError()));
   }
#else
   if (
      ::bind(m_fdSocket.get(), reinterpret_cast< ::sockaddr *>(&saaServer), cbServerSockAddr) < 0 ||
      ::listen(m_fdSocket.get(), static_cast<int>(cBacklog)) < 0
   ) {
      exception::throw_os_error();
   }
#endif
}

server::~server() {
#if ABC_HOST_API_WIN32
   ::WSACleanup();
#endif
}

_std::shared_ptr<connection> server::accept() {
   ABC_TRACE_FUNC(this);

   io::filedesc fdConnection;
   sockaddr_any * psaaRemote;
#if ABC_HOST_API_POSIX
   bool bAsync = (this_thread::coroutine_scheduler() != nullptr);
   ::socklen_t cbRemoteSockAddr;
   sockaddr_any saaRemote;
   psaaRemote = &saaRemote;
   switch (m_ipversion.base()) {
      case ip::version::v4:
         cbRemoteSockAddr = sizeof saaRemote.sa4;
         break;
      case ip::version::v6:
         cbRemoteSockAddr = sizeof saaRemote.sa6;
         break;
      ABC_SWITCH_WITHOUT_DEFAULT
   }
   for (;;) {
      ::socklen_t cb = cbRemoteSockAddr;
   #if ABC_HOST_API_DARWIN
      // accept4() is not available, so emulate it with accept() + fcntl().
      fdConnection = io::filedesc(
         ::accept(m_fdSocket.get(), reinterpret_cast< ::sockaddr *>(&saaRemote), &cb)
      );
      if (fdConnection) {
         /* Note that at this point there’s no hack that will ensure a fork()/exec() from another
         thread won’t leak the file descriptor. That’s the whole point of accept4(). */
         fdConnection.set_close_on_exec(true);
         if (bAsync) {
            fdConnection.set_nonblocking(true);
         }
      }
   #else
      int iFlags = SOCK_CLOEXEC;
      if (bAsync) {
         // Using coroutines, so make the client socket non-blocking.
         iFlags |= SOCK_NONBLOCK;
      }
      fdConnection = io::filedesc(
         ::accept4(m_fdSocket.get(), reinterpret_cast< ::sockaddr *>(&saaRemote), &cb, iFlags)
      );
   #endif
      if (fdConnection) {
         cbRemoteSockAddr = cb;
         break;
      }
      int iErr = errno;
      switch (iErr) {
         case EINTR:
            this_coroutine::interruption_point();
            break;
         case EAGAIN:
   #if EWOULDBLOCK != EAGAIN
         case EWOULDBLOCK:
   #endif
            // Wait for m_fdSocket. Accepting a connection is considered a read event.
            this_coroutine::sleep_until_fd_ready(m_fdSocket.get(), false);
            break;
         default:
            exception::throw_os_error(static_cast<errint_t>(iErr));
      }
   }
#elif ABC_HOST_API_WIN32
   // This weird structure is what ::AcceptEx() expects.
   static ::DWORD const sc_cbSockAddrBuf = sizeof(sockaddr_any) + 16;
   std::int8_t abBuf[sc_cbSockAddrBuf * 2];

   fdConnection = create_socket(m_ipversion);
   ::DWORD cbRead;
   io::overlapped ovl;
   ovl.Offset = 0;
   ovl.OffsetHigh = 0;
   m_fdSocket.bind_to_this_coroutine_scheduler_iocp();
   if (!::AcceptEx(
      reinterpret_cast< ::SOCKET>(m_fdSocket.get()),
      reinterpret_cast< ::SOCKET>(fdConnection.get()),
      abBuf, 0 /*don’t wait for data, just wait for a connection*/,
      sc_cbSockAddrBuf, sc_cbSockAddrBuf, &cbRead, &ovl
   )) {
      ::DWORD iErr = static_cast< ::DWORD>(::WSAGetLastError());
      if (iErr == ERROR_IO_PENDING) {
         this_coroutine::sleep_until_fd_ready(m_fdSocket.get(), false, &ovl);
         iErr = ovl.status();
         cbRead = ovl.transferred_size();
      }
      if (iErr != ERROR_SUCCESS) {
         exception::throw_os_error(iErr);
      }
   }

   // Parse the weird buffer.
   sockaddr_any * psaaLocal;
   int cbRemoteSockAddr, cbLocalSockAddr;
   ::GetAcceptExSockaddrs(
      abBuf, 0, sc_cbSockAddrBuf, sc_cbSockAddrBuf,
      reinterpret_cast< ::SOCKADDR **>(&psaaLocal), &cbLocalSockAddr,
      reinterpret_cast< ::SOCKADDR **>(&psaaRemote), &cbRemoteSockAddr
   );
#else
   #error "TODO: HOST_API"
#endif
   this_coroutine::interruption_point();

   ip::address addrRemote;
   ip::port portRemote;
   switch (m_ipversion.base()) {
      case ip::version::v4:
         if (cbRemoteSockAddr == sizeof(sockaddr_any::sa4)) {
            addrRemote = ip::address(
               ntohl(*reinterpret_cast<ip::address::v4_type *>(&psaaRemote->sa4.sin_addr.s_addr))
            );
            portRemote = ip::port(ntohs(psaaRemote->sa4.sin_port));
         }
         break;
      case ip::version::v6:
         if (cbRemoteSockAddr == sizeof(sockaddr_any::sa6)) {
            addrRemote = ip::address(
               *reinterpret_cast<ip::address::v6_type *>(&psaaRemote->sa6.sin6_addr.s6_addr)
            );
            portRemote = ip::port(ntohs(psaaRemote->sa6.sin6_port));
         }
         break;
      ABC_SWITCH_WITHOUT_DEFAULT
   }
   return _std::make_shared<connection>(
      _std::move(fdConnection), _std::move(addrRemote), _std::move(portRemote)
   );
}

/*static*/ io::filedesc server::create_socket(ip::version ipversion) {
   ABC_TRACE_FUNC(ipversion);

   if (ipversion == ip::version::any) {
      // TODO: provide more information in the exception.
      ABC_THROW(domain_error, ());
   }
   bool bAsync = (this_thread::coroutine_scheduler() != nullptr);
   int iFamily;
   switch (ipversion.base()) {
      case ip::version::v4:
         iFamily = AF_INET;
         break;
      case ip::version::v6:
         iFamily = AF_INET6;
         break;
      ABC_SWITCH_WITHOUT_DEFAULT
   }
   int iType = SOCK_STREAM;
#if ABC_HOST_API_POSIX
   #if !ABC_HOST_API_DARWIN
      iType |= SOCK_CLOEXEC;
      if (bAsync) {
         // Using coroutines, so make this socket non-blocking.
         iType |= SOCK_NONBLOCK;
      }
   #endif
   io::filedesc fd(::socket(iFamily, iType, 0));
   if (!fd) {
      exception::throw_os_error();
   }
   #if ABC_HOST_API_DARWIN
      /* Note that at this point there’s no hack that will ensure a fork()/exec() from another
      thread won’t leak the file descriptor. That’s the whole point of the extra SOCK_* flags. */
      fd.set_close_on_exec(true);
      if (bAsync) {
         fd.set_nonblocking(true);
      }
   #endif
   return _std::move(fd);
#elif ABC_HOST_API_WIN32 //if ABC_HOST_API_POSIX
   static std::uint8_t const sc_iWSAMajorVersion = 2, sc_iWSAMinorVersion = 2;
   ::WSADATA wsad;
   if (int iRet = ::WSAStartup(MAKEWORD(sc_iWSAMajorVersion, sc_iWSAMinorVersion), &wsad)) {
      exception::throw_os_error(static_cast<errint_t>(iRet));
   }
   if (
      LOBYTE(wsad.wVersion) != sc_iWSAMajorVersion || HIBYTE(wsad.wVersion) != sc_iWSAMinorVersion
   ) {
      // The loaded WinSock implementation does not support the requested version.
      ::WSACleanup();
      // TODO: use a better exception class.
      ABC_THROW(generic_error, ());
   }

   ::DWORD iFlags = 0;
   if (bAsync) {
      iFlags |= WSA_FLAG_OVERLAPPED;
   }
   #ifdef WSA_FLAG_NO_HANDLE_INHERIT
      iFlags |= WSA_FLAG_NO_HANDLE_INHERIT;
   #endif
   ::SOCKET sock = ::WSASocket(iFamily, iType, 0, nullptr, 0, iFlags);
   if (sock == INVALID_SOCKET) {
      exception::throw_os_error();
   }
   return io::filedesc(reinterpret_cast<io::filedesc_t>(sock));
#else //if ABC_HOST_API_POSIX … elif ABC_HOST_API_WIN32
   #error "TODO: HOST_API"
#endif //if ABC_HOST_API_POSIX … elif ABC_HOST_API_WIN32 … else
}

}}} //namespace abc::net::tcp
<|endoftext|>