<commit_before>/*
    Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org>
    Copyright (c) 2010 Michael Jansen <kde@michael-jansen>

    This library 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 library 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 Library General Public
    License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "notificationmanager.h"
#include <akdebug.h>
#include "notificationmanageradaptor.h"
#include "notificationsource.h"
#include "tracer.h"
#include "storage/datastore.h"
#include "clientcapabilityaggregator.h"

#include <akstandarddirs.h>
#include <libs/xdgbasedirs_p.h>

#include <QtCore/QDebug>
#include <QDBusConnection>
#include <QSettings>

using namespace Akonadi;

NotificationManager* NotificationManager::mSelf = 0;

NotificationManager::NotificationManager()
  : QObject( 0 )
{
  NotificationMessage::registerDBusTypes();
  NotificationMessageV2::registerDBusTypes();

  new NotificationManagerAdaptor( this );
  QDBusConnection::sessionBus().registerObject( QLatin1String("/notifications"),
    this, QDBusConnection::ExportAdaptors );
  QDBusConnection::sessionBus().registerObject( QLatin1String("/notifications/debug"),
    this, QDBusConnection::ExportScriptableSlots );

  const QString serverConfigFile = AkStandardDirs::serverConfigFile( XdgBaseDirs::ReadWrite );
  QSettings settings( serverConfigFile, QSettings::IniFormat );

  mTimer.setInterval( settings.value( QLatin1String("NotificationManager/Interval"), 50 ).toInt() );
  mTimer.setSingleShot( true );
  connect( &mTimer, SIGNAL(timeout()), SLOT(emitPendingNotifications()) );
}

NotificationManager::~NotificationManager()
{
}

NotificationManager* NotificationManager::self()
{
  if ( !mSelf )
    mSelf = new NotificationManager();

  return mSelf;
}

void NotificationManager::connectNotificationCollector(NotificationCollector* collector)
{
  connect( collector, SIGNAL(notify(Akonadi::NotificationMessageV2::List)),
           SLOT(slotNotify(Akonadi::NotificationMessageV2::List)) );
}

void NotificationManager::slotNotify(const Akonadi::NotificationMessageV2::List &msgs)
{
  //akDebug() << Q_FUNC_INFO << "Appending" << msgs.count() << "notifications to current list of " << mNotifications.count() << "notifications";
  Q_FOREACH ( const NotificationMessageV2 &msg, msgs )
    NotificationMessageV2::appendAndCompress( mNotifications, msg );
  //akDebug() << Q_FUNC_INFO << "We have" << mNotifications.count() << "notifications queued in total after appendAndCompress()";

  if ( !mTimer.isActive() )
    mTimer.start();
}

QSet< NotificationSource* > NotificationManager::findInterestedSources( const NotificationMessageV2 &msg )
{
  QSet<NotificationSource*> sources;

  if ( msg.entities().count() == 0 ) {
    return sources;
  }

  if ( msg.type() == NotificationMessageV2::InvalidType ) {
    akDebug() << "Received invalid change notification";
    return sources;
  }

  sources.unite( mAllMonitored );
  if ( msg.operation() == NotificationMessageV2::Move ) {
    sources.unite( mMonitoredResources.values( msg.destinationResource() ).toSet() );
  }

  switch ( msg.type() ) {
    case NotificationMessageV2::InvalidType:
      return sources;

    case NotificationMessageV2::Items: {
      const QList<NotificationMessageV2::Entity> entities = msg.entities().values();
      if ( !mMonitoredResources.isEmpty() || !mMonitoredMimeTypes.isEmpty() ) {
        QSet<NotificationSource*> srcs;
        srcs.unite( mMonitoredResources.values( msg.resource() ).toSet() );
        if ( msg.operation() == NotificationMessageV2::Move ) {
          srcs.unite( mMonitoredResources.values( msg.destinationResource() ) .toSet() );
        }

        if ( !mMonitoredMimeTypes.isEmpty() ) {
          Q_FOREACH ( const NotificationMessageV2::Entity &entity, entities ) {
            srcs.unite( mMonitoredMimeTypes.values( entity.mimeType ).toSet() );
          }
        }

        if ( srcs.isEmpty() ) {
          return sources;
        }

        sources.unite( srcs );

      }
      if ( !mMonitoredItems.isEmpty() ) {
        Q_FOREACH ( const NotificationMessageV2::Entity &entity, entities ) {
          sources.unite( mMonitoredMimeTypes.values( entity.mimeType ).toSet() );
        }
      }

      sources.unite( mMonitoredCollections.values( static_cast<Entity::Id>( msg.parentCollection() ) ).toSet() );
      sources.unite( mMonitoredCollections.values( static_cast<Entity::Id>( msg.parentDestCollection() ) ).toSet() );
      // If the resource is watching root collection, then it wants to be notified
      // about all changes in it's subcollections
      sources.unite( mMonitoredCollections.values( static_cast<Entity::Id>( 0 ) ).toSet() );

    } break;

    case NotificationMessageV2::Collections: {
      const QList<NotificationMessageV2::Id> ids = msg.entities().uniqueKeys();
      if ( !mMonitoredResources.isEmpty() ) {
        sources.unite( mMonitoredResources.values( msg.resource() ).toSet() );
        if ( msg.operation() == NotificationMessageV2::Move ) {
          sources.unite( mMonitoredResources.values( msg.destinationResource() ).toSet() );
        }
      }

      if ( !mMonitoredMimeTypes.isEmpty() && !mMonitoredCollections.isEmpty() ) {
        Q_FOREACH ( NotificationMessageV2::Id id, ids ) {
          sources.unite( mMonitoredCollections.values( static_cast<Entity::Id>( id ) ).toSet() );
        }

        sources.unite( mMonitoredCollections.values( static_cast<Entity::Id>( msg.parentCollection() ) ).toSet() );
        sources.unite( mMonitoredCollections.values( static_cast<Entity::Id>( msg.parentDestCollection() ) ).toSet() );
        sources.unite( mMonitoredCollections.values( static_cast<Entity::Id>( 0 ) ).toSet() );
      }

    } break;
  }

  return sources;
}

void NotificationManager::emitPendingNotifications()
{
  if ( mNotifications.isEmpty() )
    return;

  NotificationMessage::List legacyNotifications;
  Q_FOREACH ( const NotificationMessageV2 &notification, mNotifications ) {
    Tracer::self()->signal( "NotificationManager::notify", notification.toString() );

    if ( ClientCapabilityAggregator::minimumNotificationMessageVersion() < 2 ) {
      const NotificationMessage::List tmp = notification.toNotificationV1().toList();
      Q_FOREACH ( const NotificationMessage &legacyNotification, tmp ) {
        bool appended = false;
        NotificationMessage::appendAndCompress( legacyNotifications, legacyNotification, &appended );
        if ( !appended ) {
          legacyNotifications << legacyNotification;
        }
      }
    }
  }

  if ( !legacyNotifications.isEmpty() ) {
    Q_FOREACH ( NotificationSource *src, mNotificationSources ) {
      src->emitNotification( legacyNotifications );
    }
  }

  if ( ClientCapabilityAggregator::maximumNotificationMessageVersion() > 1 ) {
    Q_FOREACH ( NotificationSource *source, mClientSideMonitoredSources ) {
      source->emitNotification( mNotifications );
    }

    Q_FOREACH ( const NotificationMessageV2 &notification, mNotifications ) {

      QSet<NotificationSource*> sources = findInterestedSources( notification );

      QList<NotificationSource*> ignoredSources = mIgnoredSessions.values( notification.sessionId() );
      Q_FOREACH ( NotificationSource *source, sources ) {
        if ( !ignoredSources.contains( source ) ) {
          source->emitNotification( NotificationMessageV2::List() << notification );
        }
      }
    }
  }

  // backward compatibility with the old non-subcription interface
  // FIXME: Can we drop this already?
  if ( !legacyNotifications.isEmpty() )
    Q_EMIT notify( legacyNotifications );

  mNotifications.clear();
}

QDBusObjectPath NotificationManager::subscribeV2( const QString &identifier, bool serverSideMonitor )
{
  akDebug() << Q_FUNC_INFO << this << identifier << serverSideMonitor;

  NotificationSource *source = mNotificationSources.value( identifier );
  if ( source ) {
    akDebug() << "Known subscriber" << identifier << "subscribes again";
    source->addClientServiceName( message().service() );
  } else {
    source = new NotificationSource( identifier, message().service(), this );
  }

  registerSource( source, serverSideMonitor );

  Q_EMIT subscribed( identifier );

  return source->dbusPath();
}

void NotificationManager::registerSource( NotificationSource* source,
                                          bool serverSideMonitor )
{
  mNotificationSources.insert( source->identifier(), source );
  if ( !serverSideMonitor && !mClientSideMonitoredSources.contains( source ) ) {
    mClientSideMonitoredSources.insert( source );
  }
}

QDBusObjectPath NotificationManager::subscribe( const QString &identifier )
{
  akDebug() << Q_FUNC_INFO << this << identifier;
  return subscribeV2( identifier, false );
}

void NotificationManager::unsubscribe( const QString &identifier )
{
  NotificationSource *source = mNotificationSources.value( identifier );
  if ( source ) {
    unregisterSource( source );
    source->deleteLater();
    Q_EMIT unsubscribed( identifier );
  } else {
    akDebug() << "Attempt to unsubscribe unknown subscriber" << identifier;
  }
}

void NotificationManager::unregisterSource( NotificationSource *source )
{
  mNotificationSources.remove( source->identifier() );
  mClientSideMonitoredSources.remove( source );
  mAllMonitored.remove( source );
  Q_FOREACH ( const QByteArray &key, mIgnoredSessions.keys( source ) ) {
    mIgnoredSessions.remove( key, source );
  }
  Q_FOREACH ( const QByteArray &resource, mMonitoredResources.keys( source ) ) {
    mMonitoredResources.remove( resource, source );
  }
  Q_FOREACH ( const QString &mimeType, mMonitoredMimeTypes.keys( source ) ) {
    mMonitoredMimeTypes.remove( mimeType, source );
  }
  Q_FOREACH ( Entity::Id id, mMonitoredItems.keys( source ) ) {
    mMonitoredItems.remove( id, source );
  }
  Q_FOREACH ( Entity::Id id, mMonitoredCollections.keys( source ) ) {
    mMonitoredCollections.remove( id, source );
  }
}

QStringList NotificationManager::subscribers() const
{
  QStringList identifiers;
  Q_FOREACH ( NotificationSource *source, mNotificationSources ) {
    identifiers << source->identifier();
  }

  return identifiers;
}

<commit_msg>Fix a bug in Collection notifications filtering<commit_after>/*
    Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org>
    Copyright (c) 2010 Michael Jansen <kde@michael-jansen>

    This library 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 library 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 Library General Public
    License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "notificationmanager.h"
#include <akdebug.h>
#include "notificationmanageradaptor.h"
#include "notificationsource.h"
#include "tracer.h"
#include "storage/datastore.h"
#include "clientcapabilityaggregator.h"

#include <akstandarddirs.h>
#include <libs/xdgbasedirs_p.h>

#include <QtCore/QDebug>
#include <QDBusConnection>
#include <QSettings>

using namespace Akonadi;

NotificationManager* NotificationManager::mSelf = 0;

NotificationManager::NotificationManager()
  : QObject( 0 )
{
  NotificationMessage::registerDBusTypes();
  NotificationMessageV2::registerDBusTypes();

  new NotificationManagerAdaptor( this );
  QDBusConnection::sessionBus().registerObject( QLatin1String("/notifications"),
    this, QDBusConnection::ExportAdaptors );
  QDBusConnection::sessionBus().registerObject( QLatin1String("/notifications/debug"),
    this, QDBusConnection::ExportScriptableSlots );

  const QString serverConfigFile = AkStandardDirs::serverConfigFile( XdgBaseDirs::ReadWrite );
  QSettings settings( serverConfigFile, QSettings::IniFormat );

  mTimer.setInterval( settings.value( QLatin1String("NotificationManager/Interval"), 50 ).toInt() );
  mTimer.setSingleShot( true );
  connect( &mTimer, SIGNAL(timeout()), SLOT(emitPendingNotifications()) );
}

NotificationManager::~NotificationManager()
{
}

NotificationManager* NotificationManager::self()
{
  if ( !mSelf )
    mSelf = new NotificationManager();

  return mSelf;
}

void NotificationManager::connectNotificationCollector(NotificationCollector* collector)
{
  connect( collector, SIGNAL(notify(Akonadi::NotificationMessageV2::List)),
           SLOT(slotNotify(Akonadi::NotificationMessageV2::List)) );
}

void NotificationManager::slotNotify(const Akonadi::NotificationMessageV2::List &msgs)
{
  //akDebug() << Q_FUNC_INFO << "Appending" << msgs.count() << "notifications to current list of " << mNotifications.count() << "notifications";
  Q_FOREACH ( const NotificationMessageV2 &msg, msgs )
    NotificationMessageV2::appendAndCompress( mNotifications, msg );
  //akDebug() << Q_FUNC_INFO << "We have" << mNotifications.count() << "notifications queued in total after appendAndCompress()";

  if ( !mTimer.isActive() )
    mTimer.start();
}

QSet< NotificationSource* > NotificationManager::findInterestedSources( const NotificationMessageV2 &msg )
{
  QSet<NotificationSource*> sources;

  if ( msg.entities().count() == 0 ) {
    return sources;
  }

  if ( msg.type() == NotificationMessageV2::InvalidType ) {
    akDebug() << "Received invalid change notification";
    return sources;
  }

  sources.unite( mAllMonitored );
  if ( msg.operation() == NotificationMessageV2::Move ) {
    sources.unite( mMonitoredResources.values( msg.destinationResource() ).toSet() );
  }

  switch ( msg.type() ) {
    case NotificationMessageV2::InvalidType:
      return sources;

    case NotificationMessageV2::Items: {
      const QList<NotificationMessageV2::Entity> entities = msg.entities().values();
      if ( !mMonitoredResources.isEmpty() || !mMonitoredMimeTypes.isEmpty() ) {
        QSet<NotificationSource*> srcs;
        srcs.unite( mMonitoredResources.values( msg.resource() ).toSet() );
        if ( msg.operation() == NotificationMessageV2::Move ) {
          srcs.unite( mMonitoredResources.values( msg.destinationResource() ) .toSet() );
        }

        if ( !mMonitoredMimeTypes.isEmpty() ) {
          Q_FOREACH ( const NotificationMessageV2::Entity &entity, entities ) {
            srcs.unite( mMonitoredMimeTypes.values( entity.mimeType ).toSet() );
          }
        }

        if ( srcs.isEmpty() ) {
          return sources;
        }

        sources.unite( srcs );

      }
      if ( !mMonitoredItems.isEmpty() ) {
        Q_FOREACH ( const NotificationMessageV2::Entity &entity, entities ) {
          sources.unite( mMonitoredMimeTypes.values( entity.mimeType ).toSet() );
        }
      }

      sources.unite( mMonitoredCollections.values( static_cast<Entity::Id>( msg.parentCollection() ) ).toSet() );
      sources.unite( mMonitoredCollections.values( static_cast<Entity::Id>( msg.parentDestCollection() ) ).toSet() );
      // If the resource is watching root collection, then it wants to be notified
      // about all changes in it's subcollections
      sources.unite( mMonitoredCollections.values( static_cast<Entity::Id>( 0 ) ).toSet() );

    } break;

    case NotificationMessageV2::Collections: {
      const QList<NotificationMessageV2::Id> ids = msg.entities().uniqueKeys();
      if ( !mMonitoredResources.isEmpty() ) {
        sources.unite( mMonitoredResources.values( msg.resource() ).toSet() );
        if ( msg.operation() == NotificationMessageV2::Move ) {
          sources.unite( mMonitoredResources.values( msg.destinationResource() ).toSet() );
        }
      }

      if ( !mMonitoredCollections.isEmpty() ) {
        Q_FOREACH ( NotificationMessageV2::Id id, ids ) {
          sources.unite( mMonitoredCollections.values( static_cast<Entity::Id>( id ) ).toSet() );
        }

        sources.unite( mMonitoredCollections.values( static_cast<Entity::Id>( msg.parentCollection() ) ).toSet() );
        sources.unite( mMonitoredCollections.values( static_cast<Entity::Id>( msg.parentDestCollection() ) ).toSet() );
        sources.unite( mMonitoredCollections.values( static_cast<Entity::Id>( 0 ) ).toSet() );
      }

    } break;
  }

  return sources;
}

void NotificationManager::emitPendingNotifications()
{
  if ( mNotifications.isEmpty() )
    return;

  NotificationMessage::List legacyNotifications;
  Q_FOREACH ( const NotificationMessageV2 &notification, mNotifications ) {
    Tracer::self()->signal( "NotificationManager::notify", notification.toString() );

    if ( ClientCapabilityAggregator::minimumNotificationMessageVersion() < 2 ) {
      const NotificationMessage::List tmp = notification.toNotificationV1().toList();
      Q_FOREACH ( const NotificationMessage &legacyNotification, tmp ) {
        bool appended = false;
        NotificationMessage::appendAndCompress( legacyNotifications, legacyNotification, &appended );
        if ( !appended ) {
          legacyNotifications << legacyNotification;
        }
      }
    }
  }

  if ( !legacyNotifications.isEmpty() ) {
    Q_FOREACH ( NotificationSource *src, mNotificationSources ) {
      src->emitNotification( legacyNotifications );
    }
  }

  if ( ClientCapabilityAggregator::maximumNotificationMessageVersion() > 1 ) {
    Q_FOREACH ( NotificationSource *source, mClientSideMonitoredSources ) {
      source->emitNotification( mNotifications );
    }

    Q_FOREACH ( const NotificationMessageV2 &notification, mNotifications ) {

      QSet<NotificationSource*> sources = findInterestedSources( notification );

      QList<NotificationSource*> ignoredSources = mIgnoredSessions.values( notification.sessionId() );
      Q_FOREACH ( NotificationSource *source, sources ) {
        if ( !ignoredSources.contains( source ) ) {
          source->emitNotification( NotificationMessageV2::List() << notification );
        }
      }
    }
  }

  // backward compatibility with the old non-subcription interface
  // FIXME: Can we drop this already?
  if ( !legacyNotifications.isEmpty() )
    Q_EMIT notify( legacyNotifications );

  mNotifications.clear();
}

QDBusObjectPath NotificationManager::subscribeV2( const QString &identifier, bool serverSideMonitor )
{
  akDebug() << Q_FUNC_INFO << this << identifier << serverSideMonitor;

  NotificationSource *source = mNotificationSources.value( identifier );
  if ( source ) {
    akDebug() << "Known subscriber" << identifier << "subscribes again";
    source->addClientServiceName( message().service() );
  } else {
    source = new NotificationSource( identifier, message().service(), this );
  }

  registerSource( source, serverSideMonitor );

  Q_EMIT subscribed( identifier );

  return source->dbusPath();
}

void NotificationManager::registerSource( NotificationSource* source,
                                          bool serverSideMonitor )
{
  mNotificationSources.insert( source->identifier(), source );
  if ( !serverSideMonitor && !mClientSideMonitoredSources.contains( source ) ) {
    mClientSideMonitoredSources.insert( source );
  }
}

QDBusObjectPath NotificationManager::subscribe( const QString &identifier )
{
  akDebug() << Q_FUNC_INFO << this << identifier;
  return subscribeV2( identifier, false );
}

void NotificationManager::unsubscribe( const QString &identifier )
{
  NotificationSource *source = mNotificationSources.value( identifier );
  if ( source ) {
    unregisterSource( source );
    source->deleteLater();
    Q_EMIT unsubscribed( identifier );
  } else {
    akDebug() << "Attempt to unsubscribe unknown subscriber" << identifier;
  }
}

void NotificationManager::unregisterSource( NotificationSource *source )
{
  mNotificationSources.remove( source->identifier() );
  mClientSideMonitoredSources.remove( source );
  mAllMonitored.remove( source );
  Q_FOREACH ( const QByteArray &key, mIgnoredSessions.keys( source ) ) {
    mIgnoredSessions.remove( key, source );
  }
  Q_FOREACH ( const QByteArray &resource, mMonitoredResources.keys( source ) ) {
    mMonitoredResources.remove( resource, source );
  }
  Q_FOREACH ( const QString &mimeType, mMonitoredMimeTypes.keys( source ) ) {
    mMonitoredMimeTypes.remove( mimeType, source );
  }
  Q_FOREACH ( Entity::Id id, mMonitoredItems.keys( source ) ) {
    mMonitoredItems.remove( id, source );
  }
  Q_FOREACH ( Entity::Id id, mMonitoredCollections.keys( source ) ) {
    mMonitoredCollections.remove( id, source );
  }
}

QStringList NotificationManager::subscribers() const
{
  QStringList identifiers;
  Q_FOREACH ( NotificationSource *source, mNotificationSources ) {
    identifiers << source->identifier();
  }

  return identifiers;
}

<|endoftext|>


<commit_before>#include "util/common.hpp"
#include "engine.hpp"
#include "window.hpp"
#include "app.hpp"
#include "plugin.hpp"
#include "settings.hpp"
#include "asset.hpp"
#include "bridge.hpp"
#include "midi.hpp"
#include "rtmidi.hpp"
#include "keyboard.hpp"
#include "gamepad.hpp"
#include "util/color.hpp"

#include "osdialog.h"
#include <unistd.h>

using namespace rack;

int main(int argc, char* argv[]) {
	bool devMode = false;
	std::string patchFile;

	// Parse command line arguments
	int c;
	opterr = 0;
	while ((c = getopt(argc, argv, "dg:l:")) != -1) {
		switch (c) {
			case 'd': {
				devMode = true;
			} break;
			case 'g': {
				assetGlobalDir = optarg;
			} break;
			case 'l': {
				assetLocalDir = optarg;
			} break;
			default: break;
		}
	}
	if (optind < argc) {
		patchFile = argv[optind];
	}

	// Initialize environment
	randomInit();
	assetInit(devMode);
	loggerInit(devMode);

	// Log environment
	info("%s %s", gApplicationName.c_str(), gApplicationVersion.c_str());
	if (devMode)
		info("Development mode");
	info("Global directory: %s", assetGlobal("").c_str());
	info("Local directory: %s", assetLocal("").c_str());

	// Initialize app
	pluginInit(devMode);
	engineInit();
	rtmidiInit();
	bridgeInit();
	keyboardInit();
	gamepadInit();
	windowInit();
	appInit(devMode);
	settingsLoad(assetLocal("settings.json"));

	if (patchFile.empty()) {
		// To prevent launch crashes, if Rack crashes between now and 15 seconds from now, the "skipAutosaveOnLaunch" property will remain in settings.json, so that in the next launch, the broken autosave will not be loaded.
		bool oldSkipAutosaveOnLaunch = gSkipAutosaveOnLaunch;
		gSkipAutosaveOnLaunch = true;
		settingsSave(assetLocal("settings.json"));
		gSkipAutosaveOnLaunch = false;
		if (oldSkipAutosaveOnLaunch && osdialog_message(OSDIALOG_INFO, OSDIALOG_YES_NO, "Rack has recovered from a crash, possibly caused by a faulty module in your patch. Clear your patch and start over?")) {
			gRackWidget->lastPath = "";
		}
		else {
			// Load autosave
			std::string oldLastPath = gRackWidget->lastPath;
			gRackWidget->load(assetLocal("autosave.vcv"));
			gRackWidget->lastPath = oldLastPath;
		}
	}
	else {
		// Load patch
		gRackWidget->load(patchFile);
	}

	engineStart();
	windowRun();
	engineStop();

	// Destroy namespaces
	gRackWidget->save(assetLocal("autosave.vcv"));
	settingsSave(assetLocal("settings.json"));
	appDestroy();
	windowDestroy();
	bridgeDestroy();
	engineDestroy();
	midiDestroy();
	pluginDestroy();
	loggerDestroy();

	return 0;
}
<commit_msg>Prevent multiple Rack instances on Windows<commit_after>#include "util/common.hpp"
#include "engine.hpp"
#include "window.hpp"
#include "app.hpp"
#include "plugin.hpp"
#include "settings.hpp"
#include "asset.hpp"
#include "bridge.hpp"
#include "midi.hpp"
#include "rtmidi.hpp"
#include "keyboard.hpp"
#include "gamepad.hpp"
#include "util/color.hpp"

#include "osdialog.h"
#include <unistd.h>

#ifdef ARCH_WIN
	#include <Windows.h>
#endif

using namespace rack;

int main(int argc, char* argv[]) {
	bool devMode = false;
	std::string patchFile;

	// Parse command line arguments
	int c;
	opterr = 0;
	while ((c = getopt(argc, argv, "dg:l:")) != -1) {
		switch (c) {
			case 'd': {
				devMode = true;
			} break;
			case 'g': {
				assetGlobalDir = optarg;
			} break;
			case 'l': {
				assetLocalDir = optarg;
			} break;
			default: break;
		}
	}
	if (optind < argc) {
		patchFile = argv[optind];
	}

#ifdef ARCH_WIN
	// Windows global mutex to prevent multiple instances
	// Handle will be closed by Windows when the process ends
	HANDLE instanceMutex = CreateMutex(NULL, true, gApplicationName.c_str());
	if (GetLastError() == ERROR_ALREADY_EXISTS) {
		osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Rack is already running. Multiple Rack instances are not supported.");
		exit(1);
	}
	(void) instanceMutex;
#endif

	// Initialize environment
	randomInit();
	assetInit(devMode);
	loggerInit(devMode);

	// Log environment
	info("%s %s", gApplicationName.c_str(), gApplicationVersion.c_str());
	if (devMode)
		info("Development mode");
	info("Global directory: %s", assetGlobal("").c_str());
	info("Local directory: %s", assetLocal("").c_str());

	// Initialize app
	pluginInit(devMode);
	engineInit();
	rtmidiInit();
	bridgeInit();
	keyboardInit();
	gamepadInit();
	windowInit();
	appInit(devMode);
	settingsLoad(assetLocal("settings.json"));

	if (patchFile.empty()) {
		// To prevent launch crashes, if Rack crashes between now and 15 seconds from now, the "skipAutosaveOnLaunch" property will remain in settings.json, so that in the next launch, the broken autosave will not be loaded.
		bool oldSkipAutosaveOnLaunch = gSkipAutosaveOnLaunch;
		gSkipAutosaveOnLaunch = true;
		settingsSave(assetLocal("settings.json"));
		gSkipAutosaveOnLaunch = false;
		if (oldSkipAutosaveOnLaunch && osdialog_message(OSDIALOG_INFO, OSDIALOG_YES_NO, "Rack has recovered from a crash, possibly caused by a faulty module in your patch. Clear your patch and start over?")) {
			gRackWidget->lastPath = "";
		}
		else {
			// Load autosave
			std::string oldLastPath = gRackWidget->lastPath;
			gRackWidget->load(assetLocal("autosave.vcv"));
			gRackWidget->lastPath = oldLastPath;
		}
	}
	else {
		// Load patch
		gRackWidget->load(patchFile);
	}

	engineStart();
	windowRun();
	engineStop();

	// Destroy namespaces
	gRackWidget->save(assetLocal("autosave.vcv"));
	settingsSave(assetLocal("settings.json"));
	appDestroy();
	windowDestroy();
	bridgeDestroy();
	engineDestroy();
	midiDestroy();
	pluginDestroy();
	loggerDestroy();

	return 0;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <signal.h>
#include <sys/resource.h>

#include "bpforc.h"
#include "bpftrace.h"
#include "clang_parser.h"
#include "codegen_llvm.h"
#include "driver.h"
#include "list.h"
#include "printer.h"
#include "semantic_analyser.h"
#include "tracepoint_format_parser.h"

using namespace bpftrace;

void usage()
{
  std::cerr << "USAGE:" << std::endl;
  std::cerr << "    bpftrace [options] filename" << std::endl;
  std::cerr << "    bpftrace [options] -e 'program'" << std::endl << std::endl;
  std::cerr << "OPTIONS:" << std::endl;
  std::cerr << "    -d             debug info dry run" << std::endl;
  std::cerr << "    -dd            verbose debug info dry run" << std::endl;
  std::cerr << "    -e 'program'   execute this program" << std::endl;
  std::cerr << "    -h             show this help message" << std::endl;
  std::cerr << "    -l [search]    list probes" << std::endl;
  std::cerr << "    -p PID         PID for enabling USDT probes" << std::endl;
  std::cerr << "    -v             verbose messages" << std::endl << std::endl;
  std::cerr << "EXAMPLES:" << std::endl;
  std::cerr << "bpftrace -l '*sleep*'" << std::endl;
  std::cerr << "    list probes containing \"sleep\"" << std::endl;
  std::cerr << "bpftrace -e 'kprobe:do_nanosleep { printf(\"PID %d sleeping...\\n\", pid); }'" << std::endl;
  std::cerr << "    trace processes calling sleep" << std::endl;
  std::cerr << "bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'" << std::endl;
  std::cerr << "    count syscalls by process name" << std::endl;
}

static void enforce_infinite_rlimit() {
  struct rlimit rl = {};
  if (getrlimit(RLIMIT_MEMLOCK, &rl) != 0) {
    std::cerr << "Warning: couldn't set RLIMIT for bpftrace. " <<
        "If your program is not loading, you can try " <<
        "\"ulimit -l 8192\" to fix the problem" << std::endl;
    return;
  }
  rl.rlim_max = RLIM_INFINITY;
  rl.rlim_cur = rl.rlim_max;
  if (setrlimit(RLIMIT_MEMLOCK, &rl) != 0)
    std::cerr << "Warning: couldn't set RLIMIT for bpftrace. " <<
        "If your program is not loading, you can try " <<
        "\"ulimit -l 8192\" to fix the problem" << std::endl;
}

int main(int argc, char *argv[])
{
  int err;
  Driver driver;
  char *pid_str = NULL;
  bool listing = false;

  std::string script, search;
  int c;
  while ((c = getopt(argc, argv, "de:hlp:v")) != -1)
  {
    switch (c)
    {
      case 'd':
        bt_debug++;
        if (bt_debug == DebugLevel::kNone) {
          usage();
          return 1;
        }
        break;
      case 'v':
        bt_verbose = true;
        break;
      case 'e':
        script = optarg;
        break;
      case 'p':
        pid_str = optarg;
        break;
      case 'l':
        listing = true;
        break;
      default:
        usage();
        return 1;
    }
  }

  if (bt_verbose && (bt_debug != DebugLevel::kNone))
  {
    // TODO: allow both
    std::cerr << "USAGE: Use either -v or -d." << std::endl;
    return 1;
  }

  // Listing probes
  if (listing)
  {
    if (optind == argc-1)
      list_probes(argv[optind]);
    else if (optind == argc)
      list_probes();
    else
    {
      usage();
    }
    return 0;
  }

  if (script.empty())
  {
    // There should only be 1 non-option argument (the script file)
    if (optind != argc-1)
    {
      usage();
      return 1;
    }
    char *file_name = argv[optind];
    err = driver.parse_file(file_name);
  }
  else
  {
    // Script is provided as a command line argument
    if (optind != argc)
    {
      usage();
      return 1;
    }
    err = driver.parse_str(script);
  }

  if (err)
    return err;

  // FIXME (mmarchini): maybe we don't want to always enforce an infinite
  // rlimit?
  enforce_infinite_rlimit();

  BPFtrace bpftrace;

  // defaults
  bpftrace.join_argnum_ = 16;
  bpftrace.join_argsize_ = 1024;

  // PID is currently only used for USDT probes that need enabling. Future work:
  // - make PID a filter for all probe types: pass to perf_event_open(), etc.
  // - provide PID in USDT probe specification as a way to override -p.
  bpftrace.pid_ = 0;
  if (pid_str)
    bpftrace.pid_ = atoi(pid_str);

  TracepointFormatParser::parse(driver.root_);

  if (bt_debug != DebugLevel::kNone)
  {
    ast::Printer p(std::cout);
    driver.root_->accept(p);
    std::cout << std::endl;
  }

  ClangParser clang;
  clang.parse(driver.root_, bpftrace.structs_);

  ast::SemanticAnalyser semantics(driver.root_, bpftrace);
  err = semantics.analyse();
  if (err)
    return err;

  err = semantics.create_maps(bt_debug != DebugLevel::kNone);
  if (err)
    return err;

  ast::CodegenLLVM llvm(driver.root_, bpftrace);
  auto bpforc = llvm.compile(bt_debug);

  if (bt_debug != DebugLevel::kNone)
    return 0;

  // Empty signal handler for cleanly terminating the program
  struct sigaction act = {};
  act.sa_handler = [](int) { };
  sigaction(SIGINT, &act, NULL);

  int num_probes = bpftrace.num_probes();
  if (num_probes == 0)
  {
    std::cout << "No probes to attach" << std::endl;
    return 1;
  }
  else if (num_probes == 1)
    std::cout << "Attaching " << bpftrace.num_probes() << " probe..." << std::endl;
  else
    std::cout << "Attaching " << bpftrace.num_probes() << " probes..." << std::endl;

  err = bpftrace.run(move(bpforc));
  if (err)
    return err;

  std::cout << "\n\n";

  err = bpftrace.print_maps();
  if (err)
    return err;

  return 0;
}
<commit_msg>fixes: https://github.com/iovisor/bpftrace/issues/293<commit_after>#include <iostream>
#include <signal.h>
#include <sys/resource.h>
#include <unistd.h>

#include "bpforc.h"
#include "bpftrace.h"
#include "clang_parser.h"
#include "codegen_llvm.h"
#include "driver.h"
#include "list.h"
#include "printer.h"
#include "semantic_analyser.h"
#include "tracepoint_format_parser.h"

using namespace bpftrace;

void usage()
{
  std::cerr << "USAGE:" << std::endl;
  std::cerr << "    bpftrace [options] filename" << std::endl;
  std::cerr << "    bpftrace [options] -e 'program'" << std::endl << std::endl;
  std::cerr << "OPTIONS:" << std::endl;
  std::cerr << "    -d             debug info dry run" << std::endl;
  std::cerr << "    -dd            verbose debug info dry run" << std::endl;
  std::cerr << "    -e 'program'   execute this program" << std::endl;
  std::cerr << "    -h             show this help message" << std::endl;
  std::cerr << "    -l [search]    list probes" << std::endl;
  std::cerr << "    -p PID         PID for enabling USDT probes" << std::endl;
  std::cerr << "    -v             verbose messages" << std::endl << std::endl;
  std::cerr << "EXAMPLES:" << std::endl;
  std::cerr << "bpftrace -l '*sleep*'" << std::endl;
  std::cerr << "    list probes containing \"sleep\"" << std::endl;
  std::cerr << "bpftrace -e 'kprobe:do_nanosleep { printf(\"PID %d sleeping...\\n\", pid); }'" << std::endl;
  std::cerr << "    trace processes calling sleep" << std::endl;
  std::cerr << "bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'" << std::endl;
  std::cerr << "    count syscalls by process name" << std::endl;
}

static void enforce_infinite_rlimit() {
  struct rlimit rl = {};
  if (getrlimit(RLIMIT_MEMLOCK, &rl) != 0) {
    std::cerr << "Warning: couldn't set RLIMIT for bpftrace. " <<
        "If your program is not loading, you can try " <<
        "\"ulimit -l 8192\" to fix the problem" << std::endl;
    return;
  }
  rl.rlim_max = RLIM_INFINITY;
  rl.rlim_cur = rl.rlim_max;
  if (setrlimit(RLIMIT_MEMLOCK, &rl) != 0)
    std::cerr << "Warning: couldn't set RLIMIT for bpftrace. " <<
        "If your program is not loading, you can try " <<
        "\"ulimit -l 8192\" to fix the problem" << std::endl;
}

int main(int argc, char *argv[])
{
  if (geteuid() != 0)
  {
    std::cerr << "ERROR: bpftrace currently only supports running as the root user." << std::endl;
    exit(0);
  }

  int err;
  Driver driver;
  char *pid_str = NULL;
  bool listing = false;

  std::string script, search;
  int c;
  while ((c = getopt(argc, argv, "de:hlp:v")) != -1)
  {
    switch (c)
    {
      case 'd':
        bt_debug++;
        if (bt_debug == DebugLevel::kNone) {
          usage();
          return 1;
        }
        break;
      case 'v':
        bt_verbose = true;
        break;
      case 'e':
        script = optarg;
        break;
      case 'p':
        pid_str = optarg;
        break;
      case 'l':
        listing = true;
        break;
      default:
        usage();
        return 1;
    }
  }

  if (bt_verbose && (bt_debug != DebugLevel::kNone))
  {
    // TODO: allow both
    std::cerr << "USAGE: Use either -v or -d." << std::endl;
    return 1;
  }

  // Listing probes
  if (listing)
  {
    if (optind == argc-1)
      list_probes(argv[optind]);
    else if (optind == argc)
      list_probes();
    else
    {
      usage();
    }
    return 0;
  }

  if (script.empty())
  {
    // There should only be 1 non-option argument (the script file)
    if (optind != argc-1)
    {
      usage();
      return 1;
    }
    char *file_name = argv[optind];
    err = driver.parse_file(file_name);
  }
  else
  {
    // Script is provided as a command line argument
    if (optind != argc)
    {
      usage();
      return 1;
    }
    err = driver.parse_str(script);
  }

  if (err)
    return err;

  // FIXME (mmarchini): maybe we don't want to always enforce an infinite
  // rlimit?
  enforce_infinite_rlimit();

  BPFtrace bpftrace;

  // defaults
  bpftrace.join_argnum_ = 16;
  bpftrace.join_argsize_ = 1024;

  // PID is currently only used for USDT probes that need enabling. Future work:
  // - make PID a filter for all probe types: pass to perf_event_open(), etc.
  // - provide PID in USDT probe specification as a way to override -p.
  bpftrace.pid_ = 0;
  if (pid_str)
    bpftrace.pid_ = atoi(pid_str);

  TracepointFormatParser::parse(driver.root_);

  if (bt_debug != DebugLevel::kNone)
  {
    ast::Printer p(std::cout);
    driver.root_->accept(p);
    std::cout << std::endl;
  }

  ClangParser clang;
  clang.parse(driver.root_, bpftrace.structs_);

  ast::SemanticAnalyser semantics(driver.root_, bpftrace);
  err = semantics.analyse();
  if (err)
    return err;

  err = semantics.create_maps(bt_debug != DebugLevel::kNone);
  if (err)
    return err;

  ast::CodegenLLVM llvm(driver.root_, bpftrace);
  auto bpforc = llvm.compile(bt_debug);

  if (bt_debug != DebugLevel::kNone)
    return 0;

  // Empty signal handler for cleanly terminating the program
  struct sigaction act = {};
  act.sa_handler = [](int) { };
  sigaction(SIGINT, &act, NULL);

  int num_probes = bpftrace.num_probes();
  if (num_probes == 0)
  {
    std::cout << "No probes to attach" << std::endl;
    return 1;
  }
  else if (num_probes == 1)
    std::cout << "Attaching " << bpftrace.num_probes() << " probe..." << std::endl;
  else
    std::cout << "Attaching " << bpftrace.num_probes() << " probes..." << std::endl;

  err = bpftrace.run(move(bpforc));
  if (err)
    return err;

  std::cout << "\n\n";

  err = bpftrace.print_maps();
  if (err)
    return err;

  return 0;
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: commitlistener.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: rt $ $Date: 2004-11-09 15:37:24 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include "commitlistener.hxx"

using namespace ::com::sun::star;

OChildCommitListen_Impl::OChildCommitListen_Impl( SfxBaseModel& aModel )
: m_pModel( &aModel )
{}

OChildCommitListen_Impl::~OChildCommitListen_Impl()
{}

void OChildCommitListen_Impl::OwnerIsDisposed()
{
    ::osl::MutexGuard aGuard( m_aMutex );
    m_pModel = NULL;
}

void SAL_CALL OChildCommitListen_Impl::preCommit( const ::com::sun::star::lang::EventObject& aEvent )
        throw (::com::sun::star::uno::Exception, ::com::sun::star::uno::RuntimeException)
{
    // not interesting
}

void SAL_CALL OChildCommitListen_Impl::commited( const ::com::sun::star::lang::EventObject& aEvent )
        throw (::com::sun::star::uno::RuntimeException)
{
    ::osl::MutexGuard aGuard( m_aMutex );
    // StorageIsModified_Impl must not contain any locking!
    if ( m_pModel )
        m_pModel->StorageIsModified_Impl();
}

void SAL_CALL OChildCommitListen_Impl::preRevert( const ::com::sun::star::lang::EventObject& aEvent )
        throw (::com::sun::star::uno::Exception, ::com::sun::star::uno::RuntimeException)
{
    // not interesting
}

void SAL_CALL OChildCommitListen_Impl::reverted( const ::com::sun::star::lang::EventObject& aEvent )
        throw (::com::sun::star::uno::RuntimeException)
{
    // not interesting
}

void SAL_CALL OChildCommitListen_Impl::disposing( const lang::EventObject& Source )
        throw ( uno::RuntimeException )
{
    // not interesting
}

<commit_msg>INTEGRATION: CWS ooo19126 (1.3.352); FILE MERGED 2005/09/05 15:23:03 rt 1.3.352.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: commitlistener.cxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-07 18:36:50 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include "commitlistener.hxx"

using namespace ::com::sun::star;

OChildCommitListen_Impl::OChildCommitListen_Impl( SfxBaseModel& aModel )
: m_pModel( &aModel )
{}

OChildCommitListen_Impl::~OChildCommitListen_Impl()
{}

void OChildCommitListen_Impl::OwnerIsDisposed()
{
    ::osl::MutexGuard aGuard( m_aMutex );
    m_pModel = NULL;
}

void SAL_CALL OChildCommitListen_Impl::preCommit( const ::com::sun::star::lang::EventObject& aEvent )
        throw (::com::sun::star::uno::Exception, ::com::sun::star::uno::RuntimeException)
{
    // not interesting
}

void SAL_CALL OChildCommitListen_Impl::commited( const ::com::sun::star::lang::EventObject& aEvent )
        throw (::com::sun::star::uno::RuntimeException)
{
    ::osl::MutexGuard aGuard( m_aMutex );
    // StorageIsModified_Impl must not contain any locking!
    if ( m_pModel )
        m_pModel->StorageIsModified_Impl();
}

void SAL_CALL OChildCommitListen_Impl::preRevert( const ::com::sun::star::lang::EventObject& aEvent )
        throw (::com::sun::star::uno::Exception, ::com::sun::star::uno::RuntimeException)
{
    // not interesting
}

void SAL_CALL OChildCommitListen_Impl::reverted( const ::com::sun::star::lang::EventObject& aEvent )
        throw (::com::sun::star::uno::RuntimeException)
{
    // not interesting
}

void SAL_CALL OChildCommitListen_Impl::disposing( const lang::EventObject& Source )
        throw ( uno::RuntimeException )
{
    // not interesting
}

<|endoftext|>


<commit_before>#include <iostream>

using std::cout;
using std::endl;

int main()
{
	return 0;
}
<commit_msg>Add copyright notice.<commit_after>// Copyright 2014 Matthew Harvey

#include <iostream>

using std::cout;
using std::endl;

int main()
{
	return 0;
}
<|endoftext|>


<commit_before>/*
 * This material contains unpublished, proprietary software of
 * Entropic Research Laboratory, Inc. Any reproduction, distribution,
 * or publication of this work must be authorized in writing by Entropic
 * Research Laboratory, Inc., and must bear the notice:
 *
 *    "Copyright (c) 1990-1996 Entropic Research Laboratory, Inc.
 *                   All rights reserved"
 *
 * The copyright notice above does not evidence any actual or intended
 * publication of this source code.
 *
 * Written by:  Derek Lin
 * Checked by:
 * Revised by:  David Talkin
 *
 * Brief description:  Estimates F0 using normalized cross correlation and
 *   dynamic programming.
 *
 */

