<commit_before>/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001-2003 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 "Xerces" 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/>.
 */

/*
 * $Id$
 * $Log$
 * Revision 1.8  2003/09/23 18:16:07  peiyongz
 * Inplementation for Serialization/Deserialization
 *
 * Revision 1.7  2003/05/15 19:07:46  knoaman
 * Partial implementation of the configurable memory manager.
 *
 * Revision 1.6  2003/05/09 15:13:46  peiyongz
 * Deprecated toString() in XMLNumber family
 *
 * Revision 1.5  2003/03/10 20:55:58  peiyongz
 * Schema Errata E2-40 double/float
 *
 * Revision 1.4  2003/02/02 23:54:43  peiyongz
 * getFormattedString() added to return original and converted value.
 *
 * Revision 1.3  2003/01/30 21:55:22  tng
 * Performance: create getRawData which is similar to toString but return the internal data directly, user is not required to delete the returned memory.
 *
 * Revision 1.2  2002/11/04 15:22:05  tng
 * C++ Namespace Support.
 *
 * Revision 1.1.1.1  2002/02/01 22:22:15  peiyongz
 * sane_include
 *
 * Revision 1.2  2001/10/09 21:28:28  peiyongz
 * explicit ctor/dtor defined.
 *
 * Revision 1.1  2001/09/27 14:54:03  peiyongz
 * DTV Reorganization: new class
 *
 */

#ifndef XMLNUMBER_HPP
#define XMLNUMBER_HPP

#include <xercesc/internal/XSerializable.hpp>
#include <xercesc/util/XMemory.hpp>

XERCES_CPP_NAMESPACE_BEGIN

class XMLUTIL_EXPORT XMLNumber : public XSerializable, public XMemory
{
public:

    enum
    {
        LESS_THAN     = -1,
        EQUAL         = 0,
        GREATER_THAN  = 1,
        INDETERMINATE = 2
    };

    virtual ~XMLNumber();

    /**
     *  Deprecated: please use getRawData
     *
     *  Return string representation of the decimal value.
     *  A decimal point will be included as necessary,
     *  the caller of this method is responsible for the
     *  de-allocation of the memory.
     */
    virtual XMLCh*     toString() const = 0;

	/**
	 *  Return string representation of the decimal value.
     *  A decimal point will be included as necessary.
     *  Similar to toString above, but the internal buffer is
     *  returned directly, user is not required to delete
     *  the returned buffer
	 */
    virtual XMLCh*     getRawData() const = 0;

    /**
     *  Return the original and converted value of the original data.
     *  (applicable to double/float)
     *
     *  The internal buffer is returned directly, user is not required
     *  to delete the returned buffer
     */
    virtual const XMLCh*     getFormattedString() const = 0;

	/**
	 * Returns the sign of this number
     *
     * -1   negative
     *  0   zero
     *  1   positive
	 *
	 */
    virtual int        getSign() const = 0;

    /***
     * Support for Serialization/De-serialization
     ***/
    DECL_XSERIALIZABLE(XMLNumber)

protected:

    XMLNumber();

};

XERCES_CPP_NAMESPACE_END

#endif
<commit_msg>enum NumberType<commit_after>/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001-2003 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 "Xerces" 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/>.
 */

/*
 * $Id$
 * $Log$
 * Revision 1.9  2003/10/02 19:18:12  peiyongz
 * enum NumberType
 *
 * Revision 1.8  2003/09/23 18:16:07  peiyongz
 * Inplementation for Serialization/Deserialization
 *
 * Revision 1.7  2003/05/15 19:07:46  knoaman
 * Partial implementation of the configurable memory manager.
 *
 * Revision 1.6  2003/05/09 15:13:46  peiyongz
 * Deprecated toString() in XMLNumber family
 *
 * Revision 1.5  2003/03/10 20:55:58  peiyongz
 * Schema Errata E2-40 double/float
 *
 * Revision 1.4  2003/02/02 23:54:43  peiyongz
 * getFormattedString() added to return original and converted value.
 *
 * Revision 1.3  2003/01/30 21:55:22  tng
 * Performance: create getRawData which is similar to toString but return the internal data directly, user is not required to delete the returned memory.
 *
 * Revision 1.2  2002/11/04 15:22:05  tng
 * C++ Namespace Support.
 *
 * Revision 1.1.1.1  2002/02/01 22:22:15  peiyongz
 * sane_include
 *
 * Revision 1.2  2001/10/09 21:28:28  peiyongz
 * explicit ctor/dtor defined.
 *
 * Revision 1.1  2001/09/27 14:54:03  peiyongz
 * DTV Reorganization: new class
 *
 */

#ifndef XMLNUMBER_HPP
#define XMLNUMBER_HPP

#include <xercesc/internal/XSerializable.hpp>
#include <xercesc/util/XMemory.hpp>

XERCES_CPP_NAMESPACE_BEGIN

class XMLUTIL_EXPORT XMLNumber : public XSerializable, public XMemory
{
public:

    enum
    {
        LESS_THAN     = -1,
        EQUAL         = 0,
        GREATER_THAN  = 1,
        INDETERMINATE = 2
    };

    enum NumberType {
        Float,
        Double,
        BigDecimal,
        DateTime,
        UnKnown
    };

    virtual ~XMLNumber();

    /**
     *  Deprecated: please use getRawData
     *
     *  Return string representation of the decimal value.
     *  A decimal point will be included as necessary,
     *  the caller of this method is responsible for the
     *  de-allocation of the memory.
     */
    virtual XMLCh*     toString() const = 0;

	/**
	 *  Return string representation of the decimal value.
     *  A decimal point will be included as necessary.
     *  Similar to toString above, but the internal buffer is
     *  returned directly, user is not required to delete
     *  the returned buffer
	 */
    virtual XMLCh*     getRawData() const = 0;

    /**
     *  Return the original and converted value of the original data.
     *  (applicable to double/float)
     *
     *  The internal buffer is returned directly, user is not required
     *  to delete the returned buffer
     */
    virtual const XMLCh*     getFormattedString() const = 0;

	/**
	 * Returns the sign of this number
     *
     * -1   negative
     *  0   zero
     *  1   positive
	 *
	 */
    virtual int        getSign() const = 0;

    /***
     * Support for Serialization/De-serialization
     ***/
    DECL_XSERIALIZABLE(XMLNumber)

protected:

    XMLNumber();

};

XERCES_CPP_NAMESPACE_END

#endif
<|endoftext|>


<commit_before>/***************************************************************************
 *   Copyright (C) 2006 by Tobias Koenig <tokoe@kde.org>                   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
 ***************************************************************************/

#include "dbinitializer.h"

#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtCore/QList>
#include <QtCore/QPair>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
#include <QtSql/QSqlField>
#include <QtSql/QSqlRecord>
#include <QtSql/QSqlQuery>
#include <QtXml/QDomDocument>
#include <QtXml/QDomElement>

DbInitializer::DbInitializer( const QSqlDatabase &database, const QString &templateFile )
  : mDatabase( database ), mTemplateFile( templateFile )
{
}

DbInitializer::~DbInitializer()
{
}

bool DbInitializer::run()
{
  qDebug() << "DbInitializer::run()";

  QFile file( mTemplateFile );
  if ( !file.open( QIODevice::ReadOnly ) ) {
    mErrorMsg = QString::fromLatin1( "Unable to open template file '%1'." ).arg( mTemplateFile );
    return false;
  }

  QDomDocument document;

  QString errorMsg;
  int line, column;
  if ( !document.setContent( &file, &errorMsg, &line, &column ) ) {
    mErrorMsg = QString::fromLatin1( "Unable to parse template file '%1': %2 (%3:%4)." )
                       .arg( mTemplateFile ).arg( errorMsg ).arg( line ).arg( column );
    return false;
  }

  const QDomElement documentElement = document.documentElement();
  if ( documentElement.tagName() != QLatin1String( "database" ) ) {
    mErrorMsg = QString::fromLatin1( "Invalid format of template file '%1'." ).arg( mTemplateFile );
    return false;
  }

  QDomElement tableElement = documentElement.firstChildElement();
  while ( !tableElement.isNull() ) {
    if ( tableElement.tagName() == QLatin1String( "table" ) ) {
      if ( !checkTable( tableElement ) )
        return false;
    } else if ( tableElement.tagName() == QLatin1String( "relation" ) ) {
      if ( !checkRelation( tableElement ) )
        return false;
    }else {
      mErrorMsg = QString::fromLatin1( "Unknown tag, expected <table> and got <%1>." ).arg( tableElement.tagName() );
      return false;
    }

    tableElement = tableElement.nextSiblingElement();
  }

  qDebug() << "DbInitializer::run() done";
  return true;
}

bool DbInitializer::checkTable( const QDomElement &element )
{
  const QString tableName = element.attribute( QLatin1String("name") ) + QLatin1String("Table");
  qDebug() << "checking table " << tableName;

  typedef QPair<QString, QString> ColumnEntry;

  QList<ColumnEntry> columnsList;
  QStringList dataList;

  QDomElement columnElement = element.firstChildElement();
  while ( !columnElement.isNull() ) {
    if ( columnElement.tagName() == QLatin1String( "column" ) ) {
      ColumnEntry entry;
      entry.first = columnElement.attribute( QLatin1String("name") );
      if ( columnElement.attribute( QLatin1String("sqltype") ).isEmpty() )
        entry.second = sqlType( columnElement.attribute( QLatin1String("type") ) ) + QLatin1String( " " ) + columnElement.attribute( QLatin1String("properties") );
      else
        entry.second = columnElement.attribute( QLatin1String("sqltype") ) + QLatin1String( " " ) + columnElement.attribute( QLatin1String("properties") );
      if ( mDatabase.driverName() == QLatin1String( "QPSQL" ) ) {
        if ( entry.second.contains( QLatin1String("AUTOINCREMENT") ) )
          entry.second = QLatin1String("SERIAL PRIMARY KEY NOT NULL");
        if ( entry.second.contains( QLatin1String("BLOB") ) )
          entry.second = QLatin1String("BYTEA");
        if ( entry.second.startsWith( QLatin1String("CHAR") ) )
          entry.second.replace(QLatin1String("CHAR"), QLatin1String("VARCHAR"));
      } else if ( mDatabase.driverName().startsWith( QLatin1String("QMYSQL") ) ) {
        if ( entry.second.contains( QLatin1String("AUTOINCREMENT") ) )
          entry.second.replace(QLatin1String("AUTOINCREMENT"), QLatin1String("AUTO_INCREMENT"));
        if ( entry.second.startsWith( QLatin1String("CHAR") ) )
          entry.second.replace(QLatin1String("CHAR"), QLatin1String("VARCHAR"));
      }
      columnsList.append( entry );
    } else if ( columnElement.tagName() == QLatin1String( "data" ) ) {
      QString values = columnElement.attribute( QLatin1String("values") );
      if ( mDatabase.driverName().startsWith( QLatin1String("QMYSQL") ) )
        values.replace( QLatin1String("\\"), QLatin1String("\\\\") );
      QString statement = QString::fromLatin1( "INSERT INTO %1 (%2) VALUES (%3)" )
          .arg( tableName )
          .arg( columnElement.attribute( QLatin1String("columns") ) )
          .arg( values );
      dataList << statement;
    } else if ( columnElement.tagName() == QLatin1String("reference") ) {
      /* this is the n-side of a n:1 relationship.
       * it is used for code generation but not relevant for schema generation.
       */
    } else if ( columnElement.tagName() == QLatin1String("comment") ) {
      // we use comments for doxygen but not for schema generation
    } else {
      mErrorMsg = QString::fromLatin1( "Unknown tag, expected <column> and got <%1>." ).arg( columnElement.tagName() );
      return false;
    }

    columnElement = columnElement.nextSiblingElement();
  }

  QSqlQuery query( mDatabase );

  if ( !hasTable( tableName ) ) {
    /**
     * We have to create the entire table.
     */

    QString columns;
    for ( int i = 0; i < columnsList.count(); ++i ) {
      if ( i != 0 )
        columns.append( QLatin1String(", ") );

      columns.append( columnsList[ i ].first + QLatin1Char(' ') + columnsList[ i ].second );
    }

    const QString statement = QString::fromLatin1( "CREATE TABLE %1 (%2);" ).arg( tableName, columns );
    qDebug() << statement;

    if ( !query.exec( statement ) ) {
      mErrorMsg = QLatin1String( "Unable to create entire table." );
      return false;
    }
  } else {
    /**
     * Check for every column whether it exists.
     */

    const QSqlRecord table = mDatabase.record( tableName );

    for ( int i = 0; i < columnsList.count(); ++i ) {
      const ColumnEntry entry = columnsList[ i ];

      bool found = false;
      for ( int j = 0; j < table.count(); ++j ) {
        const QSqlField column = table.field( j );

        if ( columnsList[ i ].first == column.name() ) {
          found = true;
        }
      }

      if ( !found ) {
        /**
         * Add missing column to table.
         */
        const QString statement = QString::fromLatin1( "ALTER TABLE %1 ADD COLUMN %2 %3;" )
                                         .arg( tableName, entry.first, entry.second );

        if ( !query.exec( statement ) ) {
          mErrorMsg = QString::fromLatin1( "Unable to add column '%1' to table '%2'." ).arg( entry.first, tableName );
          return false;
        }
      }
    }

    // TODO: remove obsolete columns (when sqlite will support it) and adapt column type modifications
  }

  // add initial data if table is empty
  const QString statement = QString::fromLatin1( "SELECT * FROM %1 LIMIT 1" ).arg( tableName );
  if ( !query.exec( statement ) ) {
    mErrorMsg = QString::fromLatin1( "Unable to retrieve data from table '%1'." ).arg( tableName );
    return false;
  }
  if ( query.size() == 0  || !query.first() ) {
    foreach ( QString stmt, dataList ) {
      if ( !query.exec( stmt ) ) {
        mErrorMsg = QString::fromLatin1( "Unable to add initial data to table '%1'." ).arg( tableName );
        mErrorMsg += QString::fromLatin1( "Query was: %1" ).arg( stmt );
        return false;
      }
    }
  }

  return true;
}

