forked from dolphin-emu/dolphin
		
	git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@4 8ced0084-cf51-0410-be5f-012b33b47a6e
		
			
				
	
	
		
			551 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			551 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Under MIT licence from http://www.mindcontrol.org/~hplus/http-get.html
 | 
						|
 | 
						|
#include "PortableSockets.h"
 | 
						|
 | 
						|
#if NEED_GETTIMEOFDAY
 | 
						|
#if defined( _WIN32 )
 | 
						|
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/timeb.h>
 | 
						|
#include <math.h>
 | 
						|
#include <assert.h>
 | 
						|
#include <mmsystem.h>
 | 
						|
 | 
						|
#pragma comment( lib, "winmm.lib" )
 | 
						|
//#pragma comment( lib, "ws2_32.lib" )
 | 
						|
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
	// This class could be made cheaper using RDTSC for short-term 
 | 
						|
	// measurement. But, whatever.
 | 
						|
	class init_gettimeofday
 | 
						|
	{
 | 
						|
	public:
 | 
						|
		init_gettimeofday()
 | 
						|
		{
 | 
						|
			timeBeginPeriod( 2 );
 | 
						|
			__int64 rr;
 | 
						|
			QueryPerformanceFrequency( (LARGE_INTEGER *)&rr );
 | 
						|
			ticksPerSecInv_ = 1.0 / (double)((DWORD)rr & 0xffffffff);
 | 
						|
			int watchdog = 0;
 | 
						|
again:
 | 
						|
			lastTicks_ = timeGetTime();
 | 
						|
			QueryPerformanceCounter( (LARGE_INTEGER *)&lastRead_ );
 | 
						|
			timeb tb;
 | 
						|
			ftime( &tb );
 | 
						|
			timeOffset_ = tb.time + tb.millitm * 0.001 - lastRead_ * ticksPerSecInv_;
 | 
						|
			lastTime_ = timeOffset_;
 | 
						|
			//  make sure it didn't take too long
 | 
						|
			if( watchdog++ < 10 && (timeGetTime() != lastTicks_) ) {
 | 
						|
				goto again;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		~init_gettimeofday()
 | 
						|
		{
 | 
						|
			timeEndPeriod( 2 );
 | 
						|
		}
 | 
						|
 | 
						|
		void get( timeval * tv )
 | 
						|
		{
 | 
						|
			__int64 nu;
 | 
						|
			int watchdog = 0;
 | 
						|
again:
 | 
						|
			DWORD m = timeGetTime();
 | 
						|
			QueryPerformanceCounter( (LARGE_INTEGER *)&nu );
 | 
						|
			DWORD n = timeGetTime();
 | 
						|
			// guard against pre-emption
 | 
						|
			if( (watchdog++ < 10) && (n != m) ) {
 | 
						|
				goto again;
 | 
						|
			}
 | 
						|
			double nuTime = nu * ticksPerSecInv_ + timeOffset_;
 | 
						|
			if( (nu - lastRead_) & 0x7fffffff80000000ULL ) {
 | 
						|
				//  there's a chance that we're seeing a jump-ahead
 | 
						|
				double adjust = (nuTime - lastTime_ - (n - lastTicks_) * 0.001);
 | 
						|
				if( adjust > 0.1f ) {
 | 
						|
					timeOffset_ -= adjust;
 | 
						|
					nuTime -= adjust;
 | 
						|
					assert( nuTime >= lastTime_ );
 | 
						|
				}
 | 
						|
			}
 | 
						|
			lastRead_ = nu;
 | 
						|
			lastTicks_ = n;
 | 
						|
			lastTime_ = nuTime;
 | 
						|
			tv->tv_sec = (ulong)floor( nuTime );
 | 
						|
			tv->tv_usec = (ulong)(1000000 * (nuTime - tv->tv_sec));
 | 
						|
		}
 | 
						|
 | 
						|
		double ticksPerSecInv_;
 | 
						|
		double timeOffset_;
 | 
						|
		double lastTime_;
 | 
						|
		__int64 lastRead_;
 | 
						|
		DWORD lastTicks_;
 | 
						|
	};
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
void gettimeofday( timeval * tv, int )
 | 
						|
{
 | 
						|
	static init_gettimeofday data;
 | 
						|
	data.get( tv );
 | 
						|
}
 | 
						|
 | 
						|
#else
 | 
						|
#error "don't know how to do this"
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
#if NEED_WINDOWS_POLL
 | 
						|
#if defined( WIN32 )
 | 
						|
 | 
						|
#include <assert.h>
 | 
						|
#include <winsock2.h>
 | 
						|
#include <windows.h>
 | 
						|
 | 
						|
//  This is somewhat less than ideal -- better would be if we could 
 | 
						|
//  abstract pollfd enough that it's non-copying on Windows.
 | 
						|
int poll( pollfd * iofds, size_t count, int ms )
 | 
						|
{
 | 
						|
	FD_SET rd, wr, ex;
 | 
						|
	FD_ZERO( &rd );
 | 
						|
	FD_ZERO( &wr );
 | 
						|
	FD_ZERO( &ex );
 | 
						|
	SOCKET m = 0;
 | 
						|
	for( size_t ix = 0; ix < count; ++ix ) {
 | 
						|
		iofds[ix].revents = 0;
 | 
						|
		if( iofds[ix].fd >= m ) {
 | 
						|
			m = iofds[ix].fd + 1;
 | 
						|
		}
 | 
						|
		if( iofds[ix].events & (POLLIN | POLLPRI) ) {
 | 
						|
			assert( rd.fd_count < FD_SETSIZE );
 | 
						|
			rd.fd_array[ rd.fd_count++ ] = iofds[ix].fd;
 | 
						|
		}
 | 
						|
		if( iofds[ix].events & (POLLOUT) ) {
 | 
						|
			assert( wr.fd_count < FD_SETSIZE );
 | 
						|
			wr.fd_array[ wr.fd_count++ ] = iofds[ix].fd;
 | 
						|
		}
 | 
						|
		assert( ex.fd_count < FD_SETSIZE );
 | 
						|
		ex.fd_array[ ex.fd_count++ ] = iofds[ix].fd;
 | 
						|
	}
 | 
						|
	timeval tv;
 | 
						|
	tv.tv_sec = ms/1000;
 | 
						|
	tv.tv_usec = (ms - (tv.tv_sec * 1000)) * 1000;
 | 
						|
	int r = 0;
 | 
						|
	if( m == 0 ) {
 | 
						|
		::Sleep( ms );
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		r = ::select( (int)m, (rd.fd_count ? &rd : 0), (wr.fd_count ? &wr : 0), (ex.fd_count ? &ex : 0), &tv );
 | 
						|
	}
 | 
						|
	if( r < 0 ) {
 | 
						|
		int err = WSAGetLastError();
 | 
						|
		errno = err;
 | 
						|
		return r;
 | 
						|
	}
 | 
						|
	r = 0;
 | 
						|
	for( size_t ix = 0; ix < count; ++ix ) {
 | 
						|
		for( size_t iy = 0; iy < rd.fd_count; ++iy ) {
 | 
						|
			if( rd.fd_array[ iy ] == iofds[ix].fd ) {
 | 
						|
				iofds[ix].revents |= POLLIN;
 | 
						|
				++r;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		for( size_t iy = 0; iy < wr.fd_count; ++iy ) {
 | 
						|
			if( wr.fd_array[ iy ] == iofds[ix].fd ) {
 | 
						|
				iofds[ix].revents |= POLLOUT;
 | 
						|
				++r;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		for( size_t iy = 0; iy < ex.fd_count; ++iy ) {
 | 
						|
			if( ex.fd_array[ iy ] == iofds[ix].fd ) {
 | 
						|
				iofds[ix].revents |= POLLERR;
 | 
						|
				++r;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return r;
 | 
						|
}
 | 
						|
 | 
						|
#else
 | 
						|
#error "don't know how to do this"
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
#if NEED_FIREWALL_ENABLE
 | 
						|
#if defined( WIN32 )
 | 
						|
 | 
						|
#undef _WIN32_WINNT
 | 
						|
#define _WIN32_WINNT 0x500
 | 
						|
 | 
						|
#include <objbase.h>
 | 
						|
#include <oleauto.h>
 | 
						|
//#include <netfw.h>
 | 
						|
 | 
						|
/*
 | 
						|
#define _ASSERT assert
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
	HRESULT WindowsFirewallInitialize(OUT INetFwProfile** fwProfile)
 | 
						|
	{
 | 
						|
		HRESULT hr = S_OK;
 | 
						|
		INetFwMgr* fwMgr = NULL;
 | 
						|
		INetFwPolicy* fwPolicy = NULL;
 | 
						|
 | 
						|
		_ASSERT(fwProfile != NULL);
 | 
						|
 | 
						|
		*fwProfile = NULL;
 | 
						|
 | 
						|
		// Create an instance of the firewall settings manager.
 | 
						|
		hr = CoCreateInstance(
 | 
						|
			__uuidof(NetFwMgr),
 | 
						|
			NULL,
 | 
						|
			CLSCTX_INPROC_SERVER,
 | 
						|
			__uuidof(INetFwMgr),
 | 
						|
			(void**)&fwMgr
 | 
						|
			);
 | 
						|
		if (FAILED(hr))
 | 
						|
		{
 | 
						|
			printf("CoCreateInstance failed: 0x%08lx\n", hr);
 | 
						|
			goto error;
 | 
						|
		}
 | 
						|
 | 
						|
		// Retrieve the local firewall policy.
 | 
						|
		hr = fwMgr->get_LocalPolicy(&fwPolicy);
 | 
						|
		if (FAILED(hr))
 | 
						|
		{
 | 
						|
			printf("get_LocalPolicy failed: 0x%08lx\n", hr);
 | 
						|
			goto error;
 | 
						|
		}
 | 
						|
 | 
						|
		// Retrieve the firewall profile currently in effect.
 | 
						|
		hr = fwPolicy->get_CurrentProfile(fwProfile);
 | 
						|
		if (FAILED(hr))
 | 
						|
		{
 | 
						|
			printf("get_CurrentProfile failed: 0x%08lx\n", hr);
 | 
						|
			goto error;
 | 
						|
		}
 | 
						|
 | 
						|
error:
 | 
						|
 | 
						|
		// Release the local firewall policy.
 | 
						|
		if (fwPolicy != NULL)
 | 
						|
		{
 | 
						|
			fwPolicy->Release();
 | 
						|
		}
 | 
						|
 | 
						|
		// Release the firewall settings manager.
 | 
						|
		if (fwMgr != NULL)
 | 
						|
		{
 | 
						|
			fwMgr->Release();
 | 
						|
		}
 | 
						|
 | 
						|
		return hr;
 | 
						|
	}
 | 
						|
 | 
						|
	void WindowsFirewallCleanup(IN INetFwProfile* fwProfile)
 | 
						|
	{
 | 
						|
		// Release the firewall profile.
 | 
						|
		if (fwProfile != NULL)
 | 
						|
		{
 | 
						|
			fwProfile->Release();
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	HRESULT WindowsFirewallAppIsEnabled(
 | 
						|
		IN INetFwProfile* fwProfile,
 | 
						|
		IN const wchar_t* fwProcessImageFileName,
 | 
						|
		OUT BOOL* fwAppEnabled
 | 
						|
		)
 | 
						|
	{
 | 
						|
		HRESULT hr = S_OK;
 | 
						|
		BSTR fwBstrProcessImageFileName = NULL;
 | 
						|
		VARIANT_BOOL fwEnabled;
 | 
						|
		INetFwAuthorizedApplication* fwApp = NULL;
 | 
						|
		INetFwAuthorizedApplications* fwApps = NULL;
 | 
						|
 | 
						|
		_ASSERT(fwProfile != NULL);
 | 
						|
		_ASSERT(fwProcessImageFileName != NULL);
 | 
						|
		_ASSERT(fwAppEnabled != NULL);
 | 
						|
 | 
						|
		*fwAppEnabled = FALSE;
 | 
						|
 | 
						|
		// Retrieve the authorized application collection.
 | 
						|
		hr = fwProfile->get_AuthorizedApplications(&fwApps);
 | 
						|
		if (FAILED(hr))
 | 
						|
		{
 | 
						|
			printf("get_AuthorizedApplications failed: 0x%08lx\n", hr);
 | 
						|
			goto error;
 | 
						|
		}
 | 
						|
 | 
						|
		// Allocate a BSTR for the process image file name.
 | 
						|
		fwBstrProcessImageFileName = SysAllocString(fwProcessImageFileName);
 | 
						|
		if (SysStringLen(fwBstrProcessImageFileName) == 0)
 | 
						|
		{
 | 
						|
			hr = E_OUTOFMEMORY;
 | 
						|
			printf("SysAllocString failed: 0x%08lx\n", hr);
 | 
						|
			goto error;
 | 
						|
		}
 | 
						|
 | 
						|
		// Attempt to retrieve the authorized application.
 | 
						|
		hr = fwApps->Item(fwBstrProcessImageFileName, &fwApp);
 | 
						|
		if (SUCCEEDED(hr))
 | 
						|
		{
 | 
						|
			// Find out if the authorized application is enabled.
 | 
						|
			hr = fwApp->get_Enabled(&fwEnabled);
 | 
						|
			if (FAILED(hr))
 | 
						|
			{
 | 
						|
				printf("get_Enabled failed: 0x%08lx\n", hr);
 | 
						|
				goto error;
 | 
						|
			}
 | 
						|
 | 
						|
			if (fwEnabled != VARIANT_FALSE)
 | 
						|
			{
 | 
						|
				// The authorized application is enabled.
 | 
						|
				*fwAppEnabled = TRUE;
 | 
						|
 | 
						|
				printf(
 | 
						|
					"Authorized application %lS is enabled in the firewall.\n",
 | 
						|
					fwProcessImageFileName
 | 
						|
					);
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				printf(
 | 
						|
					"Authorized application %lS is disabled in the firewall.\n",
 | 
						|
					fwProcessImageFileName
 | 
						|
					);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			// The authorized application was not in the collection.
 | 
						|
			hr = S_OK;
 | 
						|
 | 
						|
			printf(
 | 
						|
				"Authorized application %lS is disabled in the firewall.\n",
 | 
						|
				fwProcessImageFileName
 | 
						|
				);
 | 
						|
		}
 | 
						|
 | 
						|
error:
 | 
						|
 | 
						|
		// Free the BSTR.
 | 
						|
		SysFreeString(fwBstrProcessImageFileName);
 | 
						|
 | 
						|
		// Release the authorized application instance.
 | 
						|
		if (fwApp != NULL)
 | 
						|
		{
 | 
						|
			fwApp->Release();
 | 
						|
		}
 | 
						|
 | 
						|
		// Release the authorized application collection.
 | 
						|
		if (fwApps != NULL)
 | 
						|
		{
 | 
						|
			fwApps->Release();
 | 
						|
		}
 | 
						|
 | 
						|
		return hr;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	HRESULT WindowsFirewallAddApp(
 | 
						|
		IN INetFwProfile* fwProfile,
 | 
						|
		IN const wchar_t* fwProcessImageFileName,
 | 
						|
		IN const wchar_t* fwName
 | 
						|
		)
 | 
						|
	{
 | 
						|
		HRESULT hr = S_OK;
 | 
						|
		BOOL fwAppEnabled;
 | 
						|
		BSTR fwBstrName = NULL;
 | 
						|
		BSTR fwBstrProcessImageFileName = NULL;
 | 
						|
		INetFwAuthorizedApplication* fwApp = NULL;
 | 
						|
		INetFwAuthorizedApplications* fwApps = NULL;
 | 
						|
 | 
						|
		_ASSERT(fwProfile != NULL);
 | 
						|
		_ASSERT(fwProcessImageFileName != NULL);
 | 
						|
		_ASSERT(fwName != NULL);
 | 
						|
 | 
						|
		// First check to see if the application is already authorized.
 | 
						|
		hr = WindowsFirewallAppIsEnabled(
 | 
						|
			fwProfile,
 | 
						|
			fwProcessImageFileName,
 | 
						|
			&fwAppEnabled
 | 
						|
			);
 | 
						|
		if (FAILED(hr))
 | 
						|
		{
 | 
						|
			printf("WindowsFirewallAppIsEnabled failed: 0x%08lx\n", hr);
 | 
						|
			goto error;
 | 
						|
		}
 | 
						|
 | 
						|
		// Only add the application if it isn't already authorized.
 | 
						|
		if (!fwAppEnabled)
 | 
						|
		{
 | 
						|
			// Retrieve the authorized application collection.
 | 
						|
			hr = fwProfile->get_AuthorizedApplications(&fwApps);
 | 
						|
			if (FAILED(hr))
 | 
						|
			{
 | 
						|
				printf("get_AuthorizedApplications failed: 0x%08lx\n", hr);
 | 
						|
				goto error;
 | 
						|
			}
 | 
						|
 | 
						|
			// Create an instance of an authorized application.
 | 
						|
			hr = CoCreateInstance(
 | 
						|
				__uuidof(NetFwAuthorizedApplication),
 | 
						|
				NULL,
 | 
						|
				CLSCTX_INPROC_SERVER,
 | 
						|
				__uuidof(INetFwAuthorizedApplication),
 | 
						|
				(void**)&fwApp
 | 
						|
				);
 | 
						|
			if (FAILED(hr))
 | 
						|
			{
 | 
						|
				printf("CoCreateInstance failed: 0x%08lx\n", hr);
 | 
						|
				goto error;
 | 
						|
			}
 | 
						|
 | 
						|
			// Allocate a BSTR for the process image file name.
 | 
						|
			fwBstrProcessImageFileName = SysAllocString(fwProcessImageFileName);
 | 
						|
			if (SysStringLen(fwBstrProcessImageFileName) == 0)
 | 
						|
			{
 | 
						|
				hr = E_OUTOFMEMORY;
 | 
						|
				printf("SysAllocString failed: 0x%08lx\n", hr);
 | 
						|
				goto error;
 | 
						|
			}
 | 
						|
 | 
						|
			// Set the process image file name.
 | 
						|
			hr = fwApp->put_ProcessImageFileName(fwBstrProcessImageFileName);
 | 
						|
			if (FAILED(hr))
 | 
						|
			{
 | 
						|
				printf("put_ProcessImageFileName failed: 0x%08lx\n", hr);
 | 
						|
				goto error;
 | 
						|
			}
 | 
						|
 | 
						|
			// Allocate a BSTR for the application friendly name.
 | 
						|
			fwBstrName = SysAllocString(fwName);
 | 
						|
			if (SysStringLen(fwBstrName) == 0)
 | 
						|
			{
 | 
						|
				hr = E_OUTOFMEMORY;
 | 
						|
				printf("SysAllocString failed: 0x%08lx\n", hr);
 | 
						|
				goto error;
 | 
						|
			}
 | 
						|
 | 
						|
			// Set the application friendly name.
 | 
						|
			hr = fwApp->put_Name(fwBstrName);
 | 
						|
			if (FAILED(hr))
 | 
						|
			{
 | 
						|
				printf("put_Name failed: 0x%08lx\n", hr);
 | 
						|
				goto error;
 | 
						|
			}
 | 
						|
 | 
						|
			// Add the application to the collection.
 | 
						|
			hr = fwApps->Add(fwApp);
 | 
						|
			if (FAILED(hr))
 | 
						|
			{
 | 
						|
				printf("Add failed: 0x%08lx\n", hr);
 | 
						|
				goto error;
 | 
						|
			}
 | 
						|
 | 
						|
			printf(
 | 
						|
				"Authorized application %lS is now enabled in the firewall.\n",
 | 
						|
				fwProcessImageFileName
 | 
						|
				);
 | 
						|
		}
 | 
						|
 | 
						|
error:
 | 
						|
 | 
						|
		// Free the BSTRs.
 | 
						|
		SysFreeString(fwBstrName);
 | 
						|
		SysFreeString(fwBstrProcessImageFileName);
 | 
						|
 | 
						|
		// Release the authorized application instance.
 | 
						|
		if (fwApp != NULL)
 | 
						|
		{
 | 
						|
			fwApp->Release();
 | 
						|
		}
 | 
						|
 | 
						|
		// Release the authorized application collection.
 | 
						|
		if (fwApps != NULL)
 | 
						|
		{
 | 
						|
			fwApps->Release();
 | 
						|
		}
 | 
						|
 | 
						|
		return hr;
 | 
						|
	}
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
bool ENABLE_FIREWALL()
 | 
						|
{
 | 
						|
	BOOL couldEnable = false;
 | 
						|
	HRESULT hr = S_OK;
 | 
						|
	HRESULT comInit = E_FAIL;
 | 
						|
	INetFwProfile* fwProfile = NULL;
 | 
						|
 | 
						|
	// Initialize COM.
 | 
						|
#if 1
 | 
						|
	comInit = CoInitialize( 0 );
 | 
						|
#else
 | 
						|
	comInit = CoInitializeEx(
 | 
						|
		0,
 | 
						|
		COINIT_APARTMENTTHREADED //| COINIT_DISABLE_OLE1DDE
 | 
						|
		);
 | 
						|
#endif
 | 
						|
 | 
						|
	// Ignore RPC_E_CHANGED_MODE; this just means that COM has already been
 | 
						|
	// initialized with a different mode. Since we don't care what the mode is,
 | 
						|
	// we'll just use the existing mode.
 | 
						|
	if (comInit != RPC_E_CHANGED_MODE) {
 | 
						|
		hr = comInit;
 | 
						|
		if (FAILED(hr)) {
 | 
						|
			fprintf( stderr, "CoInitializeEx failed: 0x%08lx\n", hr );
 | 
						|
			goto error;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Retrieve the firewall profile currently in effect.
 | 
						|
	hr = WindowsFirewallInitialize(&fwProfile);
 | 
						|
	if (FAILED(hr)) {
 | 
						|
		fprintf( stderr, "WindowsFirewallInitialize failed: 0x%08lx\n", hr );
 | 
						|
		goto error;
 | 
						|
	}
 | 
						|
 | 
						|
	HMODULE hm = GetModuleHandle( 0 );
 | 
						|
	wchar_t path[512];
 | 
						|
	if( !GetModuleFileNameW( hm, path, sizeof(path)/sizeof(wchar_t) ) ) {
 | 
						|
		fprintf( stderr, "GetModuleFileName() failed: 0x%lx\n", GetLastError() );
 | 
						|
		goto error;
 | 
						|
	}
 | 
						|
 | 
						|
	// Add the application to the authorized application collection.
 | 
						|
	hr = WindowsFirewallAddApp(
 | 
						|
		fwProfile,
 | 
						|
		path,
 | 
						|
		L"Introduction Library User"
 | 
						|
		);
 | 
						|
	if (FAILED(hr)) {
 | 
						|
		fprintf( stderr, "WindowsFirewallAddApp failed: 0x%08lx\n", hr );
 | 
						|
		goto error;
 | 
						|
	}
 | 
						|
 | 
						|
error:
 | 
						|
 | 
						|
	WindowsFirewallCleanup(fwProfile);
 | 
						|
 | 
						|
	if (SUCCEEDED(comInit)) {
 | 
						|
		CoUninitialize();
 | 
						|
	}
 | 
						|
 | 
						|
	return couldEnable != FALSE;
 | 
						|
}
 | 
						|
*/
 | 
						|
 | 
						|
#else
 | 
						|
#error "don't know how to do this"
 | 
						|
#endif
 | 
						|
 | 
						|
#endif
 |