#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <limits.h>
#include <stdexcept>
#include <sstream>
#include <vector>

#include "f0.h"

int	    debug_level = 0;

// ----------------------------------------
// Externs
extern "C" {
int init_dp_f0(double freq, F0_params *par, long *buffsize, long *sdstep);
int dp_f0(float *fdata, int buff_size, int sdstep, double freq, F0_params *par,
          float **f0p_pt, float **vuvp_pt, float **rms_speech_pt,
          float **acpkp_pt, int *vecsize, int last_time);
}

struct f0_params;

namespace GetF0 {

// EXCEPTIONS

#define CREATE_ERROR(_Name, _Base)                                   \
  class _Name : public _Base {                                       \
  public:                                                            \
    explicit _Name(const std::string &what_arg) : _Base(what_arg) {} \
  };

CREATE_ERROR(RuntimeError, std::runtime_error);
CREATE_ERROR(LogicError, std::logic_error);
CREATE_ERROR(ParameterError, RuntimeError);
CREATE_ERROR(ProcessingError, RuntimeError);
CREATE_ERROR(AssertionError, LogicError);

#undef CREATE_ERROR

#define THROW_ERROR(condition, exception, s) \
  do {                                       \
    if (condition) {                         \
      std::stringstream ss;                  \
      ss << s;                               \
      throw exception(ss.str());             \
    }                                        \
  } while (0);

class GetF0 {
public:
  GetF0();