bool DbInitializer::checkRelation(const QDomElement & element)
{
  QString table1 = element.attribute(QLatin1String("table1"));
  QString table2 = element.attribute(QLatin1String("table2"));
  QString col1 = element.attribute(QLatin1String("column1"));
  QString col2 = element.attribute(QLatin1String("column2"));

  QString tableName = table1 + table2 + QLatin1String("Relation");
  qDebug() << "checking relation " << tableName;

  if ( hasTable( tableName ) )
    return true;

  // TODO: add unique constraint
  QString statement = QString::fromLatin1( "CREATE TABLE %1 (" ).arg( tableName );
  statement += QString::fromLatin1("%1_%2 INTEGER REFERENCES %3(%4), " )
      .arg( table1 )
      .arg( col1 )
      .arg( table1 )
      .arg( col1 );
  statement += QString::fromLatin1("%1_%2 INTEGER REFERENCES %3(%4))" )
      .arg( table2 )
      .arg( col2 )
      .arg( table2 )
      .arg( col2 );
  qDebug() << statement;

  QSqlQuery query( mDatabase );
  if ( !query.exec( statement ) ) {
    mErrorMsg = QLatin1String( "Unable to create entire table." );
    return false;
  }

  return true;
}

QString DbInitializer::errorMsg() const
{
  return mErrorMsg;
}

QString DbInitializer::sqlType(const QString & type)
{
  if ( type == QLatin1String("int") )
    return QLatin1String("INTEGER");
  if ( type == QLatin1String("QString") )
    return QLatin1String("TEXT");
  if (type == QLatin1String("QByteArray") )
    return QLatin1String("BLOB");
  if ( type == QLatin1String("QDateTime") )
    return QLatin1String("TIMESTAMP");
  if ( type == QLatin1String( "bool" ) )
    return QLatin1String("BOOL");
  Q_ASSERT( false );
  return QString();
}

bool DbInitializer::hasTable(const QString & tableName)
{
  QString statement;
  if ( mDatabase.driverName() == QLatin1String( "QPSQL" ) )
    statement = QLatin1String("SELECT tablename FROM pg_tables ORDER BY tablename;" );
  else if ( mDatabase.driverName() == QLatin1String( "QSQLITE" ) )
    statement = QLatin1String("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;");
  else if ( mDatabase.driverName().startsWith( QLatin1String("QMYSQL") ) )
    statement = QLatin1String("SHOW TABLES");
  else
    Q_ASSERT( false ); // unknown driver

  QSqlQuery query( mDatabase );
  if ( !query.exec( statement ) ) {
    mErrorMsg = QLatin1String( "Unable to retrieve table information from database." );
    return false;
  }

  while ( query.next() ) {
    if ( query.value( 0 ).toString().toLower() == tableName.toLower() )
      return true;
  }
  return false;
}
<commit_msg>compare column names case insensitive<commit_after>/***************************************************************************
 *   Copyright (C) 2006 by Tobias Koenig <tokoe@kde.org>                   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
 ***************************************************************************/

#include "dbinitializer.h"

#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtCore/QList>
#include <QtCore/QPair>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
#include <QtSql/QSqlField>
#include <QtSql/QSqlRecord>
#include <QtSql/QSqlQuery>
#include <QtXml/QDomDocument>
#include <QtXml/QDomElement>

DbInitializer::DbInitializer( const QSqlDatabase &database, const QString &templateFile )
  : mDatabase( database ), mTemplateFile( templateFile )
{
}

DbInitializer::~DbInitializer()
{
}

bool DbInitializer::run()
{
  qDebug() << "DbInitializer::run()";

  QFile file( mTemplateFile );
  if ( !file.open( QIODevice::ReadOnly ) ) {
    mErrorMsg = QString::fromLatin1( "Unable to open template file '%1'." ).arg( mTemplateFile );
    return false;
  }

  QDomDocument document;

  QString errorMsg;
  int line, column;
  if ( !document.setContent( &file, &errorMsg, &line, &column ) ) {
    mErrorMsg = QString::fromLatin1( "Unable to parse template file '%1': %2 (%3:%4)." )
                       .arg( mTemplateFile ).arg( errorMsg ).arg( line ).arg( column );
    return false;
  }

  const QDomElement documentElement = document.documentElement();
  if ( documentElement.tagName() != QLatin1String( "database" ) ) {
    mErrorMsg = QString::fromLatin1( "Invalid format of template file '%1'." ).arg( mTemplateFile );
    return false;
  }

  QDomElement tableElement = documentElement.firstChildElement();
  while ( !tableElement.isNull() ) {
    if ( tableElement.tagName() == QLatin1String( "table" ) ) {
      if ( !checkTable( tableElement ) )
        return false;
    } else if ( tableElement.tagName() == QLatin1String( "relation" ) ) {
      if ( !checkRelation( tableElement ) )
        return false;
    }else {
      mErrorMsg = QString::fromLatin1( "Unknown tag, expected <table> and got <%1>." ).arg( tableElement.tagName() );
      return false;
    }

    tableElement = tableElement.nextSiblingElement();
  }

  qDebug() << "DbInitializer::run() done";
  return true;
}

bool DbInitializer::checkTable( const QDomElement &element )
{
  const QString tableName = element.attribute( QLatin1String("name") ) + QLatin1String("Table");
  qDebug() << "checking table " << tableName;

  typedef QPair<QString, QString> ColumnEntry;

  QList<ColumnEntry> columnsList;
  QStringList dataList;

  QDomElement columnElement = element.firstChildElement();
  while ( !columnElement.isNull() ) {
    if ( columnElement.tagName() == QLatin1String( "column" ) ) {
      ColumnEntry entry;
      entry.first = columnElement.attribute( QLatin1String("name") );
      if ( columnElement.attribute( QLatin1String("sqltype") ).isEmpty() )
        entry.second = sqlType( columnElement.attribute( QLatin1String("type") ) ) + QLatin1String( " " ) + columnElement.attribute( QLatin1String("properties") );
      else
        entry.second = columnElement.attribute( QLatin1String("sqltype") ) + QLatin1String( " " ) + columnElement.attribute( QLatin1String("properties") );
      if ( mDatabase.driverName() == QLatin1String( "QPSQL" ) ) {
        if ( entry.second.contains( QLatin1String("AUTOINCREMENT") ) )
          entry.second = QLatin1String("SERIAL PRIMARY KEY NOT NULL");
        if ( entry.second.contains( QLatin1String("BLOB") ) )
          entry.second = QLatin1String("BYTEA");
        if ( entry.second.startsWith( QLatin1String("CHAR") ) )
          entry.second.replace(QLatin1String("CHAR"), QLatin1String("VARCHAR"));
      } else if ( mDatabase.driverName().startsWith( QLatin1String("QMYSQL") ) ) {
        if ( entry.second.contains( QLatin1String("AUTOINCREMENT") ) )
          entry.second.replace(QLatin1String("AUTOINCREMENT"), QLatin1String("AUTO_INCREMENT"));
        if ( entry.second.startsWith( QLatin1String("CHAR") ) )
          entry.second.replace(QLatin1String("CHAR"), QLatin1String("VARCHAR"));
      }
      columnsList.append( entry );
    } else if ( columnElement.tagName() == QLatin1String( "data" ) ) {
      QString values = columnElement.attribute( QLatin1String("values") );
      if ( mDatabase.driverName().startsWith( QLatin1String("QMYSQL") ) )
        values.replace( QLatin1String("\\"), QLatin1String("\\\\") );
      QString statement = QString::fromLatin1( "INSERT INTO %1 (%2) VALUES (%3)" )
          .arg( tableName )
          .arg( columnElement.attribute( QLatin1String("columns") ) )
          .arg( values );
      dataList << statement;
    } else if ( columnElement.tagName() == QLatin1String("reference") ) {
      /* this is the n-side of a n:1 relationship.
       * it is used for code generation but not relevant for schema generation.
       */
    } else if ( columnElement.tagName() == QLatin1String("comment") ) {
      // we use comments for doxygen but not for schema generation
    } else {
      mErrorMsg = QString::fromLatin1( "Unknown tag, expected <column> and got <%1>." ).arg( columnElement.tagName() );
      return false;
    }

    columnElement = columnElement.nextSiblingElement();
  }

  QSqlQuery query( mDatabase );

  if ( !hasTable( tableName ) ) {
    /**
     * We have to create the entire table.
     */

    QString columns;
    for ( int i = 0; i < columnsList.count(); ++i ) {
      if ( i != 0 )
        columns.append( QLatin1String(", ") );

      columns.append( columnsList[ i ].first + QLatin1Char(' ') + columnsList[ i ].second );
    }

    const QString statement = QString::fromLatin1( "CREATE TABLE %1 (%2);" ).arg( tableName, columns );
    qDebug() << statement;

    if ( !query.exec( statement ) ) {
      mErrorMsg = QLatin1String( "Unable to create entire table." );
      return false;
    }
  } else {
    /**
     * Check for every column whether it exists.
     */

    const QSqlRecord table = mDatabase.record( tableName );

    for ( int i = 0; i < columnsList.count(); ++i ) {
      const ColumnEntry entry = columnsList[ i ];

      bool found = false;
      for ( int j = 0; j < table.count(); ++j ) {
        const QSqlField column = table.field( j );

        if ( columnsList[ i ].first.toLower() == column.name().toLower() ) {
          found = true;
        }
      }

      if ( !found ) {
        /**
         * Add missing column to table.
         */
        const QString statement = QString::fromLatin1( "ALTER TABLE %1 ADD COLUMN %2 %3;" )
                                         .arg( tableName, entry.first, entry.second );

        if ( !query.exec( statement ) ) {
          mErrorMsg = QString::fromLatin1( "Unable to add column '%1' to table '%2'." ).arg( entry.first, tableName );
          return false;
        }
      }
    }

    // TODO: remove obsolete columns (when sqlite will support it) and adapt column type modifications
  }

  // add initial data if table is empty
  const QString statement = QString::fromLatin1( "SELECT * FROM %1 LIMIT 1" ).arg( tableName );
  if ( !query.exec( statement ) ) {
    mErrorMsg = QString::fromLatin1( "Unable to retrieve data from table '%1'." ).arg( tableName );
    return false;
  }
  if ( query.size() == 0  || !query.first() ) {
    foreach ( QString stmt, dataList ) {
      if ( !query.exec( stmt ) ) {
        mErrorMsg = QString::fromLatin1( "Unable to add initial data to table '%1'." ).arg( tableName );
        mErrorMsg += QString::fromLatin1( "Query was: %1" ).arg( stmt );
        return false;
      }
    }
  }

  return true;
}

bool DbInitializer::checkRelation(const QDomElement & element)
{
  QString table1 = element.attribute(QLatin1String("table1"));
  QString table2 = element.attribute(QLatin1String("table2"));
  QString col1 = element.attribute(QLatin1String("column1"));
  QString col2 = element.attribute(QLatin1String("column2"));

  QString tableName = table1 + table2 + QLatin1String("Relation");
  qDebug() << "checking relation " << tableName;

  if ( hasTable( tableName ) )
    return true;

  // TODO: add unique constraint
  QString statement = QString::fromLatin1( "CREATE TABLE %1 (" ).arg( tableName );
  statement += QString::fromLatin1("%1_%2 INTEGER REFERENCES %3(%4), " )
      .arg( table1 )
      .arg( col1 )
      .arg( table1 )
      .arg( col1 );
  statement += QString::fromLatin1("%1_%2 INTEGER REFERENCES %3(%4))" )
      .arg( table2 )
      .arg( col2 )
      .arg( table2 )
      .arg( col2 );
  qDebug() << statement;

  QSqlQuery query( mDatabase );
  if ( !query.exec( statement ) ) {
    mErrorMsg = QLatin1String( "Unable to create entire table." );
    return false;
  }

  return true;
}

QString DbInitializer::errorMsg() const
{
  return mErrorMsg;
}

QString DbInitializer::sqlType(const QString & type)
{
  if ( type == QLatin1String("int") )
    return QLatin1String("INTEGER");
  if ( type == QLatin1String("QString") )
    return QLatin1String("TEXT");
  if (type == QLatin1String("QByteArray") )
    return QLatin1String("BLOB");
  if ( type == QLatin1String("QDateTime") )
    return QLatin1String("TIMESTAMP");
  if ( type == QLatin1String( "bool" ) )
    return QLatin1String("BOOL");
  Q_ASSERT( false );
  return QString();
}