  void resetParameters();

  int derp();

protected:

  /// @brief Provide a `buffer` we can read `num_records` samples
  /// from, returning how many samples we can read. Returning less
  /// than requested samples is a termination condition.
  ///
  /// `buffer` is not guaranteed to not be written to. (TODO: check to
  /// see if buffer can be written to.)
  virtual long read_samples(float **buffer, long num_records) { return 0; }

  /// @brief Like `read_samples`, but read `step` samples from
  /// previous buffer.
  virtual long read_samples_overlap(float **buffer, long num_records, long step)
  {
    return 0;
  }

  virtual void writeOutput(float *f0p, float *vuvp, float *rms_speech,
                           float *acpkp, int vecsize)
  {
  }

private:

  F0_params m_par;

  static void check_f0_params(F0_params *par, double sample_freq);

};

GetF0::GetF0()
{
  resetParameters();
}

void GetF0::resetParameters()
{
  m_par.cand_thresh = 0.3;
  m_par.lag_weight = 0.3;
  m_par.freq_weight = 0.02;
  m_par.trans_cost = 0.005;
  m_par.trans_amp = 0.5;
  m_par.trans_spec = 0.5;
  m_par.voice_bias = 0.0;
  m_par.double_cost = 0.35;
  m_par.min_f0 = 50;
  m_par.max_f0 = 550;
  m_par.frame_step = 0.01;
  m_par.wind_dur = 0.0075;
  m_par.n_cands = 20;
  m_par.mean_f0 = 200;        /* unused */
  m_par.mean_f0_weight = 0.0; /* unused */
  m_par.conditioning = 0;     /*unused */
}

int GetF0::derp()
{
  int done;
  long buff_size, actsize;
  double sf, output_starts, frame_rate;
  float *f0p, *vuvp, *rms_speech, *acpkp;
  int i, vecsize;
  long sdstep = 0;

#define SW_CUSTOMIZABLE(x) //TODO(sw)
  SW_CUSTOMIZABLE(debug_level);

  SW_CUSTOMIZABLE(m_par.frame_step);
  SW_CUSTOMIZABLE(m_par.cand_thresh);
  SW_CUSTOMIZABLE(m_par.lag_weight);
  SW_CUSTOMIZABLE(m_par.freq_weight);
  SW_CUSTOMIZABLE(m_par.trans_cost);
  SW_CUSTOMIZABLE(m_par.trans_amp);
  SW_CUSTOMIZABLE(m_par.trans_spec);
  SW_CUSTOMIZABLE(m_par.voice_bias);
  SW_CUSTOMIZABLE(m_par.double_cost);
  SW_CUSTOMIZABLE(m_par.min_f0);
  SW_CUSTOMIZABLE(m_par.max_f0);
  SW_CUSTOMIZABLE(m_par.wind_dur);
  SW_CUSTOMIZABLE(m_par.n_cands);
#undef SW_CUSTOMIZABLE

#define SW_FILE_PARAMS(x, y) //TODO (sw)
  SW_FILE_PARAMS(sf, "sampling frequency");
#undef SW_FILE_PARAMS

  check_f0_params(&m_par, sf);

  /*SW: Removed range restricter, but this may be interesting:
    if (total_samps < ((par->frame_step * 2.0) + par->wind_dur) * sf), then
      input range too small*/

  output_starts = m_par.wind_dur/2.0;
  /* Average delay due to loc. of ref. window center. */
  frame_rate = 1.0 / m_par.frame_step;

  /* Initialize variables in get_f0.c; allocate data structures;
   * determine length and overlap of input frames to read.
   *
   * sw: Looks like init_dp_f0 never returns errors via rcode, but put
   * under assertion.
   */
  THROW_ERROR(init_dp_f0(sf, &m_par, &buff_size, &sdstep) ||
                  buff_size > INT_MAX || sdstep > INT_MAX,
              AssertionError, "problem in init_dp_f0().");

  /*SW: pass sdstep to caller so it knows how much we have to buffer. */

  if (debug_level)
    Fprintf(stderr, "init_dp_f0 returned buff_size %ld, sdstep %ld.\n",
	    buff_size, sdstep);

  float* fdata = nullptr;
  actsize = read_samples(&fdata, buff_size);

  while (1) {

    done = (actsize < buff_size);

    THROW_ERROR(dp_f0(fdata, (int)actsize, (int)sdstep, sf, &m_par, &f0p, &vuvp,
                      &rms_speech, &acpkp, &vecsize, done),
                ProcessingError, "problem in dp_f0().");

    writeOutput(f0p, vuvp, rms_speech, acpkp, vecsize);

    if (done)
      break;

    actsize = read_samples_overlap(&fdata, buff_size, sdstep);

  }

  exit(0);
}

/*
 * Some consistency checks on parameter values.
 */
void GetF0::check_f0_params(F0_params *par, double sample_freq)
{
  std::vector<std::string> errors;

  if ((par->cand_thresh < 0.01) || (par->cand_thresh > 0.99)) {
    errors.push_back("cand_thresh parameter must be between [0.01, 0.99].");
  }
  if ((par->wind_dur > .1) || (par->wind_dur < .0001)) {
    errors.push_back("wind_dur parameter must be between [0.0001, 0.1].");
  }
  if ((par->n_cands > 100) || (par->n_cands < 3)) {
    errors.push_back("n_cands parameter must be between [3,100].");
  }
  if ((par->max_f0 <= par->min_f0) || (par->max_f0 >= (sample_freq / 2.0)) ||
      (par->min_f0 < (sample_freq / 10000.0))) {
    errors.push_back(
        "min(max)_f0 parameter inconsistent with sampling frequency.");
  }
  double dstep =
      ((double)((int)(0.5 + (sample_freq * par->frame_step)))) / sample_freq;
  if (dstep != par->frame_step) {
    if (debug_level)
      Fprintf(stderr,
              "Frame step set to %f to exactly match signal sample rate.\n",
              dstep);
    par->frame_step = dstep;
  }
  if ((par->frame_step > 0.1) || (par->frame_step < (1.0 / sample_freq))) {
    errors.push_back(
        "frame_step parameter must be between [1/sampling rate, "
        "0.1].");
  }

  if (!errors.empty()) {
    std::stringstream ss;
    bool first = true;
    for (auto &error : errors) {
      if (!first) ss << " ";
      ss << error;
    }

    THROW_ERROR(true, ParameterError, ss.str());
  }
}

} // namespace GetF0
<commit_msg>checkParameters<commit_after>/*
 * This material contains unpublished, proprietary software of
 * Entropic Research Laboratory, Inc. Any reproduction, distribution,
 * or publication of this work must be authorized in writing by Entropic
 * Research Laboratory, Inc., and must bear the notice:
 *
 *    "Copyright (c) 1990-1996 Entropic Research Laboratory, Inc.
 *                   All rights reserved"
 *
 * The copyright notice above does not evidence any actual or intended
 * publication of this source code.
 *
 * Written by:  Derek Lin
 * Checked by:
 * Revised by:  David Talkin
 *
 * Brief description:  Estimates F0 using normalized cross correlation and
 *   dynamic programming.
 *
 */

#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <limits.h>
#include <stdexcept>
#include <sstream>
#include <vector>

#include "f0.h"

int	    debug_level = 0;

// ----------------------------------------
// Externs
extern "C" {
int init_dp_f0(double freq, F0_params *par, long *buffsize, long *sdstep);
int dp_f0(float *fdata, int buff_size, int sdstep, double freq, F0_params *par,
          float **f0p_pt, float **vuvp_pt, float **rms_speech_pt,
          float **acpkp_pt, int *vecsize, int last_time);
}

struct f0_params;

namespace GetF0 {

// EXCEPTIONS

#define CREATE_ERROR(_Name, _Base)                                   \
  class _Name : public _Base {                                       \
  public:                                                            \
    explicit _Name(const std::string &what_arg) : _Base(what_arg) {} \
  };

CREATE_ERROR(RuntimeError, std::runtime_error);
CREATE_ERROR(LogicError, std::logic_error);
CREATE_ERROR(ParameterError, RuntimeError);
CREATE_ERROR(ProcessingError, RuntimeError);
CREATE_ERROR(AssertionError, LogicError);

#undef CREATE_ERROR

#define THROW_ERROR(condition, exception, s) \
  do {                                       \
    if (condition) {                         \
      std::stringstream ss;                  \
      ss << s;                               \
      throw exception(ss.str());             \
    }                                        \
  } while (0);

class GetF0 {
public:
  GetF0();