bool DbInitializer::hasTable(const QString & tableName)
{
  QString statement;
  if ( mDatabase.driverName() == QLatin1String( "QPSQL" ) )
    statement = QLatin1String("SELECT tablename FROM pg_tables ORDER BY tablename;" );
  else if ( mDatabase.driverName() == QLatin1String( "QSQLITE" ) )
    statement = QLatin1String("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;");
  else if ( mDatabase.driverName().startsWith( QLatin1String("QMYSQL") ) )
    statement = QLatin1String("SHOW TABLES");
  else
    Q_ASSERT( false ); // unknown driver

  QSqlQuery query( mDatabase );
  if ( !query.exec( statement ) ) {
    mErrorMsg = QLatin1String( "Unable to retrieve table information from database." );
    return false;
  }

  while ( query.next() ) {
    if ( query.value( 0 ).toString().toLower() == tableName.toLower() )
      return true;
  }
  return false;
}
<|endoftext|>


<commit_before>// RUN: %clang_cc1 %s -std=c++11 -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 %s -std=c++11 -emit-pch -o %t
// RUN: %clang_cc1 %s -std=c++11 -include-pch %t -emit-llvm -o - | FileCheck %s

#ifndef HEADER
#define HEADER

typedef __INTPTR_TYPE__ intptr_t;

// CHECK-DAG:   [[CAP_TYPE1:%.+]] = type { [[INTPTR_T:i.+]], [[INTPTR_T]]*, [[INTPTR_T]]* }
// CHECK-DAG:   [[CAP_TYPE2:%.+]] = type { [[INTPTR_T]], [[INTPTR_T]]* }
// CHECK-DAG:   [[CAP_TYPE3:%.+]] = type { [[INTPTR_T]]*, [[INTPTR_T]], [[INTPTR_T]], [[INTPTR_T]]*, [[INTPTR_T]]* }
// CHECK-DAG:   [[CAP_TYPE4:%.+]] = type { [[INTPTR_T]]*, [[INTPTR_T]], [[INTPTR_T]]*, [[INTPTR_T]], [[INTPTR_T]]* }

// CHECK:       define void [[G:@.+]](
// CHECK:       [[N_ADDR:%.+]] = alloca [[INTPTR_T]]
// CHECK:       store [[INTPTR_T]] %{{.+}}, [[INTPTR_T]]* [[N_ADDR]]
// CHECK:       [[N_VAL:%.+]] = load [[INTPTR_T]]* [[N_ADDR]]
// CHECK:       [[CAP_EXPR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE1]]* [[CAP_ARG:%.+]], i{{.+}} 0, i{{.+}} 0
// CHECK:       store [[INTPTR_T]] [[N_VAL]], [[INTPTR_T]]* [[CAP_EXPR_REF]]
// CHECK:       [[CAP_BUFFER_ADDR:%.+]] = getelementptr inbounds [[CAP_TYPE1]]* [[CAP_ARG]], i{{.+}} 0, i{{.+}} 1
// CHECK:       store [[INTPTR_T]]* %{{.+}}, [[INTPTR_T]]** [[CAP_BUFFER_ADDR]]
// CHECK:       [[CAP_N_REF:%.+]] = getelementptr inbounds [[CAP_TYPE1]]* [[CAP_ARG:%.+]], i{{.+}} 0, i{{.+}} 2
// CHECK:       store [[INTPTR_T]]* [[N_ADDR]], [[INTPTR_T]]** [[CAP_N_REF]]
// CHECK:       call void [[G_LAMBDA:@.+]]([[CAP_TYPE1]]* [[CAP_ARG]])
// CHECK:       ret void
void g(intptr_t n) {
  intptr_t buffer[n];
  [&buffer, &n]() {
    __typeof(buffer) x;
  }();
}

// CHECK: void [[G_LAMBDA]]([[CAP_TYPE1]]*
// CHECK: [[THIS:%.+]] = load [[CAP_TYPE1]]**
// CHECK: [[N_ADDR:%.+]] = getelementptr inbounds [[CAP_TYPE1]]* [[THIS]], i{{.+}} 0, i{{.+}} 0
// CHECK: [[N:%.+]] = load [[INTPTR_T]]* [[N_ADDR]]
// CHECK: [[BUFFER_ADDR:%.+]] = getelementptr inbounds [[CAP_TYPE1]]* [[THIS]], i{{.+}} 0, i{{.+}} 1
// CHECK: [[BUFFER:%.+]] = load [[INTPTR_T]]** [[BUFFER_ADDR]]
// CHECK: call i{{.+}}* @llvm.stacksave()
// CHECK: alloca [[INTPTR_T]], [[INTPTR_T]] [[N]]
// CHECK: call void @llvm.stackrestore(
// CHECK: ret void

template <typename T>
void f(T n, T m) {
  intptr_t buffer[n + m];
  [&buffer]() {
    __typeof(buffer) x;
  }();
}

template <typename T>
intptr_t getSize(T);

template <typename T>
void b(intptr_t n, T arg) {
  typedef intptr_t ArrTy[getSize(arg)];
  ArrTy buffer2;
  ArrTy buffer1[n + arg];
  intptr_t a;
  [&]() {
    n = sizeof(buffer1[n]);
    [&](){
      n = sizeof(buffer2);
      n = sizeof(buffer1);
    }();
  }();
}

// CHECK-LABEL: @main
int main() {
  // CHECK:       call void [[G]]([[INTPTR_T]] 1)
  g((intptr_t)1);
  // CHECK:       call void [[F_INT:@.+]]([[INTPTR_T]] 1, [[INTPTR_T]] 2)
  f((intptr_t)1, (intptr_t)2);
  // CHECK:       call void [[B_INT:@.+]]([[INTPTR_T]] 12, [[INTPTR_T]] 13)
  b((intptr_t)12, (intptr_t)13);
  // CHECK:       ret i32 0
  return 0;
}

// CHECK: void [[F_INT]]([[INTPTR_T]]
// CHECK: [[SIZE:%.+]] = add
// CHECK: call i{{.+}}* @llvm.stacksave()
// CHECK: [[BUFFER_ADDR:%.+]] = alloca [[INTPTR_T]], [[INTPTR_T]] [[SIZE]]
// CHECK: [[CAP_SIZE_REF:%.+]] = getelementptr inbounds [[CAP_TYPE2]]* [[CAP_ARG:%.+]], i{{.+}} 0, i{{.+}} 0
// CHECK: store [[INTPTR_T]] [[SIZE]], [[INTPTR_T]]* [[CAP_SIZE_REF]]
// CHECK: [[CAP_BUFFER_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE2]]* [[CAP_ARG]], i{{.+}} 0, i{{.+}} 1
// CHECK: store [[INTPTR_T]]* [[BUFFER_ADDR]], [[INTPTR_T]]** [[CAP_BUFFER_ADDR_REF]]
// CHECK: call void [[F_INT_LAMBDA:@.+]]([[CAP_TYPE2]]* [[CAP_ARG]])
// CHECK: call void @llvm.stackrestore(
// CHECK: ret void
// CHECK: void [[B_INT]]([[INTPTR_T]]
// CHECK: [[SIZE1:%.+]] = call [[INTPTR_T]]
// CHECK: call i{{.+}}* @llvm.stacksave()
// CHECK: [[BUFFER2_ADDR:%.+]] = alloca [[INTPTR_T]], [[INTPTR_T]] [[SIZE1]]
// CHECK: [[SIZE2:%.+]] = add
// CHECK: [[BUFFER1_ADDR:%.+]] = alloca [[INTPTR_T]], [[INTPTR_T]]
// CHECK: [[CAP_N_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[CAP_ARG:%.+]], i{{.+}} 0, i{{.+}} 0
// CHECK: store [[INTPTR_T]]* {{%.+}}, [[INTPTR_T]]** [[CAP_N_ADDR_REF]]
// CHECK: [[CAP_SIZE2_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[CAP_ARG]], i{{.+}} 0, i{{.+}} 1
// CHECK: store i{{[0-9]+}} [[SIZE2]], i{{[0-9]+}}* [[CAP_SIZE2_REF]]
// CHECK: [[CAP_SIZE1_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[CAP_ARG]], i{{.+}} 0, i{{.+}} 2
// CHECK: store i{{[0-9]+}} [[SIZE1]], i{{[0-9]+}}* [[CAP_SIZE1_REF]]
// CHECK: [[CAP_BUFFER1_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[CAP_ARG]], i{{.+}} 0, i{{.+}} 3
// CHECK: store [[INTPTR_T]]* [[BUFFER1_ADDR]], [[INTPTR_T]]** [[CAP_BUFFER1_ADDR_REF]]
// CHECK: [[CAP_BUFFER2_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[CAP_ARG]], i{{.+}} 0, i{{.+}} 4
// CHECK: store [[INTPTR_T]]* [[BUFFER2_ADDR]], [[INTPTR_T]]** [[CAP_BUFFER2_ADDR_REF]]
// CHECK: call void [[B_INT_LAMBDA:@.+]]([[CAP_TYPE3]]* [[CAP_ARG]])
// CHECK: call void @llvm.stackrestore(
// CHECK: ret void

// CHECK: define {{.*}} void [[B_INT_LAMBDA]]([[CAP_TYPE3]]*
// CHECK: [[SIZE2_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS:%.+]], i{{[0-9]+}} 0, i{{[0-9]+}} 1
// CHECK: [[SIZE2:%.+]] = load i{{[0-9]+}}* [[SIZE2_REF]]
// CHECK: [[SIZE1_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 2
// CHECK: [[SIZE1:%.+]] = load i{{[0-9]+}}* [[SIZE1_REF]]
// CHECK: [[N_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 0
// CHECK: [[N_ADDR:%.+]] = load [[INTPTR_T]]** [[N_ADDR_REF]]
// CHECK: [[N:%.+]] = load [[INTPTR_T]]* [[N_ADDR]]
// CHECK: [[BUFFER1_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 3
// CHECK: [[BUFFER1_ADDR:%.+]] = load [[INTPTR_T]]** [[BUFFER1_ADDR_REF]]
// CHECK: [[ELEM_OFFSET:%.+]] = mul {{.*}} i{{[0-9]+}} [[N]], [[SIZE1]]
// CHECK: [[ELEM_ADDR:%.+]] = getelementptr inbounds [[INTPTR_T]]* [[BUFFER1_ADDR]], i{{[0-9]+}} [[ELEM_OFFSET]]
// CHECK: [[SIZEOF:%.+]] = mul {{.*}} i{{[0-9]+}} {{[0-9]+}}, [[SIZE1]]
// CHECK: [[N_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 0
// CHECK: [[N_ADDR:%.+]] = load [[INTPTR_T]]** [[N_ADDR_REF]]
// CHECK: store [[INTPTR_T]] {{%.+}}, [[INTPTR_T]]* [[N_ADDR]]
// CHECK: [[N_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[CAP:%.+]], i{{[0-9]+}} 0, i{{[0-9]+}} 0
// CHECK: [[N_ADDR_REF_ORIG:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 0
// CHECK: [[N_ADDR_ORIG:%.+]] = load [[INTPTR_T]]** [[N_ADDR_REF_ORIG]]
// CHECK: store [[INTPTR_T]]* [[N_ADDR_ORIG]], [[INTPTR_T]]** [[N_ADDR_REF]]
// CHECK: [[SIZE1_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[CAP]], i{{[0-9]+}} 0, i{{[0-9]+}} 1
// CHECK: store i{{[0-9]+}} [[SIZE1]], i{{[0-9]+}}* [[SIZE1_REF]]
// CHECK: [[BUFFER2_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[CAP]], i{{[0-9]+}} 0, i{{[0-9]+}} 2
// CHECK: [[BUFFER2_ADDR_REF_ORIG:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 4
// CHECK: [[BUFFER2_ADDR_ORIG:%.+]] = load [[INTPTR_T]]** [[BUFFER2_ADDR_REF_ORIG]]
// CHECK: store [[INTPTR_T]]* [[BUFFER2_ADDR_ORIG]], [[INTPTR_T]]** [[BUFFER2_ADDR_REF]]
// CHECK: [[SIZE2_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[CAP]], i{{[0-9]+}} 0, i{{[0-9]+}} 3
// CHECK: store i{{[0-9]+}} [[SIZE2]], i{{[0-9]+}}* [[SIZE2_REF]]
// CHECK: [[BUFFER1_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[CAP]], i{{[0-9]+}} 0, i{{[0-9]+}} 4
// CHECK: [[BUFFER1_ADDR_REF_ORIG:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 3
// CHECK: [[BUFFER1_ADDR_ORIG:%.+]] = load [[INTPTR_T]]** [[BUFFER1_ADDR_REF_ORIG]]
// CHECK: store [[INTPTR_T]]* [[BUFFER1_ADDR_ORIG]], [[INTPTR_T]]** [[BUFFER1_ADDR_REF]]
// CHECK: call void [[B_INT_LAMBDA_LAMBDA:@.+]]([[CAP_TYPE4]]* [[CAP]])
// CHECK: ret void

// CHECK: define {{.*}} void [[B_INT_LAMBDA_LAMBDA]]([[CAP_TYPE4]]*
// CHECK: [[SIZE1_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[THIS:%.+]], i{{[0-9]+}} 0, i{{[0-9]+}} 1
// CHECK: [[SIZE1:%.+]] = load i{{[0-9]+}}* [[SIZE1_REF]]
// CHECK: [[SIZE2_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 3
// CHECK: [[SIZE2:%.+]] = load i{{[0-9]+}}* [[SIZE2_REF]]
// CHECK: [[BUFFER2_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 2
// CHECK: [[BUFFER2_ADDR:%.+]] = load [[INTPTR_T]]** [[BUFFER2_ADDR_REF]]
// CHECK: [[SIZEOF_BUFFER2:%.+]] = mul {{.*}} i{{[0-9]+}} {{[0-9]+}}, [[SIZE1]]
// CHECK: [[BUFFER1_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 4
// CHECK: [[BUFFER1_ADDR:%.+]] = load [[INTPTR_T]]** [[BUFFER1_ADDR_REF]]
// CHECK: [[MUL:%.+]] = mul {{.*}} i{{[0-9]+}} [[SIZE2]], [[SIZE1]]
// CHECK: mul {{.*}} i{{[0-9]+}} {{[0-9]+}}, [[MUL]]
// CHECK: ret void

// CHECK: void [[F_INT_LAMBDA]]([[CAP_TYPE2]]*
// CHECK: [[THIS:%.+]] = load [[CAP_TYPE2]]**
// CHECK: [[SIZE_REF:%.+]] = getelementptr inbounds [[CAP_TYPE2]]* [[THIS]], i{{.+}} 0, i{{.+}} 0
// CHECK: [[SIZE:%.+]] = load [[INTPTR_T]]* [[SIZE_REF]]
// CHECK: call i{{.+}}* @llvm.stacksave()
// CHECK: alloca [[INTPTR_T]], [[INTPTR_T]] [[SIZE]]
// CHECK: call void @llvm.stackrestore(
// CHECK: ret void
#endif
<commit_msg>Support matching signext attribute in vla-lambda-capturing test to appease clang-cmake-mips builder.<commit_after>// RUN: %clang_cc1 %s -std=c++11 -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 %s -std=c++11 -emit-pch -o %t
// RUN: %clang_cc1 %s -std=c++11 -include-pch %t -emit-llvm -o - | FileCheck %s

#ifndef HEADER
#define HEADER

typedef __INTPTR_TYPE__ intptr_t;

// CHECK-DAG:   [[CAP_TYPE1:%.+]] = type { [[INTPTR_T:i.+]], [[INTPTR_T]]*, [[INTPTR_T]]* }
// CHECK-DAG:   [[CAP_TYPE2:%.+]] = type { [[INTPTR_T]], [[INTPTR_T]]* }
// CHECK-DAG:   [[CAP_TYPE3:%.+]] = type { [[INTPTR_T]]*, [[INTPTR_T]], [[INTPTR_T]], [[INTPTR_T]]*, [[INTPTR_T]]* }
// CHECK-DAG:   [[CAP_TYPE4:%.+]] = type { [[INTPTR_T]]*, [[INTPTR_T]], [[INTPTR_T]]*, [[INTPTR_T]], [[INTPTR_T]]* }

// CHECK:       define void [[G:@.+]](
// CHECK:       [[N_ADDR:%.+]] = alloca [[INTPTR_T]]
// CHECK:       store [[INTPTR_T]] %{{.+}}, [[INTPTR_T]]* [[N_ADDR]]
// CHECK:       [[N_VAL:%.+]] = load [[INTPTR_T]]* [[N_ADDR]]
// CHECK:       [[CAP_EXPR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE1]]* [[CAP_ARG:%.+]], i{{.+}} 0, i{{.+}} 0
// CHECK:       store [[INTPTR_T]] [[N_VAL]], [[INTPTR_T]]* [[CAP_EXPR_REF]]
// CHECK:       [[CAP_BUFFER_ADDR:%.+]] = getelementptr inbounds [[CAP_TYPE1]]* [[CAP_ARG]], i{{.+}} 0, i{{.+}} 1
// CHECK:       store [[INTPTR_T]]* %{{.+}}, [[INTPTR_T]]** [[CAP_BUFFER_ADDR]]
// CHECK:       [[CAP_N_REF:%.+]] = getelementptr inbounds [[CAP_TYPE1]]* [[CAP_ARG:%.+]], i{{.+}} 0, i{{.+}} 2
// CHECK:       store [[INTPTR_T]]* [[N_ADDR]], [[INTPTR_T]]** [[CAP_N_REF]]
// CHECK:       call void [[G_LAMBDA:@.+]]([[CAP_TYPE1]]* [[CAP_ARG]])
// CHECK:       ret void
void g(intptr_t n) {
  intptr_t buffer[n];
  [&buffer, &n]() {
    __typeof(buffer) x;
  }();
}

// CHECK: void [[G_LAMBDA]]([[CAP_TYPE1]]*
// CHECK: [[THIS:%.+]] = load [[CAP_TYPE1]]**
// CHECK: [[N_ADDR:%.+]] = getelementptr inbounds [[CAP_TYPE1]]* [[THIS]], i{{.+}} 0, i{{.+}} 0
// CHECK: [[N:%.+]] = load [[INTPTR_T]]* [[N_ADDR]]
// CHECK: [[BUFFER_ADDR:%.+]] = getelementptr inbounds [[CAP_TYPE1]]* [[THIS]], i{{.+}} 0, i{{.+}} 1
// CHECK: [[BUFFER:%.+]] = load [[INTPTR_T]]** [[BUFFER_ADDR]]
// CHECK: call i{{.+}}* @llvm.stacksave()
// CHECK: alloca [[INTPTR_T]], [[INTPTR_T]] [[N]]
// CHECK: call void @llvm.stackrestore(
// CHECK: ret void

template <typename T>
void f(T n, T m) {
  intptr_t buffer[n + m];
  [&buffer]() {
    __typeof(buffer) x;
  }();
}

template <typename T>
intptr_t getSize(T);

template <typename T>
void b(intptr_t n, T arg) {
  typedef intptr_t ArrTy[getSize(arg)];
  ArrTy buffer2;
  ArrTy buffer1[n + arg];
  intptr_t a;
  [&]() {
    n = sizeof(buffer1[n]);
    [&](){
      n = sizeof(buffer2);
      n = sizeof(buffer1);
    }();
  }();
}

// CHECK-LABEL: @main
int main() {
  // CHECK:       call void [[G]]([[INTPTR_T]] [[INTPTR_T_ATTR:(signext )?]]1)
  g((intptr_t)1);
  // CHECK:       call void [[F_INT:@.+]]([[INTPTR_T]] [[INTPTR_T_ATTR]]1, [[INTPTR_T]] [[INTPTR_T_ATTR]]2)
  f((intptr_t)1, (intptr_t)2);
  // CHECK:       call void [[B_INT:@.+]]([[INTPTR_T]] [[INTPTR_T_ATTR]]12, [[INTPTR_T]] [[INTPTR_T_ATTR]]13)
  b((intptr_t)12, (intptr_t)13);
  // CHECK:       ret i32 0
  return 0;
}

// CHECK: void [[F_INT]]([[INTPTR_T]]
// CHECK: [[SIZE:%.+]] = add
// CHECK: call i{{.+}}* @llvm.stacksave()
// CHECK: [[BUFFER_ADDR:%.+]] = alloca [[INTPTR_T]], [[INTPTR_T]] [[SIZE]]
// CHECK: [[CAP_SIZE_REF:%.+]] = getelementptr inbounds [[CAP_TYPE2]]* [[CAP_ARG:%.+]], i{{.+}} 0, i{{.+}} 0
// CHECK: store [[INTPTR_T]] [[SIZE]], [[INTPTR_T]]* [[CAP_SIZE_REF]]
// CHECK: [[CAP_BUFFER_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE2]]* [[CAP_ARG]], i{{.+}} 0, i{{.+}} 1
// CHECK: store [[INTPTR_T]]* [[BUFFER_ADDR]], [[INTPTR_T]]** [[CAP_BUFFER_ADDR_REF]]
// CHECK: call void [[F_INT_LAMBDA:@.+]]([[CAP_TYPE2]]* [[CAP_ARG]])
// CHECK: call void @llvm.stackrestore(
// CHECK: ret void
// CHECK: void [[B_INT]]([[INTPTR_T]]
// CHECK: [[SIZE1:%.+]] = call [[INTPTR_T]]
// CHECK: call i{{.+}}* @llvm.stacksave()
// CHECK: [[BUFFER2_ADDR:%.+]] = alloca [[INTPTR_T]], [[INTPTR_T]] [[SIZE1]]
// CHECK: [[SIZE2:%.+]] = add
// CHECK: [[BUFFER1_ADDR:%.+]] = alloca [[INTPTR_T]], [[INTPTR_T]]
// CHECK: [[CAP_N_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[CAP_ARG:%.+]], i{{.+}} 0, i{{.+}} 0
// CHECK: store [[INTPTR_T]]* {{%.+}}, [[INTPTR_T]]** [[CAP_N_ADDR_REF]]
// CHECK: [[CAP_SIZE2_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[CAP_ARG]], i{{.+}} 0, i{{.+}} 1
// CHECK: store i{{[0-9]+}} [[SIZE2]], i{{[0-9]+}}* [[CAP_SIZE2_REF]]
// CHECK: [[CAP_SIZE1_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[CAP_ARG]], i{{.+}} 0, i{{.+}} 2
// CHECK: store i{{[0-9]+}} [[SIZE1]], i{{[0-9]+}}* [[CAP_SIZE1_REF]]
// CHECK: [[CAP_BUFFER1_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[CAP_ARG]], i{{.+}} 0, i{{.+}} 3
// CHECK: store [[INTPTR_T]]* [[BUFFER1_ADDR]], [[INTPTR_T]]** [[CAP_BUFFER1_ADDR_REF]]
// CHECK: [[CAP_BUFFER2_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[CAP_ARG]], i{{.+}} 0, i{{.+}} 4
// CHECK: store [[INTPTR_T]]* [[BUFFER2_ADDR]], [[INTPTR_T]]** [[CAP_BUFFER2_ADDR_REF]]
// CHECK: call void [[B_INT_LAMBDA:@.+]]([[CAP_TYPE3]]* [[CAP_ARG]])
// CHECK: call void @llvm.stackrestore(
// CHECK: ret void

// CHECK: define {{.*}} void [[B_INT_LAMBDA]]([[CAP_TYPE3]]*
// CHECK: [[SIZE2_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS:%.+]], i{{[0-9]+}} 0, i{{[0-9]+}} 1
// CHECK: [[SIZE2:%.+]] = load i{{[0-9]+}}* [[SIZE2_REF]]
// CHECK: [[SIZE1_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 2
// CHECK: [[SIZE1:%.+]] = load i{{[0-9]+}}* [[SIZE1_REF]]
// CHECK: [[N_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 0
// CHECK: [[N_ADDR:%.+]] = load [[INTPTR_T]]** [[N_ADDR_REF]]
// CHECK: [[N:%.+]] = load [[INTPTR_T]]* [[N_ADDR]]
// CHECK: [[BUFFER1_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 3
// CHECK: [[BUFFER1_ADDR:%.+]] = load [[INTPTR_T]]** [[BUFFER1_ADDR_REF]]
// CHECK: [[ELEM_OFFSET:%.+]] = mul {{.*}} i{{[0-9]+}} [[N]], [[SIZE1]]
// CHECK: [[ELEM_ADDR:%.+]] = getelementptr inbounds [[INTPTR_T]]* [[BUFFER1_ADDR]], i{{[0-9]+}} [[ELEM_OFFSET]]
// CHECK: [[SIZEOF:%.+]] = mul {{.*}} i{{[0-9]+}} {{[0-9]+}}, [[SIZE1]]
// CHECK: [[N_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 0
// CHECK: [[N_ADDR:%.+]] = load [[INTPTR_T]]** [[N_ADDR_REF]]
// CHECK: store [[INTPTR_T]] {{%.+}}, [[INTPTR_T]]* [[N_ADDR]]
// CHECK: [[N_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[CAP:%.+]], i{{[0-9]+}} 0, i{{[0-9]+}} 0
// CHECK: [[N_ADDR_REF_ORIG:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 0
// CHECK: [[N_ADDR_ORIG:%.+]] = load [[INTPTR_T]]** [[N_ADDR_REF_ORIG]]
// CHECK: store [[INTPTR_T]]* [[N_ADDR_ORIG]], [[INTPTR_T]]** [[N_ADDR_REF]]
// CHECK: [[SIZE1_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[CAP]], i{{[0-9]+}} 0, i{{[0-9]+}} 1
// CHECK: store i{{[0-9]+}} [[SIZE1]], i{{[0-9]+}}* [[SIZE1_REF]]
// CHECK: [[BUFFER2_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[CAP]], i{{[0-9]+}} 0, i{{[0-9]+}} 2
// CHECK: [[BUFFER2_ADDR_REF_ORIG:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 4
// CHECK: [[BUFFER2_ADDR_ORIG:%.+]] = load [[INTPTR_T]]** [[BUFFER2_ADDR_REF_ORIG]]
// CHECK: store [[INTPTR_T]]* [[BUFFER2_ADDR_ORIG]], [[INTPTR_T]]** [[BUFFER2_ADDR_REF]]
// CHECK: [[SIZE2_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[CAP]], i{{[0-9]+}} 0, i{{[0-9]+}} 3
// CHECK: store i{{[0-9]+}} [[SIZE2]], i{{[0-9]+}}* [[SIZE2_REF]]
// CHECK: [[BUFFER1_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[CAP]], i{{[0-9]+}} 0, i{{[0-9]+}} 4
// CHECK: [[BUFFER1_ADDR_REF_ORIG:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 3
// CHECK: [[BUFFER1_ADDR_ORIG:%.+]] = load [[INTPTR_T]]** [[BUFFER1_ADDR_REF_ORIG]]
// CHECK: store [[INTPTR_T]]* [[BUFFER1_ADDR_ORIG]], [[INTPTR_T]]** [[BUFFER1_ADDR_REF]]
// CHECK: call void [[B_INT_LAMBDA_LAMBDA:@.+]]([[CAP_TYPE4]]* [[CAP]])
// CHECK: ret void