  void resetParameters();

  /// @brief Some consistency checks on parameter values. Throws
  /// ParameterError if there's something wrong.
  void checkParameters(double sample_freq);

  int derp();

protected:

  /// @brief Provide a `buffer` we can read `num_records` samples
  /// from, returning how many samples we can read. Returning less
  /// than requested samples is a termination condition.
  ///
  /// `buffer` is not guaranteed to not be written to. (TODO: check to
  /// see if buffer can be written to.)
  virtual long read_samples(float **buffer, long num_records) { return 0; }

  /// @brief Like `read_samples`, but read `step` samples from
  /// previous buffer.
  virtual long read_samples_overlap(float **buffer, long num_records, long step)
  {
    return 0;
  }

  virtual void writeOutput(float *f0p, float *vuvp, float *rms_speech,
                           float *acpkp, int vecsize)
  {
  }

private:

  f0_params m_par;

};

GetF0::GetF0()
{
  resetParameters();
}

void GetF0::resetParameters()
{
  m_par.cand_thresh = 0.3;
  m_par.lag_weight = 0.3;
  m_par.freq_weight = 0.02;
  m_par.trans_cost = 0.005;
  m_par.trans_amp = 0.5;
  m_par.trans_spec = 0.5;
  m_par.voice_bias = 0.0;
  m_par.double_cost = 0.35;
  m_par.min_f0 = 50;
  m_par.max_f0 = 550;
  m_par.frame_step = 0.01;
  m_par.wind_dur = 0.0075;
  m_par.n_cands = 20;
  m_par.mean_f0 = 200;        /* unused */
  m_par.mean_f0_weight = 0.0; /* unused */
  m_par.conditioning = 0;     /*unused */
}

int GetF0::derp()
{
  int done;
  long buff_size, actsize;
  double sf, output_starts, frame_rate;
  float *f0p, *vuvp, *rms_speech, *acpkp;
  int i, vecsize;
  long sdstep = 0;

#define SW_CUSTOMIZABLE(x) //TODO(sw)
  SW_CUSTOMIZABLE(debug_level);

  SW_CUSTOMIZABLE(m_par.frame_step);
  SW_CUSTOMIZABLE(m_par.cand_thresh);
  SW_CUSTOMIZABLE(m_par.lag_weight);
  SW_CUSTOMIZABLE(m_par.freq_weight);
  SW_CUSTOMIZABLE(m_par.trans_cost);
  SW_CUSTOMIZABLE(m_par.trans_amp);
  SW_CUSTOMIZABLE(m_par.trans_spec);
  SW_CUSTOMIZABLE(m_par.voice_bias);
  SW_CUSTOMIZABLE(m_par.double_cost);
  SW_CUSTOMIZABLE(m_par.min_f0);
  SW_CUSTOMIZABLE(m_par.max_f0);
  SW_CUSTOMIZABLE(m_par.wind_dur);
  SW_CUSTOMIZABLE(m_par.n_cands);
#undef SW_CUSTOMIZABLE

#define SW_FILE_PARAMS(x, y) //TODO (sw)
  SW_FILE_PARAMS(sf, "sampling frequency");
#undef SW_FILE_PARAMS

  checkParameters(sf);

  /*SW: Removed range restricter, but this may be interesting:
    if (total_samps < ((par->frame_step * 2.0) + par->wind_dur) * sf), then
      input range too small*/

  output_starts = m_par.wind_dur/2.0;
  /* Average delay due to loc. of ref. window center. */
  frame_rate = 1.0 / m_par.frame_step;

  /* Initialize variables in get_f0.c; allocate data structures;
   * determine length and overlap of input frames to read.
   *
   * sw: Looks like init_dp_f0 never returns errors via rcode, but put
   * under assertion.
   */
  THROW_ERROR(init_dp_f0(sf, &m_par, &buff_size, &sdstep) ||
                  buff_size > INT_MAX || sdstep > INT_MAX,
              AssertionError, "problem in init_dp_f0().");

  /*SW: pass sdstep to caller so it knows how much we have to buffer. */

  if (debug_level)
    Fprintf(stderr, "init_dp_f0 returned buff_size %ld, sdstep %ld.\n",
	    buff_size, sdstep);

  float* fdata = nullptr;
  actsize = read_samples(&fdata, buff_size);

  while (1) {

    done = (actsize < buff_size);

    THROW_ERROR(dp_f0(fdata, (int)actsize, (int)sdstep, sf, &m_par, &f0p, &vuvp,
                      &rms_speech, &acpkp, &vecsize, done),
                ProcessingError, "problem in dp_f0().");

    writeOutput(f0p, vuvp, rms_speech, acpkp, vecsize);

    if (done)
      break;

    actsize = read_samples_overlap(&fdata, buff_size, sdstep);

  }

  exit(0);
}

void GetF0::checkParameters(double sample_freq)
{
  std::vector<std::string> errors;

  if ((m_par.cand_thresh < 0.01) || (m_par.cand_thresh > 0.99)) {
    errors.push_back("cand_thresh parameter must be between [0.01, 0.99].");
  }
  if ((m_par.wind_dur > .1) || (m_par.wind_dur < .0001)) {
    errors.push_back("wind_dur parameter must be between [0.0001, 0.1].");
  }
  if ((m_par.n_cands > 100) || (m_par.n_cands < 3)) {
    errors.push_back("n_cands parameter must be between [3,100].");
  }
  if ((m_par.max_f0 <= m_par.min_f0) || (m_par.max_f0 >= (sample_freq / 2.0)) ||
      (m_par.min_f0 < (sample_freq / 10000.0))) {
    errors.push_back(
        "min(max)_f0 parameter inconsistent with sampling frequency.");
  }
  double dstep =
      ((double)((int)(0.5 + (sample_freq * m_par.frame_step)))) / sample_freq;
  if (dstep != m_par.frame_step) {
    if (debug_level)
      Fprintf(stderr,
              "Frame step set to %f to exactly match signal sample rate.\n",
              dstep);
    m_par.frame_step = dstep;
  }
  if ((m_par.frame_step > 0.1) || (m_par.frame_step < (1.0 / sample_freq))) {
    errors.push_back(
        "frame_step parameter must be between [1/sampling rate, "
        "0.1].");
  }

  if (!errors.empty()) {
    std::stringstream ss;
    bool first = true;
    for (auto &error : errors) {
      if (!first) ss << " ";
      ss << error;
    }

    THROW_ERROR(true, ParameterError, ss.str());
  }
}

} // namespace GetF0
<|endoftext|>


<commit_before>#include <Arduino.h>
#include <ESP8266WebServer.h>

#include <Logger.h>
#include <Network.h>
#include "HTTPServer.h"
#include "HTMLProvider.h"
#include "GPIO.h"
#include "MQTT.h"
#include "Settings.h"
#include "UPnP.h"

//#define SERIAL_OUTPUT
//#define DEBUG
//#define FULLDEBUG

ESP8266WebServer server(80);

Storage _storage;
Settings _settings(&_storage);
Logger _logger(&_settings);
Network _network(&_settings);
HTMLProvider _htmlProvider(&_settings);
MQTT _mqtt(&_settings);
GPIO _gpio(&_settings, &_logger, &_mqtt);
Commands _commands(&_settings, &_gpio);
HTTPServer _http(&server, &_settings, &_logger, &_htmlProvider, &_commands, &_storage);
UPnP _upnp(&_logger, &_http, &_settings, &_htmlProvider, &_commands, &_gpio);

void setup()
{
  using namespace std::placeholders; // for `_1`

#ifdef SERIAL_OUTPUT
  Serial.begin(74880);
#endif

  pinMode(PIN_A0, INPUT);

  _gpio.setup();
  Serial.println("starting initialization ...");
  _gpio.led(true);
  _settings.load();
  _network.setup();
  _logger.setup(_settings.hostName);
  _logger.writeLog(LOG_NOTICE, "starting up");
  _upnp.setup();
  _http.setup();
  _mqtt.setup(std::bind(&Commands::execute, _commands, _1, _2));
  _gpio.led(false);
  _gpio.restoreLastState();
  Serial.println("initialization finished.");
  _logger.writeLog(LOG_INFO, "started successfully");
}

void loop()
{
  _gpio.loop();
  _network.loop();
  _http.loop();
  _upnp.loop();
  _mqtt.loop();
}
<commit_msg>removed left-over poc code<commit_after>#include <Arduino.h>
#include <ESP8266WebServer.h>

#include <Logger.h>
#include <Network.h>
#include "HTTPServer.h"
#include "HTMLProvider.h"
#include "GPIO.h"
#include "MQTT.h"
#include "Settings.h"
#include "UPnP.h"

//#define SERIAL_OUTPUT
//#define DEBUG
//#define FULLDEBUG

ESP8266WebServer server(80);

Storage _storage;
Settings _settings(&_storage);
Logger _logger(&_settings);
Network _network(&_settings);
HTMLProvider _htmlProvider(&_settings);
MQTT _mqtt(&_settings);
GPIO _gpio(&_settings, &_logger, &_mqtt);
Commands _commands(&_settings, &_gpio);
HTTPServer _http(&server, &_settings, &_logger, &_htmlProvider, &_commands, &_storage);
UPnP _upnp(&_logger, &_http, &_settings, &_htmlProvider, &_commands, &_gpio);

void setup()
{
  using namespace std::placeholders; // for `_1`

#ifdef SERIAL_OUTPUT
  Serial.begin(74880);
#endif

  _gpio.setup();
  Serial.println("starting initialization ...");
  _gpio.led(true);
  _settings.load();
  _network.setup();
  _logger.setup(_settings.hostName);
  _logger.writeLog(LOG_NOTICE, "starting up");
  _upnp.setup();
  _http.setup();
  _mqtt.setup(std::bind(&Commands::execute, _commands, _1, _2));
  _gpio.led(false);
  _gpio.restoreLastState();
  Serial.println("initialization finished.");
  _logger.writeLog(LOG_INFO, "started successfully");
}

void loop()
{
  _gpio.loop();
  _network.loop();
  _http.loop();
  _upnp.loop();
  _mqtt.loop();
}
<|endoftext|>


<commit_before>#include "http.h"
#include <fcntl.h>
#include <fstream>

#ifdef _WIN32
#   include <io.h>
#endif

namespace enji {

int cb_http_message_begin(http_parser* parser) {
    return 0;
}

int cb_http_url(http_parser* parser, const char* at, size_t len) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_http_url(at, len);
}

int cb_http_status(http_parser* parser, const char* at, size_t len) {
    std::cerr << "Got unhandled status: " << String(at, len) << std::endl;
    return 0;
}

int cb_http_header_field(http_parser* parser, const char* at, size_t len) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_http_header_field(at, len);
}

int cb_http_header_value(http_parser* parser, const char* at, size_t len) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_http_header_value(at, len);
}

int cb_http_headers_complete(http_parser* parser) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_http_headers_complete();
}

int cb_http_body(http_parser* parser, const char* at, size_t len) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_http_body(at, len);
}

int cb_http_message_complete(http_parser* parser) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_message_complete();
}

http_parser_settings& get_http_settings() {
    static http_parser_settings http_settings = {};
    http_settings.on_message_begin = cb_http_message_begin;
    http_settings.on_url = cb_http_url;
    http_settings.on_status = cb_http_status;
    http_settings.on_header_field = cb_http_header_field;
    http_settings.on_header_value = cb_http_header_value;
    http_settings.on_headers_complete = cb_http_headers_complete;
    http_settings.on_body = cb_http_body;
    http_settings.on_message_complete = cb_http_message_complete;
    return http_settings;
}

HttpConnection::HttpConnection(HttpServer* parent, size_t id)
:   Connection(parent, id),
    parent_(parent) {
    parser_.reset(new http_parser{});
    http_parser_init(parser_.get(), HTTP_REQUEST);
    parser_.get()->data = this;

    request_.reset(new HttpRequest{});
}

const HttpRequest& HttpConnection::request() const {
    return *request_.get();
}

int HttpConnection::on_http_url(const char* at, size_t len) {
    request_->url_ += String{at, len};
    return 0;
}