// CHECK: define {{.*}} void [[B_INT_LAMBDA_LAMBDA]]([[CAP_TYPE4]]*
// CHECK: [[SIZE1_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[THIS:%.+]], i{{[0-9]+}} 0, i{{[0-9]+}} 1
// CHECK: [[SIZE1:%.+]] = load i{{[0-9]+}}* [[SIZE1_REF]]
// CHECK: [[SIZE2_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 3
// CHECK: [[SIZE2:%.+]] = load i{{[0-9]+}}* [[SIZE2_REF]]
// CHECK: [[BUFFER2_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 2
// CHECK: [[BUFFER2_ADDR:%.+]] = load [[INTPTR_T]]** [[BUFFER2_ADDR_REF]]
// CHECK: [[SIZEOF_BUFFER2:%.+]] = mul {{.*}} i{{[0-9]+}} {{[0-9]+}}, [[SIZE1]]
// CHECK: [[BUFFER1_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 4
// CHECK: [[BUFFER1_ADDR:%.+]] = load [[INTPTR_T]]** [[BUFFER1_ADDR_REF]]
// CHECK: [[MUL:%.+]] = mul {{.*}} i{{[0-9]+}} [[SIZE2]], [[SIZE1]]
// CHECK: mul {{.*}} i{{[0-9]+}} {{[0-9]+}}, [[MUL]]
// CHECK: ret void

// CHECK: void [[F_INT_LAMBDA]]([[CAP_TYPE2]]*
// CHECK: [[THIS:%.+]] = load [[CAP_TYPE2]]**
// CHECK: [[SIZE_REF:%.+]] = getelementptr inbounds [[CAP_TYPE2]]* [[THIS]], i{{.+}} 0, i{{.+}} 0
// CHECK: [[SIZE:%.+]] = load [[INTPTR_T]]* [[SIZE_REF]]
// CHECK: call i{{.+}}* @llvm.stacksave()
// CHECK: alloca [[INTPTR_T]], [[INTPTR_T]] [[SIZE]]
// CHECK: call void @llvm.stackrestore(
// CHECK: ret void
#endif
<|endoftext|>


<commit_before>#include "gtest/gtest.h"
#include <stack>
#include <map>

extern "C" {
    #include "Std_Types.h"
    #include "Os.h"
    #include "Os_Cfg.h"
    #include "Os_Types.h"

    #include "Os.c"
}

std::stack<Os_StatusType>  Os_Errors;

extern "C" void Os_ErrorHook   (Os_StatusType ret)
{
    Os_Errors.push(ret);
}

extern "C" void Os_PreTaskHook (Os_TaskType task)
{
}

extern "C" void Os_PostTaskHook(Os_TaskType task)
{
}

extern "C" void Os_Arch_Init(void)
{
}

extern "C" void Os_Arch_SuspendInterrupts(Os_IrqState* mask)
{
}

extern "C" void Os_Arch_ResumeInterrupts(const Os_IrqState* mask)
{
}

extern "C" void Os_Arch_DisableAllInterrupts(void)
{
}

extern "C" void Os_Arch_EnableAllInterrupts(void)
{
}

extern "C" void Os_Arch_PrepareState(Os_TaskType task)
{
}

extern "C" void Os_Arch_SwapState(Os_TaskType task, Os_TaskType prev)
{
    throw std::exception();
}

struct Os_TestInternal : public testing::Test {
    static Os_TestInternal* active;

    virtual void SetUp()
    {
        memset(m_tasks    , 0, sizeof(m_tasks));
        memset(m_resources, 0, sizeof(m_resources));
        memset(m_alarms   , 0, sizeof(m_alarms));
        for(Os_TaskType i = 0; i < OS_TASK_COUNT; ++i) {
            m_tasks[i].priority = (Os_PriorityType)i;
            m_tasks[i].resource = OS_INVALID_RESOURCE;
            m_tasks[i].stack    = m_stacks[i];
            m_tasks[i].stack_size = 256;
        }
        for(Os_ResourceType i = 0; i < OS_RES_COUNT; ++i) {
            ;
        }
        for(Os_AlarmType i = 0; i < OS_ALARM_COUNT; ++i) {
            m_alarms[i].task    = OS_INVALID_TASK;
            m_alarms[i].counter = OS_COUNTER_SYSTEM;
        }

        m_resources[OS_RES_SCHEDULER].priority = OS_PRIO_COUNT;

        m_config.tasks     = &m_tasks;
        m_config.resources = &m_resources;
        m_config.alarms    = &m_alarms;
        active             = this;
    }

    virtual void TearDown()
    {
        Os_Errors = std::stack<Os_StatusType>();
        for(Os_TaskType i = 0; i < OS_TASK_COUNT; ++i) {
            ;
        }
    }

    uint8_t               (m_stacks[256])[OS_TASK_COUNT];
    Os_TaskConfigType     m_tasks    [OS_TASK_COUNT];
    Os_ResourceConfigType m_resources[OS_RES_COUNT];
    Os_AlarmConfigType    m_alarms   [OS_ALARM_COUNT];
    Os_ConfigType         m_config;
};

Os_TestInternal* Os_TestInternal::active = NULL;

struct Os_TestAlarm : public Os_TestInternal
{
    virtual void SetUp()
    {
        Os_TestInternal::SetUp();
        Os_Init(&m_config);
    }
};

TEST_F(Os_TestAlarm, SetAbsAlarm1) {
    EXPECT_EQ(E_OS_ID    , Os_SetAbsAlarm_Internal(OS_ALARM_COUNT, 1, 0)) << "Alarm of invalid ID";
}

TEST_F(Os_TestAlarm, SetAbsAlarm2) {
    Os_TickType tick;
    EXPECT_EQ(E_OK       , Os_SetAbsAlarm_Internal(0, 1, 0));
    EXPECT_EQ(E_OK       , Os_SetAbsAlarm_Internal(1, 5, 0));
    EXPECT_EQ(E_OK       , Os_SetAbsAlarm_Internal(2, 3, 0));

    EXPECT_EQ(E_OK       , Os_GetAlarm_Internal(0, &tick));
    EXPECT_EQ(1          , tick);

    EXPECT_EQ(E_OK       , Os_GetAlarm_Internal(1, &tick));
    EXPECT_EQ(5          , tick);

    EXPECT_EQ(E_OK       , Os_GetAlarm_Internal(2, &tick));
    EXPECT_EQ(3          , tick);
}

TEST_F(Os_TestAlarm, SetAbsAlarm3) {
    EXPECT_EQ(E_OK       , Os_SetAbsAlarm_Internal(0, 1, 0));
    EXPECT_EQ(E_OS_STATE , Os_SetAbsAlarm_Internal(0, 1, 0)) << "Alarm should already have been set";
}

TEST_F(Os_TestAlarm, SetRelAlarm1) {
    EXPECT_EQ(E_OS_ID    , Os_SetRelAlarm_Internal(OS_ALARM_COUNT, 1, 0)) << "Alarm of invalid ID";
}

TEST_F(Os_TestAlarm, SetRelAlarm2) {
    Os_TickType tick;
    EXPECT_EQ(E_OK       , Os_SetRelAlarm_Internal(0, 1, 0));
    EXPECT_EQ(E_OK       , Os_SetRelAlarm_Internal(1, 5, 0));
    EXPECT_EQ(E_OK       , Os_SetRelAlarm_Internal(2, 3, 0));

    EXPECT_EQ(E_OK       , Os_GetAlarm_Internal(0, &tick));
    EXPECT_EQ(1          , tick);

    EXPECT_EQ(E_OK       , Os_GetAlarm_Internal(1, &tick));
    EXPECT_EQ(5          , tick);

    EXPECT_EQ(E_OK       , Os_GetAlarm_Internal(2, &tick));
    EXPECT_EQ(3          , tick);
}

TEST_F(Os_TestAlarm, SetRelAlarm3) {
    EXPECT_EQ(E_OK       , Os_SetRelAlarm_Internal(0, 1, 0));
    EXPECT_EQ(E_OS_STATE , Os_SetRelAlarm_Internal(0, 1, 0)) << "Alarm should already have been set";
}

TEST_F(Os_TestAlarm, CancelAlarm1) {
    EXPECT_EQ(E_OS_ID    , Os_CancelAlarm_Internal(OS_ALARM_COUNT)) << "Alarm of invalid ID";
}

TEST_F(Os_TestAlarm, CancelAlarm2) {
    Os_TickType tick;
    EXPECT_EQ(E_OK       , Os_SetAbsAlarm_Internal(0, 1, 0));
    EXPECT_EQ(E_OK       , Os_CancelAlarm_Internal(0));
    EXPECT_EQ(E_OS_NOFUNC, Os_CancelAlarm_Internal(0))     << "Already canceled this alarm";
}

TEST_F(Os_TestAlarm, CancelAlarm3) {
    Os_TickType tick;
    EXPECT_EQ(E_OK       , Os_SetAbsAlarm_Internal(0, 1, 0));
    EXPECT_EQ(E_OK       , Os_SetAbsAlarm_Internal(1, 5, 0));
    EXPECT_EQ(E_OK       , Os_SetAbsAlarm_Internal(2, 3, 0));

    EXPECT_EQ(E_OK       , Os_CancelAlarm_Internal(2));

    EXPECT_EQ(E_OK       , Os_GetAlarm_Internal(0, &tick));
    EXPECT_EQ(1          , tick);

    EXPECT_EQ(E_OK       , Os_GetAlarm_Internal(1, &tick));
    EXPECT_EQ(5          , tick);

    EXPECT_EQ(E_OS_NOFUNC, Os_GetAlarm_Internal(2, &tick)) << "Alarm should have been cancelled";
}

TEST_F(Os_TestAlarm, GetAlarm1) {
    Os_TickType tick;
    EXPECT_EQ(E_OS_ID    , Os_GetAlarm_Internal(OS_ALARM_COUNT, &tick)) << "Alarm of invalid ID";
}

struct Os_TestResource : public Os_TestInternal
{
    virtual void SetUp()
    {
        Os_TestInternal::SetUp();
        Os_Init(&m_config);
    }
};

TEST_F(Os_TestResource, GetResource1) {
    EXPECT_EQ(E_OS_ID    , Os_GetResource_Internal(OS_RES_COUNT))       << "Resource of invalid ID";
}

TEST_F(Os_TestResource, ReleaseResource1) {
    EXPECT_EQ(E_OS_ID    , Os_ReleaseResource_Internal(OS_RES_COUNT))   << "Resource of invalid ID";
}
<commit_msg>Add dummy functions to get linking working for tests<commit_after>#include "gtest/gtest.h"
#include <stack>
#include <map>

extern "C" {
    #include "Std_Types.h"
    #include "Os.h"
    #include "Os_Cfg.h"
    #include "Os_Types.h"

    #include "Os.c"
}

std::stack<Os_StatusType>  Os_Errors;

extern "C" void Os_ErrorHook   (Os_StatusType ret)
{
    Os_Errors.push(ret);
}

extern "C" void Os_PreTaskHook (Os_TaskType task)
{
}

extern "C" void Os_PostTaskHook(Os_TaskType task)
{
}

extern "C" void Os_Arch_Init(void)
{
}

extern "C" void Os_Arch_SuspendInterrupts(Os_IrqState* mask)
{
}

extern "C" void Os_Arch_ResumeInterrupts(const Os_IrqState* mask)
{
}

extern "C" void Os_Arch_DisableAllInterrupts(void)
{
}

extern "C" void Os_Arch_EnableAllInterrupts(void)
{
}

extern "C" void Os_Arch_PrepareState(Os_TaskType task)
{
}

extern "C" void Os_Arch_SwapState(Os_TaskType task, Os_TaskType prev)
{
    throw std::exception();
}