int HttpConnection::on_http_header_field(const char* at, size_t len) {
    check_header_finished();
    read_header_.first += String{at, len};
    return 0;
}

int HttpConnection::on_http_header_value(const char* at, size_t len) {
    read_header_.second += String{at, len};
    return 0;
}

int HttpConnection::on_http_headers_complete() {
    check_header_finished();
    return 0;
}

int HttpConnection::on_http_body(const char* at, size_t len) {
    request_->body_ += String{at, len};
    return 0;
}

const String MULTIPART_FORM_DATA = "multipart/form-data; boundary=";

int HttpConnection::on_message_complete() {
    message_completed_ = true;

    auto expect_iter = request_->headers_.find("Expect");

    if (expect_iter != request_->headers_.end()) {
        if (expect_iter->second == "100-continue") {
            message_completed_ = false;
            std::ostringstream buf;
            buf << "HTTP/1.1 100 Continue\r\n";
            write_chunk(buf);
        }
    }

    auto content_type_iter = request_->headers_.find("Content-Type");
    if (content_type_iter != request_->headers_.end()) {
        String boundary;
        auto multipart = content_type_iter->second.find(MULTIPART_FORM_DATA);
        if (multipart != String::npos) {
            auto boundary_start = multipart + MULTIPART_FORM_DATA.size();
            boundary = content_type_iter->second.substr(boundary_start);
        }

        std::vector<size_t> occurrences;
        size_t start = 0;

        boundary = "--" + boundary;

        while ((start = request_->body_.find(boundary, start)) != String::npos) {
            occurrences.push_back(start);
            start += boundary.length();
        }

        const char* body = &request_->body_.front();
        for (size_t occurence = 1; occurence < occurrences.size(); ++occurence) {
            auto file_content = String{
                body + occurrences[occurence - 1] + boundary.length() + 2, // Skip boundary + "\r\n"
                body + occurrences[occurence]
            };

            auto headers_finished = file_content.find("\r\n\r\n");
            auto headers = file_content.substr(0, headers_finished);

            std::regex regex("Content-Disposition: form-data; name=\"(.+)\"; filename=\"(.+)\"");
            std::smatch match_groups;
            std::regex_search(headers, match_groups, regex);

            File file;

            if (!match_groups.empty()) {
                file.name_ = match_groups[1].str();
                file.filename_ = match_groups[2].str();
            }

            if (file.name_.empty() && file.filename_.empty()) {
                continue;
            }

            auto file_body = file_content.substr(headers_finished + 4);

            file.body_ = std::move(file_body);

            request_->files_.emplace_back(std::move(file));
        }
    }

    return 0;
}

void HttpConnection::handle_input(StringView data) {
    http_parser_execute(parser_.get(), &get_http_settings(), data.data, data.size);

    if (message_completed_) {
        request_->method_ = http_method_str(static_cast<http_method>(parser_.get()->method));
        parent_->call_handler(*request_.get(), this);
    }
}

void HttpConnection::check_header_finished() {
    if (!read_header_.first.empty() && !read_header_.second.empty()) {
        request_->headers_.insert(Header{std::move(read_header_)});
    }
}

HttpResponse::HttpResponse(HttpConnection* conn)
:   conn_{conn},
    response_{std::stringstream::in | std::stringstream::out | std::stringstream::binary},
    headers_{std::stringstream::in | std::stringstream::out | std::stringstream::binary},
    body_{std::stringstream::in | std::stringstream::out | std::stringstream::binary},
    full_response_{std::stringstream::in | std::stringstream::out | std::stringstream::binary} {
}

HttpResponse::~HttpResponse() {
    close();
}

HttpResponse& HttpResponse::response(int code) {
    code_ = code;
    response_ << "HTTP/1.1 " << code << "\r\n";
    return *this;
}

HttpResponse& HttpResponse::add_headers(std::vector<std::pair<String, String>> headers) {
    for (auto&& h : headers) {
        add_header(h.first, h.second);
    }
    return *this;
}

HttpResponse& HttpResponse::add_header(const String& name, const String& value) {
    if (headers_sent_) {
        throw std::runtime_error("Can't add headers to response. Headers already sent");
    }
    headers_ << name << ": " << value << "\r\n";
    return *this;
}

bool stream2stream(std::stringstream& output, std::stringstream& input) {
    const size_t alloc_block = 32 * 1024;
    char tmp[alloc_block];
    bool written_smth = false;
    while (input) {
        input.read(tmp, sizeof(tmp));
        size_t size = input.gcount();
        output.write(tmp, size);
        if (size) {
            written_smth = true;
        }
    }
    return written_smth;
}

void stream2conn(Connection* conn, std::stringstream& buf) {
    while (buf) {
        size_t alloc_block = 32 * 1024;
        char* data = new char[alloc_block];
        buf.read(data, alloc_block);
        const size_t size = buf.gcount();
        TransferBlock block = {data, size};
        conn->write_chunk(block);
    }
}

HttpResponse& HttpResponse::body(const String& value) {
    body_ << value;
    return *this;
}

HttpResponse& HttpResponse::body(std::stringstream& buf) {
    stream2stream(body_, buf);
    return *this;
}

HttpResponse& HttpResponse::body(const void* data, size_t length) {
    body_.write(static_cast<const char*>(data), length);
    return *this;
}

void HttpResponse::flush() {
    if (!headers_sent_) {
        if (!stream2stream(full_response_, response_)) {
            full_response_ << "HTTP/1.1 200\r\n";
        }

        body_.seekp(0, std::ios::end);
        auto body_size = body_.tellp();

        std::ostringstream body_size_stream;
        body_size_stream << body_size;
        add_header("Content-length", body_size_stream.str());

        stream2stream(full_response_, headers_);
        headers_sent_ = true;

        full_response_ << "\r\n";
    }

    stream2stream(full_response_, body_);
    stream2conn(conn_, full_response_);
}

void HttpResponse::close() {
    flush();
    conn_->close();
}

HttpRoute::HttpRoute(const char* path, Handler handler)
:   path_{path},
    handler_{handler},
    path_match_(path) {
}

HttpRoute::HttpRoute(String&& path, Handler handler)
:   path_{path},
    handler_{handler},
    path_match_(path) {
}

HttpRoute::HttpRoute(const char* path, FuncHandler handler)
:   path_{path},
    handler_{handler},
    path_match_(path) {
}

HttpRoute::HttpRoute(String&& path, FuncHandler handler)
:   path_{path},
    handler_{handler},
    path_match_(path) {
}

std::smatch HttpRoute::match(const String& url) const {
    std::smatch match_groups;
    std::regex_search(url, match_groups, path_match_);
    return match_groups;
}

void HttpRoute::call_handler(const HttpRequest& req, HttpResponse& out) {
    handler_(req, out);
}

HttpServer::HttpServer(ServerOptions&& options)
:   Server{std::move(options)} {
    create_connection([this]() {
        return std::make_shared<HttpConnection>(this, counter_++); });
}

void HttpServer::routes(std::vector<HttpRoute>&& routes) {
    routes_ = std::move(routes);
}

void HttpServer::add_route(HttpRoute&& route) {
    routes_.emplace_back(route);
}

void HttpServer::call_handler(HttpRequest& request, HttpConnection* bind) {
    bool matched = false;
    HttpResponse out{bind};

    for (auto&& route : routes_) {
        auto matches = route.match(request.url());
        if (!matches.empty()) {
            request.set_match(matches);
            route.call_handler(request, out);
            matched = true;
        }
    }

    if (!matched) {
        out.response(404);
    }

    std::cout << request.method() << " " << request.url() << " " << out.code() << std::endl;
}

String match1_filename(const HttpRequest& req) {
   return req.match()[1].str();
}

HttpRoute::Handler serve_static(std::function<String(const HttpRequest& req)> request2file, const Config& config) {
    //auto request_func = std::function<String(const HttpRequest& req)>{std::move(request2file)};
    return HttpRoute::Handler{
        [&] (const HttpRequest& req, HttpResponse& out)
    {
        static_file(request2file(req), out, config);
    }};
}

HttpRoute::Handler serve_static(const String& root_dir, std::function<String(const HttpRequest& req)> request2file) {
    String dir = root_dir;
    auto request_func = std::function<String(const HttpRequest& req)>{std::move(request2file)};
    return HttpRoute::Handler{
        [&request_func, &dir]
    (const HttpRequest& req, HttpResponse& out)->void
    {
        String result = request_func(req);
        response_file(path_join(dir, result), out);
    }};
}

void static_file(const String& filename, HttpResponse& out, const Config& config) {
    response_file(path_join(config["STATIC_ROOT_DIR"].str(), filename), out);
}

void response_file(const String& filename, HttpResponse& out) {
    uv_fs_t open_req;
    uv_fs_open(nullptr, &open_req, filename.c_str(), O_RDONLY, S_IRUSR, nullptr);
    auto open_req_exit = Defer{[&open_req] { uv_fs_req_cleanup(&open_req); }};
    const auto fd = static_cast<uv_file>(open_req.result);

    if (fd < 0) {
        out.response(404);
        return;
    }

    uv_fs_t read_req;
    const size_t alloc_block = 64 * 1024;
    size_t offset = 0;
    char mem[alloc_block];
    while (true) {
        uv_buf_t buf[] = {uv_buf_init(mem, sizeof(mem))};
        int read = uv_fs_read(nullptr, &read_req, fd, buf, 1, offset, nullptr);
        auto read_req_exit = Defer{[&read_req] { uv_fs_req_cleanup(&read_req); }};
        out.body(buf[0].base, read);
        offset += read;
        if (static_cast<unsigned int>(read) < buf[0].len) {
            break;
        }
    }

    uv_fs_t close_req;
    uv_fs_close(nullptr, &close_req, fd, nullptr);
    auto close_req_exit = Defer{[&close_req] { uv_fs_req_cleanup(&close_req); }};
}

void temporary_redirect(const String& redirect_to, HttpResponse& out) {
    out.response(307);
    out.add_header("Location", redirect_to);
    out.close();
}

} // namespace enji<commit_msg>Win32 build<commit_after>#include "http.h"
#include <fcntl.h>
#include <fstream>

#ifdef _WIN32
#   include <io.h>
#endif

namespace enji {

int cb_http_message_begin(http_parser* parser) {
    return 0;
}

int cb_http_url(http_parser* parser, const char* at, size_t len) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_http_url(at, len);
}

int cb_http_status(http_parser* parser, const char* at, size_t len) {
    std::cerr << "Got unhandled status: " << String(at, len) << std::endl;
    return 0;
}

int cb_http_header_field(http_parser* parser, const char* at, size_t len) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_http_header_field(at, len);
}

int cb_http_header_value(http_parser* parser, const char* at, size_t len) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_http_header_value(at, len);
}

int cb_http_headers_complete(http_parser* parser) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_http_headers_complete();
}

int cb_http_body(http_parser* parser, const char* at, size_t len) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_http_body(at, len);
}

int cb_http_message_complete(http_parser* parser) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_message_complete();
}