void Os_Arch_Syscall_Enter(Os_SyscallStateType* state)
{
}

void Os_Arch_Syscall_Leave(const Os_SyscallStateType* state)
{
}

struct Os_TestInternal : public testing::Test {
    static Os_TestInternal* active;

    virtual void SetUp()
    {
        memset(m_tasks    , 0, sizeof(m_tasks));
        memset(m_resources, 0, sizeof(m_resources));
        memset(m_alarms   , 0, sizeof(m_alarms));
        for(Os_TaskType i = 0; i < OS_TASK_COUNT; ++i) {
            m_tasks[i].priority = (Os_PriorityType)i;
            m_tasks[i].resource = OS_INVALID_RESOURCE;
            m_tasks[i].stack    = m_stacks[i];
            m_tasks[i].stack_size = 256;
        }
        for(Os_ResourceType i = 0; i < OS_RES_COUNT; ++i) {
            ;
        }
        for(Os_AlarmType i = 0; i < OS_ALARM_COUNT; ++i) {
            m_alarms[i].task    = OS_INVALID_TASK;
            m_alarms[i].counter = OS_COUNTER_SYSTEM;
        }

        m_resources[OS_RES_SCHEDULER].priority = OS_PRIO_COUNT;

        m_config.tasks     = &m_tasks;
        m_config.resources = &m_resources;
        m_config.alarms    = &m_alarms;
        active             = this;
    }

    virtual void TearDown()
    {
        Os_Errors = std::stack<Os_StatusType>();
        for(Os_TaskType i = 0; i < OS_TASK_COUNT; ++i) {
            ;
        }
    }

    uint8_t               (m_stacks[256])[OS_TASK_COUNT];
    Os_TaskConfigType     m_tasks    [OS_TASK_COUNT];
    Os_ResourceConfigType m_resources[OS_RES_COUNT];
    Os_AlarmConfigType    m_alarms   [OS_ALARM_COUNT];
    Os_ConfigType         m_config;
};

Os_TestInternal* Os_TestInternal::active = NULL;

struct Os_TestAlarm : public Os_TestInternal
{
    virtual void SetUp()
    {
        Os_TestInternal::SetUp();
        Os_Init(&m_config);
    }
};

TEST_F(Os_TestAlarm, SetAbsAlarm1) {
    EXPECT_EQ(E_OS_ID    , Os_SetAbsAlarm_Internal(OS_ALARM_COUNT, 1, 0)) << "Alarm of invalid ID";
}

TEST_F(Os_TestAlarm, SetAbsAlarm2) {
    Os_TickType tick;
    EXPECT_EQ(E_OK       , Os_SetAbsAlarm_Internal(0, 1, 0));
    EXPECT_EQ(E_OK       , Os_SetAbsAlarm_Internal(1, 5, 0));
    EXPECT_EQ(E_OK       , Os_SetAbsAlarm_Internal(2, 3, 0));

    EXPECT_EQ(E_OK       , Os_GetAlarm_Internal(0, &tick));
    EXPECT_EQ(1          , tick);

    EXPECT_EQ(E_OK       , Os_GetAlarm_Internal(1, &tick));
    EXPECT_EQ(5          , tick);

    EXPECT_EQ(E_OK       , Os_GetAlarm_Internal(2, &tick));
    EXPECT_EQ(3          , tick);
}

TEST_F(Os_TestAlarm, SetAbsAlarm3) {
    EXPECT_EQ(E_OK       , Os_SetAbsAlarm_Internal(0, 1, 0));
    EXPECT_EQ(E_OS_STATE , Os_SetAbsAlarm_Internal(0, 1, 0)) << "Alarm should already have been set";
}

TEST_F(Os_TestAlarm, SetRelAlarm1) {
    EXPECT_EQ(E_OS_ID    , Os_SetRelAlarm_Internal(OS_ALARM_COUNT, 1, 0)) << "Alarm of invalid ID";
}

TEST_F(Os_TestAlarm, SetRelAlarm2) {
    Os_TickType tick;
    EXPECT_EQ(E_OK       , Os_SetRelAlarm_Internal(0, 1, 0));
    EXPECT_EQ(E_OK       , Os_SetRelAlarm_Internal(1, 5, 0));
    EXPECT_EQ(E_OK       , Os_SetRelAlarm_Internal(2, 3, 0));

    EXPECT_EQ(E_OK       , Os_GetAlarm_Internal(0, &tick));
    EXPECT_EQ(1          , tick);

    EXPECT_EQ(E_OK       , Os_GetAlarm_Internal(1, &tick));
    EXPECT_EQ(5          , tick);

    EXPECT_EQ(E_OK       , Os_GetAlarm_Internal(2, &tick));
    EXPECT_EQ(3          , tick);
}

TEST_F(Os_TestAlarm, SetRelAlarm3) {
    EXPECT_EQ(E_OK       , Os_SetRelAlarm_Internal(0, 1, 0));
    EXPECT_EQ(E_OS_STATE , Os_SetRelAlarm_Internal(0, 1, 0)) << "Alarm should already have been set";
}

TEST_F(Os_TestAlarm, CancelAlarm1) {
    EXPECT_EQ(E_OS_ID    , Os_CancelAlarm_Internal(OS_ALARM_COUNT)) << "Alarm of invalid ID";
}

TEST_F(Os_TestAlarm, CancelAlarm2) {
    Os_TickType tick;
    EXPECT_EQ(E_OK       , Os_SetAbsAlarm_Internal(0, 1, 0));
    EXPECT_EQ(E_OK       , Os_CancelAlarm_Internal(0));
    EXPECT_EQ(E_OS_NOFUNC, Os_CancelAlarm_Internal(0))     << "Already canceled this alarm";
}

TEST_F(Os_TestAlarm, CancelAlarm3) {
    Os_TickType tick;
    EXPECT_EQ(E_OK       , Os_SetAbsAlarm_Internal(0, 1, 0));
    EXPECT_EQ(E_OK       , Os_SetAbsAlarm_Internal(1, 5, 0));
    EXPECT_EQ(E_OK       , Os_SetAbsAlarm_Internal(2, 3, 0));

    EXPECT_EQ(E_OK       , Os_CancelAlarm_Internal(2));

    EXPECT_EQ(E_OK       , Os_GetAlarm_Internal(0, &tick));
    EXPECT_EQ(1          , tick);

    EXPECT_EQ(E_OK       , Os_GetAlarm_Internal(1, &tick));
    EXPECT_EQ(5          , tick);

    EXPECT_EQ(E_OS_NOFUNC, Os_GetAlarm_Internal(2, &tick)) << "Alarm should have been cancelled";
}

TEST_F(Os_TestAlarm, GetAlarm1) {
    Os_TickType tick;
    EXPECT_EQ(E_OS_ID    , Os_GetAlarm_Internal(OS_ALARM_COUNT, &tick)) << "Alarm of invalid ID";
}

struct Os_TestResource : public Os_TestInternal
{
    virtual void SetUp()
    {
        Os_TestInternal::SetUp();
        Os_Init(&m_config);
    }
};

TEST_F(Os_TestResource, GetResource1) {
    EXPECT_EQ(E_OS_ID    , Os_GetResource_Internal(OS_RES_COUNT))       << "Resource of invalid ID";
}

TEST_F(Os_TestResource, ReleaseResource1) {
    EXPECT_EQ(E_OS_ID    , Os_ReleaseResource_Internal(OS_RES_COUNT))   << "Resource of invalid ID";
}
<|endoftext|>


<commit_before>// RUN: %clang_cc1 -verify -std=c++11 %s
// RUN: %clang_cc1 -xobjective-c++ -verify -std=c++11 %s

#define BEGIN_PRAGMA _Pragma("clang attribute push (__attribute__((availability(macos, introduced=1000))), apply_to=function)")
#define END_PRAGMA _Pragma("clang attribute pop")

extern "C" {
BEGIN_PRAGMA
int f(); // expected-note{{'f' has been marked as being introduced in macOS 1000 here}}
END_PRAGMA
}

namespace my_ns {
BEGIN_PRAGMA
int g(); // expected-note{{'g' has been marked as being introduced in macOS 1000 here}}
END_PRAGMA
namespace nested {
BEGIN_PRAGMA
int h(); // expected-note{{'h' has been marked as being introduced in macOS 1000 here}}
END_PRAGMA
}
}

int a = f(); // expected-warning{{'f' is only available on macOS 1000 or newer}} expected-note{{annotate 'a'}}
int b = my_ns::g(); // expected-warning{{'g' is only available on macOS 1000 or newer}} expected-note{{annotate 'b'}}
int c = my_ns::nested::h(); // expected-warning{{'h' is only available on macOS 1000 or newer}} expected-note{{annotate 'c'}}

struct InStruct {
  // FIXME: This asserts in Objective-C++!
  // FIXME: This is a horrible diagnostic!
#ifndef __OBJC__
  BEGIN_PRAGMA // expected-error {{expected member name or ';' after declaration specifiers}}
#endif
};
<commit_msg>Fix a failing test.<commit_after>// RUN: %clang_cc1 -triple x86_64-apple-darwin9.0.0 -verify -std=c++11 %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin9.0.0 -xobjective-c++ -verify -std=c++11 %s

#define BEGIN_PRAGMA _Pragma("clang attribute push (__attribute__((availability(macos, introduced=1000))), apply_to=function)")
#define END_PRAGMA _Pragma("clang attribute pop")

extern "C" {
BEGIN_PRAGMA
int f(); // expected-note{{'f' has been marked as being introduced in macOS 1000 here}}
END_PRAGMA
}

namespace my_ns {
BEGIN_PRAGMA
int g(); // expected-note{{'g' has been marked as being introduced in macOS 1000 here}}
END_PRAGMA
namespace nested {
BEGIN_PRAGMA
int h(); // expected-note{{'h' has been marked as being introduced in macOS 1000 here}}
END_PRAGMA
}
}

int a = f(); // expected-warning{{'f' is only available on macOS 1000 or newer}} expected-note{{annotate 'a'}}
int b = my_ns::g(); // expected-warning{{'g' is only available on macOS 1000 or newer}} expected-note{{annotate 'b'}}
int c = my_ns::nested::h(); // expected-warning{{'h' is only available on macOS 1000 or newer}} expected-note{{annotate 'c'}}

struct InStruct {
  // FIXME: This asserts in Objective-C++!
  // FIXME: This is a horrible diagnostic!
#ifndef __OBJC__
  BEGIN_PRAGMA // expected-error {{expected member name or ';' after declaration specifiers}}
#endif
};
<|endoftext|>


<commit_before><commit_msg>minimized recursive minimize function<commit_after><|endoftext|>


<commit_before>/*
 * Escape and unescape in URI style ('%20').
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "uri_escape.hxx"
#include "format.h"
#include "pool.hxx"
#include "util/CharUtil.hxx"

#include <string.h>

/**
 * @see RFC 3986 2.3
 */
constexpr
static inline bool
uri_harmless_char(char ch)
{
    return IsAlphaNumericASCII(ch) ||
        ch == '-' || ch == '.' || ch == '_' || ch == '~';
}

size_t
uri_escape(char *dest, const char *src, size_t src_length,
           char escape_char)
{
    size_t i, dest_length = 0;

    for (i = 0; i < src_length; ++i) {
        if (uri_harmless_char(src[i])) {
            dest[dest_length++] = src[i];
        } else {
            dest[dest_length++] = escape_char;
            format_uint8_hex_fixed(&dest[dest_length], (uint8_t)src[i]);
            dest_length += 2;
        }
    }

    return dest_length;
}

const char *
uri_escape_dup(struct pool *pool, const char *src, size_t src_length,
               char escape_char)
{
    char *dest = (char *)p_malloc(pool, src_length * 3 + 1);
    size_t dest_length = uri_escape(dest, src, src_length, escape_char);
    dest[dest_length] = 0;
    return dest;
}

static int
parse_hexdigit(char ch)
{
    if (IsDigitASCII(ch))
        return ch - '0';
    else if (ch >= 'a' && ch <= 'f')
        return ch - 'a' + 0xa;
    else if (ch >= 'A' && ch <= 'F')
        return ch - 'A' + 0xa;
    else
        return -1;
}

size_t
uri_unescape_inplace(char *src, size_t length, char escape_char)
{
    char *end = src + length, *p = src;

    while ((p = (char *)memchr(p, escape_char, end - p)) != nullptr) {
        if (p >= end - 2)
            /* percent sign at the end of string */
            return 0;

        const int digit1 = parse_hexdigit(p[1]);
        const int digit2 = parse_hexdigit(p[2]);
        if (digit1 == -1 || digit2 == -1)
            /* invalid hex digits */
            return 0;

        const char ch = (char)((digit1 << 4) | digit2);
        if (ch == 0)
            /* no %00 hack allowed! */
            return 0;

        *p++ = ch;
        memmove(p, p + 2, end - p - 2);
        end -= 2;
    }

    return end - src;
}

char *
uri_unescape_dup(struct pool *pool, const char *src, size_t length,
                 char escape_char)
{
    char *dest = (char *)p_malloc(pool, length + 1);
    memcpy(dest, src, length);
    size_t dest_length = uri_unescape_inplace(dest, length, escape_char);
    dest[dest_length] = 0;
    return dest;
}
<commit_msg>uri_escape: rename uri_harmless_char() to IsUriUnreserved()<commit_after>/*
 * Escape and unescape in URI style ('%20').
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "uri_escape.hxx"
#include "format.h"
#include "pool.hxx"
#include "util/CharUtil.hxx"

#include <string.h>

/**
 * @see RFC 3986 2.3
 */
constexpr
static inline bool
IsUriUnreserved(char ch)
{
    return IsAlphaNumericASCII(ch) ||
        ch == '-' || ch == '.' || ch == '_' || ch == '~';
}

size_t
uri_escape(char *dest, const char *src, size_t src_length,
           char escape_char)
{
    size_t i, dest_length = 0;

    for (i = 0; i < src_length; ++i) {
        if (IsUriUnreserved(src[i])) {
            dest[dest_length++] = src[i];
        } else {
            dest[dest_length++] = escape_char;
            format_uint8_hex_fixed(&dest[dest_length], (uint8_t)src[i]);
            dest_length += 2;
        }
    }

    return dest_length;
}

const char *
uri_escape_dup(struct pool *pool, const char *src, size_t src_length,
               char escape_char)
{
    char *dest = (char *)p_malloc(pool, src_length * 3 + 1);
    size_t dest_length = uri_escape(dest, src, src_length, escape_char);
    dest[dest_length] = 0;
    return dest;
}

static int
parse_hexdigit(char ch)
{
    if (IsDigitASCII(ch))
        return ch - '0';
    else if (ch >= 'a' && ch <= 'f')
        return ch - 'a' + 0xa;
    else if (ch >= 'A' && ch <= 'F')
        return ch - 'A' + 0xa;
    else
        return -1;
}

size_t
uri_unescape_inplace(char *src, size_t length, char escape_char)
{
    char *end = src + length, *p = src;

    while ((p = (char *)memchr(p, escape_char, end - p)) != nullptr) {
        if (p >= end - 2)
            /* percent sign at the end of string */
            return 0;

        const int digit1 = parse_hexdigit(p[1]);
        const int digit2 = parse_hexdigit(p[2]);
        if (digit1 == -1 || digit2 == -1)
            /* invalid hex digits */
            return 0;

        const char ch = (char)((digit1 << 4) | digit2);
        if (ch == 0)
            /* no %00 hack allowed! */
            return 0;

        *p++ = ch;
        memmove(p, p + 2, end - p - 2);
        end -= 2;
    }

    return end - src;
}

char *
uri_unescape_dup(struct pool *pool, const char *src, size_t length,
                 char escape_char)
{
    char *dest = (char *)p_malloc(pool, length + 1);
    memcpy(dest, src, length);
    size_t dest_length = uri_unescape_inplace(dest, length, escape_char);
    dest[dest_length] = 0;
    return dest;
}
<|endoftext|>


<commit_before>/*
 * Listener on a control datagram socket.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "Control.hxx"
#include "lb_instance.hxx"
#include "lb_config.hxx"
#include "lb_stats.hxx"
#include "control_server.hxx"
#include "failure.hxx"
#include "tpool.hxx"
#include "pool.hxx"
#include "net/ToString.hxx"
#include "util/Exception.hxx"

#include <daemon/log.h>

#include <string.h>
#include <stdlib.h>

LbControl::LbControl(LbInstance &_instance)
    :instance(_instance) {}

static void
enable_node(const LbInstance *instance,
          const char *payload, size_t length)
{
    const char *colon = (const char *)memchr(payload, ':', length);
    if (colon == nullptr || colon == payload || colon == payload + length - 1) {
        daemon_log(3, "malformed FADE_NODE control packet: no port\n");
        return;
    }

    const AutoRewindPool auto_rewind(*tpool);

    char *node_name = p_strndup(tpool, payload, length);
    char *port_string = node_name + (colon - payload);
    *port_string++ = 0;

    const auto *node = instance->config.FindNode(node_name);
    if (node == nullptr) {
        daemon_log(3, "unknown node in FADE_NODE control packet\n");
        return;
    }

    char *endptr;
    unsigned port = strtoul(port_string, &endptr, 10);
    if (port == 0 || *endptr != 0) {
        daemon_log(3, "malformed FADE_NODE control packet: port is not a number\n");
        return;
    }

    const auto with_port = node->address.WithPort(port);

    char buffer[64];
    ToString(buffer, sizeof(buffer), with_port);
    daemon_log(4, "enabling node %s (%s)\n", node_name, buffer);

    failure_unset(with_port, FAILURE_OK);
}

static void
fade_node(const LbInstance *instance,
          const char *payload, size_t length)
{
    const char *colon = (const char *)memchr(payload, ':', length);
    if (colon == nullptr || colon == payload || colon == payload + length - 1) {
        daemon_log(3, "malformed FADE_NODE control packet: no port\n");
        return;
    }

    const AutoRewindPool auto_rewind(*tpool);

    char *node_name = p_strndup(tpool, payload, length);
    char *port_string = node_name + (colon - payload);
    *port_string++ = 0;

    const auto *node = instance->config.FindNode(node_name);
    if (node == nullptr) {
        daemon_log(3, "unknown node in FADE_NODE control packet\n");
        return;
    }

    char *endptr;
    unsigned port = strtoul(port_string, &endptr, 10);
    if (port == 0 || *endptr != 0) {
        daemon_log(3, "malformed FADE_NODE control packet: port is not a number\n");
        return;
    }

    const auto with_port = node->address.WithPort(port);

    char buffer[64];
    ToString(buffer, sizeof(buffer), with_port);
    daemon_log(4, "fading node %s (%s)\n", node_name, buffer);

    /* set status "FADE" for 3 hours */
    failure_set(with_port, FAILURE_FADE,
                std::chrono::hours(3));
}

gcc_const
static const char *
failure_status_to_string(enum failure_status status)
{
    switch (status) {
    case FAILURE_OK:
        return "ok";

    case FAILURE_FADE:
        return "fade";

    case FAILURE_RESPONSE:
    case FAILURE_FAILED:
    case FAILURE_MONITOR:
        break;
    }

    return "error";
}

static void
node_status_response(ControlServer *server,
                     SocketAddress address,
                     const char *payload, size_t length, const char *status)
{
    size_t status_length = strlen(status);

    size_t response_length = length + 1 + status_length;
    char *response = PoolAlloc<char>(*tpool, response_length);
    memcpy(response, payload, length);
    response[length] = 0;
    memcpy(response + length + 1, status, status_length);

    server->Reply(address,
                  CONTROL_NODE_STATUS, response, response_length);
}

static void
query_node_status(LbControl *control, ControlServer &control_server,
                  const char *payload, size_t length,
                  SocketAddress address)
try {
    if (address.GetSize() == 0) {
        daemon_log(3, "got NODE_STATUS from unbound client socket\n");
        return;
    }

    const char *colon = (const char *)memchr(payload, ':', length);
    if (colon == nullptr || colon == payload || colon == payload + length - 1) {
        daemon_log(3, "malformed NODE_STATUS control packet: no port\n");
        node_status_response(control->server.get(), address,
                             payload, length, "malformed");
        return;
    }

    const AutoRewindPool auto_rewind(*tpool);

    char *node_name = p_strndup(tpool, payload, length);
    char *port_string = node_name + (colon - payload);
    *port_string++ = 0;

    const auto *node = control->instance.config.FindNode(node_name);
    if (node == nullptr) {
        daemon_log(3, "unknown node in NODE_STATUS control packet\n");
        node_status_response(control->server.get(), address,
                             payload, length, "unknown");
        return;
    }

    char *endptr;
    unsigned port = strtoul(port_string, &endptr, 10);
    if (port == 0 || *endptr != 0) {
        daemon_log(3, "malformed NODE_STATUS control packet: port is not a number\n");
        node_status_response(control->server.get(), address,
                             payload, length, "malformed");
        return;
    }

    const auto with_port = node->address.WithPort(port);

    char buffer[64];
    ToString(buffer, sizeof(buffer), with_port);

    enum failure_status status = failure_get_status(with_port);
    const char *s = failure_status_to_string(status);

    node_status_response(&control_server, address,
                         payload, length, s);
} catch (const std::runtime_error &e) {
    daemon_log(3, "%s\n", e.what());
}

static void
query_stats(LbControl *control, ControlServer &control_server,
            SocketAddress address)
try {
    struct beng_control_stats stats;
    lb_get_stats(&control->instance, &stats);

    control_server.Reply(address,
                         CONTROL_STATS, &stats, sizeof(stats));
} catch (const std::runtime_error &e) {
    daemon_log(3, "%s\n", e.what());
}

void
LbControl::OnControlPacket(ControlServer &control_server,
                           enum beng_control_command command,
                           const void *payload, size_t payload_length,
                           SocketAddress address)
{
    /* only local clients are allowed to use most commands */
    const bool is_privileged = address.GetFamily() == AF_LOCAL;

    switch (command) {
    case CONTROL_NOP:
    case CONTROL_TCACHE_INVALIDATE:
    case CONTROL_FADE_CHILDREN:
        break;

    case CONTROL_ENABLE_NODE:
        if (is_privileged)
            enable_node(&instance, (const char *)payload, payload_length);
        break;

    case CONTROL_FADE_NODE:
        if (is_privileged)
            fade_node(&instance, (const char *)payload, payload_length);
        break;

    case CONTROL_NODE_STATUS:
        query_node_status(this, control_server,
                          (const char *)payload, payload_length,
                          address);
        break;

    case CONTROL_DUMP_POOLS:
        if (is_privileged)
            pool_dump_tree(instance.root_pool);
        break;

    case CONTROL_STATS:
        query_stats(this, control_server, address);
        break;

    case CONTROL_VERBOSE:
        if (is_privileged && payload_length == 1)
            daemon_log_config.verbose = *(const uint8_t *)payload;
        break;
    }
}

void
LbControl::OnControlError(std::exception_ptr ep)
{
    daemon_log(2, "%s\n", GetFullMessage(ep).c_str());
}

void
LbControl::Open(const LbControlConfig &config)
{
    assert(server == nullptr);

    server = std::make_unique<ControlServer>(instance.event_loop, *this,
                                             config);
}

LbControl::~LbControl()
{
}

void
LbControl::Enable()
{
    server->Enable();
}

void
LbControl::Disable()
{
    server->Disable();
}
<commit_msg>lb/Control: use the "control_server" parameter<commit_after>/*
 * Listener on a control datagram socket.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "Control.hxx"
#include "lb_instance.hxx"
#include "lb_config.hxx"
#include "lb_stats.hxx"
#include "control_server.hxx"
#include "failure.hxx"
#include "tpool.hxx"
#include "pool.hxx"
#include "net/ToString.hxx"
#include "util/Exception.hxx"

#include <daemon/log.h>

#include <string.h>
#include <stdlib.h>

LbControl::LbControl(LbInstance &_instance)
    :instance(_instance) {}

static void
enable_node(const LbInstance *instance,
          const char *payload, size_t length)
{
    const char *colon = (const char *)memchr(payload, ':', length);
    if (colon == nullptr || colon == payload || colon == payload + length - 1) {
        daemon_log(3, "malformed FADE_NODE control packet: no port\n");
        return;
    }

    const AutoRewindPool auto_rewind(*tpool);

    char *node_name = p_strndup(tpool, payload, length);
    char *port_string = node_name + (colon - payload);
    *port_string++ = 0;

    const auto *node = instance->config.FindNode(node_name);
    if (node == nullptr) {
        daemon_log(3, "unknown node in FADE_NODE control packet\n");
        return;
    }

    char *endptr;
    unsigned port = strtoul(port_string, &endptr, 10);
    if (port == 0 || *endptr != 0) {
        daemon_log(3, "malformed FADE_NODE control packet: port is not a number\n");
        return;
    }

    const auto with_port = node->address.WithPort(port);

    char buffer[64];
    ToString(buffer, sizeof(buffer), with_port);
    daemon_log(4, "enabling node %s (%s)\n", node_name, buffer);

    failure_unset(with_port, FAILURE_OK);
}

static void
fade_node(const LbInstance *instance,
          const char *payload, size_t length)
{
    const char *colon = (const char *)memchr(payload, ':', length);
    if (colon == nullptr || colon == payload || colon == payload + length - 1) {
        daemon_log(3, "malformed FADE_NODE control packet: no port\n");
        return;
    }

    const AutoRewindPool auto_rewind(*tpool);

    char *node_name = p_strndup(tpool, payload, length);
    char *port_string = node_name + (colon - payload);
    *port_string++ = 0;

    const auto *node = instance->config.FindNode(node_name);
    if (node == nullptr) {
        daemon_log(3, "unknown node in FADE_NODE control packet\n");
        return;
    }

    char *endptr;
    unsigned port = strtoul(port_string, &endptr, 10);
    if (port == 0 || *endptr != 0) {
        daemon_log(3, "malformed FADE_NODE control packet: port is not a number\n");
        return;
    }

    const auto with_port = node->address.WithPort(port);

    char buffer[64];
    ToString(buffer, sizeof(buffer), with_port);
    daemon_log(4, "fading node %s (%s)\n", node_name, buffer);

    /* set status "FADE" for 3 hours */
    failure_set(with_port, FAILURE_FADE,
                std::chrono::hours(3));
}