http_parser_settings& get_http_settings() {
    static http_parser_settings http_settings = {};
    http_settings.on_message_begin = cb_http_message_begin;
    http_settings.on_url = cb_http_url;
    http_settings.on_status = cb_http_status;
    http_settings.on_header_field = cb_http_header_field;
    http_settings.on_header_value = cb_http_header_value;
    http_settings.on_headers_complete = cb_http_headers_complete;
    http_settings.on_body = cb_http_body;
    http_settings.on_message_complete = cb_http_message_complete;
    return http_settings;
}

HttpConnection::HttpConnection(HttpServer* parent, size_t id)
:   Connection(parent, id),
    parent_(parent) {
    parser_.reset(new http_parser{});
    http_parser_init(parser_.get(), HTTP_REQUEST);
    parser_.get()->data = this;

    request_.reset(new HttpRequest{});
}

const HttpRequest& HttpConnection::request() const {
    return *request_.get();
}

int HttpConnection::on_http_url(const char* at, size_t len) {
    request_->url_ += String{at, len};
    return 0;
}

int HttpConnection::on_http_header_field(const char* at, size_t len) {
    check_header_finished();
    read_header_.first += String{at, len};
    return 0;
}

int HttpConnection::on_http_header_value(const char* at, size_t len) {
    read_header_.second += String{at, len};
    return 0;
}

int HttpConnection::on_http_headers_complete() {
    check_header_finished();
    return 0;
}

int HttpConnection::on_http_body(const char* at, size_t len) {
    request_->body_ += String{at, len};
    return 0;
}

const String MULTIPART_FORM_DATA = "multipart/form-data; boundary=";

int HttpConnection::on_message_complete() {
    message_completed_ = true;

    auto expect_iter = request_->headers_.find("Expect");

    if (expect_iter != request_->headers_.end()) {
        if (expect_iter->second == "100-continue") {
            message_completed_ = false;
            std::ostringstream buf;
            buf << "HTTP/1.1 100 Continue\r\n";
            write_chunk(buf);
        }
    }

    auto content_type_iter = request_->headers_.find("Content-Type");
    if (content_type_iter != request_->headers_.end()) {
        String boundary;
        auto multipart = content_type_iter->second.find(MULTIPART_FORM_DATA);
        if (multipart != String::npos) {
            auto boundary_start = multipart + MULTIPART_FORM_DATA.size();
            boundary = content_type_iter->second.substr(boundary_start);
        }

        std::vector<size_t> occurrences;
        size_t start = 0;

        boundary = "--" + boundary;

        while ((start = request_->body_.find(boundary, start)) != String::npos) {
            occurrences.push_back(start);
            start += boundary.length();
        }

        const char* body = &request_->body_.front();
        for (size_t occurence = 1; occurence < occurrences.size(); ++occurence) {
            auto file_content = String{
                body + occurrences[occurence - 1] + boundary.length() + 2, // Skip boundary + "\r\n"
                body + occurrences[occurence]
            };

            auto headers_finished = file_content.find("\r\n\r\n");
            auto headers = file_content.substr(0, headers_finished);

            std::regex regex("Content-Disposition: form-data; name=\"(.+)\"; filename=\"(.+)\"");
            std::smatch match_groups;
            std::regex_search(headers, match_groups, regex);

            File file;

            if (!match_groups.empty()) {
                file.name_ = match_groups[1].str();
                file.filename_ = match_groups[2].str();
            }

            if (file.name_.empty() && file.filename_.empty()) {
                continue;
            }

            auto file_body = file_content.substr(headers_finished + 4);

            file.body_ = std::move(file_body);

            request_->files_.emplace_back(std::move(file));
        }
    }

    return 0;
}

void HttpConnection::handle_input(StringView data) {
    http_parser_execute(parser_.get(), &get_http_settings(), data.data, data.size);

    if (message_completed_) {
        request_->method_ = http_method_str(static_cast<http_method>(parser_.get()->method));
        parent_->call_handler(*request_.get(), this);
    }
}

void HttpConnection::check_header_finished() {
    if (!read_header_.first.empty() && !read_header_.second.empty()) {
        request_->headers_.insert(Header{std::move(read_header_)});
    }
}

HttpResponse::HttpResponse(HttpConnection* conn)
:   conn_{conn},
    response_{std::stringstream::in | std::stringstream::out | std::stringstream::binary},
    headers_{std::stringstream::in | std::stringstream::out | std::stringstream::binary},
    body_{std::stringstream::in | std::stringstream::out | std::stringstream::binary},
    full_response_{std::stringstream::in | std::stringstream::out | std::stringstream::binary} {
}

HttpResponse::~HttpResponse() {
    close();
}

HttpResponse& HttpResponse::response(int code) {
    code_ = code;
    response_ << "HTTP/1.1 " << code << "\r\n";
    return *this;
}

HttpResponse& HttpResponse::add_headers(std::vector<std::pair<String, String>> headers) {
    for (auto&& h : headers) {
        add_header(h.first, h.second);
    }
    return *this;
}

HttpResponse& HttpResponse::add_header(const String& name, const String& value) {
    if (headers_sent_) {
        throw std::runtime_error("Can't add headers to response. Headers already sent");
    }
    headers_ << name << ": " << value << "\r\n";
    return *this;
}

bool stream2stream(std::stringstream& output, std::stringstream& input) {
    const size_t alloc_block = 32 * 1024;
    char tmp[alloc_block];
    bool written_smth = false;
    while (input) {
        input.read(tmp, sizeof(tmp));
        size_t size = input.gcount();
        output.write(tmp, size);
        if (size) {
            written_smth = true;
        }
    }
    return written_smth;
}

void stream2conn(Connection* conn, std::stringstream& buf) {
    while (buf) {
        size_t alloc_block = 32 * 1024;
        char* data = new char[alloc_block];
        buf.read(data, alloc_block);
        const size_t size = buf.gcount();
        TransferBlock block = {data, size};
        conn->write_chunk(block);
    }
}

HttpResponse& HttpResponse::body(const String& value) {
    body_ << value;
    return *this;
}

HttpResponse& HttpResponse::body(std::stringstream& buf) {
    stream2stream(body_, buf);
    return *this;
}

HttpResponse& HttpResponse::body(const void* data, size_t length) {
    body_.write(static_cast<const char*>(data), length);
    return *this;
}

void HttpResponse::flush() {
    if (!headers_sent_) {
        if (!stream2stream(full_response_, response_)) {
            full_response_ << "HTTP/1.1 200\r\n";
        }

        body_.seekp(0, std::ios::end);
        auto body_size = body_.tellp();

        std::ostringstream body_size_stream;
        body_size_stream << body_size;
        add_header("Content-length", body_size_stream.str());

        stream2stream(full_response_, headers_);
        headers_sent_ = true;

        full_response_ << "\r\n";
    }

    stream2stream(full_response_, body_);
    stream2conn(conn_, full_response_);
}

void HttpResponse::close() {
    flush();
    conn_->close();
}

HttpRoute::HttpRoute(const char* path, Handler handler)
:   path_{path},
    handler_{handler},
    path_match_(path) {
}

HttpRoute::HttpRoute(String&& path, Handler handler)
:   path_{path},
    handler_{handler},
    path_match_(path) {
}

HttpRoute::HttpRoute(const char* path, FuncHandler handler)
:   path_{path},
    handler_{handler},
    path_match_(path) {
}

HttpRoute::HttpRoute(String&& path, FuncHandler handler)
:   path_{path},
    handler_{handler},
    path_match_(path) {
}

std::smatch HttpRoute::match(const String& url) const {
    std::smatch match_groups;
    std::regex_search(url, match_groups, path_match_);
    return match_groups;
}

void HttpRoute::call_handler(const HttpRequest& req, HttpResponse& out) {
    handler_(req, out);
}

HttpServer::HttpServer(ServerOptions&& options)
:   Server{std::move(options)} {
    create_connection([this]() {
        return std::make_shared<HttpConnection>(this, counter_++); });
}

void HttpServer::routes(std::vector<HttpRoute>&& routes) {
    routes_ = std::move(routes);
}

void HttpServer::add_route(HttpRoute&& route) {
    routes_.emplace_back(route);
}

void HttpServer::call_handler(HttpRequest& request, HttpConnection* bind) {
    bool matched = false;
    HttpResponse out{bind};

    for (auto&& route : routes_) {
        auto matches = route.match(request.url());
        if (!matches.empty()) {
            request.set_match(matches);
            route.call_handler(request, out);
            matched = true;
        }
    }

    if (!matched) {
        out.response(404);
    }

    std::cout << request.method() << " " << request.url() << " " << out.code() << std::endl;
}

String match1_filename(const HttpRequest& req) {
   return req.match()[1].str();
}

HttpRoute::Handler serve_static(std::function<String(const HttpRequest& req)> request2file, const Config& config) {
    //auto request_func = std::function<String(const HttpRequest& req)>{std::move(request2file)};
    return HttpRoute::Handler{
        [&] (const HttpRequest& req, HttpResponse& out)
    {
        static_file(request2file(req), out, config);
    }};
}

HttpRoute::Handler serve_static(const String& root_dir, std::function<String(const HttpRequest& req)> request2file) {
    String dir = root_dir;
    auto request_func = std::function<String(const HttpRequest& req)>{std::move(request2file)};
    return HttpRoute::Handler{
        [&request_func, &dir]
    (const HttpRequest& req, HttpResponse& out)->void
    {
        String result = request_func(req);
        response_file(path_join(dir, result), out);
    }};
}

void static_file(const String& filename, HttpResponse& out, const Config& config) {
    response_file(path_join(config["STATIC_ROOT_DIR"].str(), filename), out);
}

void response_file(const String& filename, HttpResponse& out) {
    uv_fs_t open_req;

#ifdef _WIN32
    int open_mode = _S_IREAD;
#else
    int open_mode = S_IRUSR;
#endif

    uv_fs_open(nullptr, &open_req, filename.c_str(), O_RDONLY, open_mode, nullptr);
    auto open_req_exit = Defer{[&open_req] { uv_fs_req_cleanup(&open_req); }};
    const auto fd = static_cast<uv_file>(open_req.result);

    if (fd < 0) {
        out.response(404);
        return;
    }

    uv_fs_t read_req;
    const size_t alloc_block = 64 * 1024;
    size_t offset = 0;
    char mem[alloc_block];
    while (true) {
        uv_buf_t buf[] = {uv_buf_init(mem, sizeof(mem))};
        int read = uv_fs_read(nullptr, &read_req, fd, buf, 1, offset, nullptr);
        auto read_req_exit = Defer{[&read_req] { uv_fs_req_cleanup(&read_req); }};
        out.body(buf[0].base, read);
        offset += read;
        if (static_cast<unsigned int>(read) < buf[0].len) {
            break;
        }
    }

    uv_fs_t close_req;
    uv_fs_close(nullptr, &close_req, fd, nullptr);
    auto close_req_exit = Defer{[&close_req] { uv_fs_req_cleanup(&close_req); }};
}

void temporary_redirect(const String& redirect_to, HttpResponse& out) {
    out.response(307);
    out.add_header("Location", redirect_to);
    out.close();
}

} // namespace enji<|endoftext|>