gcc_const
static const char *
failure_status_to_string(enum failure_status status)
{
    switch (status) {
    case FAILURE_OK:
        return "ok";

    case FAILURE_FADE:
        return "fade";

    case FAILURE_RESPONSE:
    case FAILURE_FAILED:
    case FAILURE_MONITOR:
        break;
    }

    return "error";
}

static void
node_status_response(ControlServer *server,
                     SocketAddress address,
                     const char *payload, size_t length, const char *status)
{
    size_t status_length = strlen(status);

    size_t response_length = length + 1 + status_length;
    char *response = PoolAlloc<char>(*tpool, response_length);
    memcpy(response, payload, length);
    response[length] = 0;
    memcpy(response + length + 1, status, status_length);

    server->Reply(address,
                  CONTROL_NODE_STATUS, response, response_length);
}

static void
query_node_status(LbControl *control, ControlServer &control_server,
                  const char *payload, size_t length,
                  SocketAddress address)
try {
    if (address.GetSize() == 0) {
        daemon_log(3, "got NODE_STATUS from unbound client socket\n");
        return;
    }

    const char *colon = (const char *)memchr(payload, ':', length);
    if (colon == nullptr || colon == payload || colon == payload + length - 1) {
        daemon_log(3, "malformed NODE_STATUS control packet: no port\n");
        node_status_response(&control_server, address,
                             payload, length, "malformed");
        return;
    }

    const AutoRewindPool auto_rewind(*tpool);

    char *node_name = p_strndup(tpool, payload, length);
    char *port_string = node_name + (colon - payload);
    *port_string++ = 0;

    const auto *node = control->instance.config.FindNode(node_name);
    if (node == nullptr) {
        daemon_log(3, "unknown node in NODE_STATUS control packet\n");
        node_status_response(&control_server, address,
                             payload, length, "unknown");
        return;
    }

    char *endptr;
    unsigned port = strtoul(port_string, &endptr, 10);
    if (port == 0 || *endptr != 0) {
        daemon_log(3, "malformed NODE_STATUS control packet: port is not a number\n");
        node_status_response(&control_server, address,
                             payload, length, "malformed");
        return;
    }

    const auto with_port = node->address.WithPort(port);

    char buffer[64];
    ToString(buffer, sizeof(buffer), with_port);

    enum failure_status status = failure_get_status(with_port);
    const char *s = failure_status_to_string(status);

    node_status_response(&control_server, address,
                         payload, length, s);
} catch (const std::runtime_error &e) {
    daemon_log(3, "%s\n", e.what());
}

static void
query_stats(LbControl *control, ControlServer &control_server,
            SocketAddress address)
try {
    struct beng_control_stats stats;
    lb_get_stats(&control->instance, &stats);

    control_server.Reply(address,
                         CONTROL_STATS, &stats, sizeof(stats));
} catch (const std::runtime_error &e) {
    daemon_log(3, "%s\n", e.what());
}

void
LbControl::OnControlPacket(ControlServer &control_server,
                           enum beng_control_command command,
                           const void *payload, size_t payload_length,
                           SocketAddress address)
{
    /* only local clients are allowed to use most commands */
    const bool is_privileged = address.GetFamily() == AF_LOCAL;

    switch (command) {
    case CONTROL_NOP:
    case CONTROL_TCACHE_INVALIDATE:
    case CONTROL_FADE_CHILDREN:
        break;

    case CONTROL_ENABLE_NODE:
        if (is_privileged)
            enable_node(&instance, (const char *)payload, payload_length);
        break;

    case CONTROL_FADE_NODE:
        if (is_privileged)
            fade_node(&instance, (const char *)payload, payload_length);
        break;

    case CONTROL_NODE_STATUS:
        query_node_status(this, control_server,
                          (const char *)payload, payload_length,
                          address);
        break;

    case CONTROL_DUMP_POOLS:
        if (is_privileged)
            pool_dump_tree(instance.root_pool);
        break;

    case CONTROL_STATS:
        query_stats(this, control_server, address);
        break;

    case CONTROL_VERBOSE:
        if (is_privileged && payload_length == 1)
            daemon_log_config.verbose = *(const uint8_t *)payload;
        break;
    }
}

void
LbControl::OnControlError(std::exception_ptr ep)
{
    daemon_log(2, "%s\n", GetFullMessage(ep).c_str());
}

void
LbControl::Open(const LbControlConfig &config)
{
    assert(server == nullptr);

    server = std::make_unique<ControlServer>(instance.event_loop, *this,
                                             config);
}

LbControl::~LbControl()
{
}

void
LbControl::Enable()
{
    server->Enable();
}

void
LbControl::Disable()
{
    server->Disable();
}
<|endoftext|>


<commit_before>#ifndef SILICIUM_ASIO_POSTING_OBSERVABLE_HPP
#define SILICIUM_ASIO_POSTING_OBSERVABLE_HPP

#include <silicium/exchange.hpp>
#include <silicium/observable/function_observer.hpp>
#include <silicium/to_shared.hpp>
#include <algorithm>
#include <boost/asio/io_service.hpp>
#include <boost/asio/strand.hpp>

namespace Si
{
	namespace asio
	{
		inline boost::asio::io_service &get_io_service(boost::asio::io_service &io)
		{
			return io;
		}

		inline boost::asio::io_service &get_io_service(boost::asio::io_service::strand &strand)
		{
			return strand.get_io_service();
		}

		template <class Next, class Dispatcher = boost::asio::io_service>
		struct posting_observable
		{
			typedef typename Next::element_type element_type;

			posting_observable()
			    : m_io(nullptr)
			{
			}

			explicit posting_observable(Dispatcher &io, Next next)
			    : m_io(&io)
			    , m_next(std::move(next))
			{
			}

			template <class Observer>
			void async_get_one(Observer &&observer_)
			{
				auto keep_io_running =
#if SILICIUM_COMPILER_HAS_EXTENDED_CAPTURE
				    Si::make_unique
#else
				    std::make_shared
#endif
				    <boost::asio::io_service::work>(get_io_service(*m_io));

				// VC++ 2013 bug: this cannot be captured by the following lambda for some reason (fails with
				// nonsense compiler error about a generated posting_observable copy operator)
				boost::asio::io_service *io = this->m_io;
				m_next.async_get_one(make_function_observer(
				    [
					  io,
					  SILICIUM_CAPTURE_EXPRESSION(keep_io_running, std::move(keep_io_running)),
					  SILICIUM_CAPTURE_EXPRESSION(observer_, std::forward<Observer>(observer_))
					](Si::optional<element_type> element) mutable
				    {
					    if (element)
					    {
#if SILICIUM_COMPILER_HAS_EXTENDED_CAPTURE
						    io->post(
						        // The additional indirection makes the function object copyable even
						        // if element_type is not. post() would not take a noncopyable function.
						        // TODO: do this only when necessary
						        function<void()>([
							        SILICIUM_CAPTURE_EXPRESSION(element, std::move(element)),
							        SILICIUM_CAPTURE_EXPRESSION(observer_, std::forward<Observer>(observer_))
							    ]() mutable
						                         {
							                         std::forward<Observer>(observer_).got_element(std::move(*element));
							                     }));
#else
						    auto copyable_element = to_shared(std::move(*element));
						    io->post([copyable_element, observer_]() mutable
						             {
							             std::forward<Observer>(observer_).got_element(std::move(*copyable_element));
							         });
#endif
					    }
					    else
					    {
						    io->post([SILICIUM_CAPTURE_EXPRESSION(observer_,
						                                          std::forward<Observer>(observer_))]() mutable
						             {
							             std::forward<Observer>(observer_).ended();
							         });
					    }
					}));
			}

#if SILICIUM_COMPILER_GENERATES_MOVES
			posting_observable(posting_observable &&) = default;
			posting_observable &operator=(posting_observable &&) = default;
#else
			posting_observable(posting_observable &&other)
			    : m_io(std::move(other.m_io))
			    , m_next(std::move(other.m_next))
			{
			}

			posting_observable &operator=(posting_observable &&other)
			{
				m_io = std::move(other.m_io);
				m_next = std::move(other.m_next);
				return *this;
			}
#endif

			Next &get_input()
			{
				return m_next;
			}

		private:
			Dispatcher *m_io;
			Next m_next;

			SILICIUM_DELETED_FUNCTION(posting_observable(posting_observable const &))
			SILICIUM_DELETED_FUNCTION(posting_observable &operator=(posting_observable const &))
		};

		template <class Next>
		auto make_posting_observable(boost::asio::io_service &io, Next &&next)
#if !SILICIUM_COMPILER_HAS_AUTO_RETURN_TYPE
		    -> posting_observable<typename std::decay<Next>::type>
#endif
		{
			return posting_observable<typename std::decay<Next>::type>(io, std::forward<Next>(next));
		}
	}
}

#endif
<commit_msg>correct type of a local variable<commit_after>#ifndef SILICIUM_ASIO_POSTING_OBSERVABLE_HPP
#define SILICIUM_ASIO_POSTING_OBSERVABLE_HPP

#include <silicium/exchange.hpp>
#include <silicium/observable/function_observer.hpp>
#include <silicium/to_shared.hpp>
#include <algorithm>
#include <boost/asio/io_service.hpp>
#include <boost/asio/strand.hpp>

namespace Si
{
	namespace asio
	{
		inline boost::asio::io_service &get_io_service(boost::asio::io_service &io)
		{
			return io;
		}

		inline boost::asio::io_service &get_io_service(boost::asio::io_service::strand &strand)
		{
			return strand.get_io_service();
		}

		template <class Next, class Dispatcher = boost::asio::io_service>
		struct posting_observable
		{
			typedef typename Next::element_type element_type;

			posting_observable()
			    : m_io(nullptr)
			{
			}

			explicit posting_observable(Dispatcher &io, Next next)
			    : m_io(&io)
			    , m_next(std::move(next))
			{
			}

			template <class Observer>
			void async_get_one(Observer &&observer_)
			{
				auto keep_io_running =
#if SILICIUM_COMPILER_HAS_EXTENDED_CAPTURE
				    Si::make_unique
#else
				    std::make_shared
#endif
				    <boost::asio::io_service::work>(get_io_service(*m_io));

				// VC++ 2013 bug: this cannot be captured by the following lambda for some reason (fails with
				// nonsense compiler error about a generated posting_observable copy operator)
				Dispatcher *io = this->m_io;
				m_next.async_get_one(make_function_observer(
				    [
					  io,
					  SILICIUM_CAPTURE_EXPRESSION(keep_io_running, std::move(keep_io_running)),
					  SILICIUM_CAPTURE_EXPRESSION(observer_, std::forward<Observer>(observer_))
					](Si::optional<element_type> element) mutable
				    {
					    if (element)
					    {
#if SILICIUM_COMPILER_HAS_EXTENDED_CAPTURE
						    io->post(
						        // The additional indirection makes the function object copyable even
						        // if element_type is not. post() would not take a noncopyable function.
						        // TODO: do this only when necessary
						        function<void()>([
							        SILICIUM_CAPTURE_EXPRESSION(element, std::move(element)),
							        SILICIUM_CAPTURE_EXPRESSION(observer_, std::forward<Observer>(observer_))
							    ]() mutable
						                         {
							                         std::forward<Observer>(observer_).got_element(std::move(*element));
							                     }));
#else
						    auto copyable_element = to_shared(std::move(*element));
						    io->post([copyable_element, observer_]() mutable
						             {
							             std::forward<Observer>(observer_).got_element(std::move(*copyable_element));
							         });
#endif
					    }
					    else
					    {
						    io->post([SILICIUM_CAPTURE_EXPRESSION(observer_,
						                                          std::forward<Observer>(observer_))]() mutable
						             {
							             std::forward<Observer>(observer_).ended();
							         });
					    }
					}));
			}

#if SILICIUM_COMPILER_GENERATES_MOVES
			posting_observable(posting_observable &&) = default;
			posting_observable &operator=(posting_observable &&) = default;
#else
			posting_observable(posting_observable &&other)
			    : m_io(std::move(other.m_io))
			    , m_next(std::move(other.m_next))
			{
			}

			posting_observable &operator=(posting_observable &&other)
			{
				m_io = std::move(other.m_io);
				m_next = std::move(other.m_next);
				return *this;
			}
#endif

			Next &get_input()
			{
				return m_next;
			}

		private:
			Dispatcher *m_io;
			Next m_next;

			SILICIUM_DELETED_FUNCTION(posting_observable(posting_observable const &))
			SILICIUM_DELETED_FUNCTION(posting_observable &operator=(posting_observable const &))
		};

		template <class Next>
		auto make_posting_observable(boost::asio::io_service &io, Next &&next)
#if !SILICIUM_COMPILER_HAS_AUTO_RETURN_TYPE
		    -> posting_observable<typename std::decay<Next>::type>
#endif
		{
			return posting_observable<typename std::decay<Next>::type>(io, std::forward<Next>(next));
		}
	}
}

#endif
<|endoftext|>