2013-07-17 09:53:17 -04:00
|
|
|
/*
|
2019-08-02 23:10:55 -04:00
|
|
|
* Copyright (C) 2013-2016 John Emmas <john@creativepost.co.uk>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*/
|
2013-07-17 09:53:17 -04:00
|
|
|
|
|
|
|
#ifdef COMPILER_MSVC
|
|
|
|
|
|
|
|
#include <WTypes.h>
|
|
|
|
|
|
|
|
extern "C" WINBASEAPI BOOL WINAPI
|
|
|
|
CreateHardLinkA( LPCSTR lpFileName,
|
|
|
|
LPCSTR lpExistingFileName,
|
|
|
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes ); // Needs kernel32.lib on anything higher than Win2K
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <string>
|
|
|
|
#include <io.h>
|
2013-08-04 10:36:07 -04:00
|
|
|
#include <math.h>
|
2013-07-17 09:53:17 -04:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <pbd/error.h>
|
|
|
|
#include <ardourext/misc.h>
|
|
|
|
#include <ardourext/pthread.h> // Should ensure that we include the right
|
|
|
|
// version - but we'll check anyway, later
|
|
|
|
|
|
|
|
#include <glibmm.h>
|
|
|
|
|
|
|
|
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
|
|
|
|
|
|
|
|
struct timezone
|
|
|
|
{
|
|
|
|
int tz_minuteswest; /* minutes W of Greenwich */
|
|
|
|
int tz_dsttime; /* type of dst correction */
|
|
|
|
};
|
|
|
|
|
2013-12-01 09:26:08 -05:00
|
|
|
LIBPBD_API int PBD_APICALLTYPE
|
2013-07-17 09:53:17 -04:00
|
|
|
gettimeofday(struct timeval *__restrict tv, __timezone_ptr_t tz) // Does this need to be exported ?
|
|
|
|
{
|
|
|
|
FILETIME ft;
|
|
|
|
unsigned __int64 tmpres = 0;
|
|
|
|
static int tzflag = 0;
|
|
|
|
|
|
|
|
if (NULL != tv)
|
|
|
|
{
|
|
|
|
GetSystemTimeAsFileTime(&ft);
|
|
|
|
|
|
|
|
tmpres |= ft.dwHighDateTime;
|
|
|
|
tmpres <<= 32;
|
|
|
|
tmpres |= ft.dwLowDateTime;
|
|
|
|
|
|
|
|
/*converting file time to unix epoch*/
|
|
|
|
tmpres /= 10; /*convert into microseconds*/
|
|
|
|
tmpres -= DELTA_EPOCH_IN_MICROSECS;
|
|
|
|
tv->tv_sec = (long)(tmpres / 1000000UL);
|
|
|
|
tv->tv_usec = (long)(tmpres % 1000000UL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NULL != tz)
|
|
|
|
{
|
|
|
|
struct timezone *ptz = static_cast<struct timezone*> (tz);
|
|
|
|
if (!tzflag)
|
|
|
|
{
|
|
|
|
_tzset();
|
|
|
|
tzflag++;
|
|
|
|
}
|
|
|
|
if (ptz)
|
|
|
|
{
|
|
|
|
ptz->tz_minuteswest = _timezone / 60;
|
|
|
|
ptz->tz_dsttime = _daylight;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Define the default comparison operators for Windows (ptw32) 'pthread_t' (not used
|
|
|
|
// by Ardour AFAIK but would be needed if an array of 'pthread_t' had to be sorted).
|
|
|
|
#ifndef PTHREAD_H // Defined by PTW32 (Linux and other versions define _PTHREAD_H)
|
|
|
|
#error "An incompatible version of 'pthread.h' is #included. Use only the Windows (ptw32) version!"
|
|
|
|
#else
|
2013-12-01 09:26:08 -05:00
|
|
|
LIBPBD_API bool operator> (const pthread_t& lhs, const pthread_t& rhs)
|
2013-07-17 09:53:17 -04:00
|
|
|
{
|
|
|
|
return (std::greater<void*>()(lhs.p, rhs.p));
|
|
|
|
}
|
|
|
|
|
2013-12-01 09:26:08 -05:00
|
|
|
LIBPBD_API bool operator< (const pthread_t& lhs, const pthread_t& rhs)
|
2013-07-17 09:53:17 -04:00
|
|
|
{
|
|
|
|
return (std::less<void*>()(lhs.p, rhs.p));
|
|
|
|
}
|
|
|
|
|
2013-12-01 09:26:08 -05:00
|
|
|
LIBPBD_API bool operator!= (const pthread_t& lhs, const pthread_t& rhs)
|
2013-07-17 09:53:17 -04:00
|
|
|
{
|
|
|
|
return (std::not_equal_to<void*>()(lhs.p, rhs.p));
|
|
|
|
}
|
|
|
|
|
2013-12-01 09:26:08 -05:00
|
|
|
LIBPBD_API bool operator== (const pthread_t& lhs, const pthread_t& rhs)
|
2013-07-17 09:53:17 -04:00
|
|
|
{
|
|
|
|
return (!(lhs != rhs));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Functions supplied (later) to std::transform
|
|
|
|
//***************************************************************
|
|
|
|
//
|
|
|
|
// invert_backslash()
|
|
|
|
//
|
|
|
|
// Examines a supplied ASCII character and (if the character is
|
|
|
|
// a backslash) converts it to a forward slash,
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
|
|
|
// The supplied character (converted, if it was a backslash)
|
|
|
|
//
|
|
|
|
char invert_backslash(char character)
|
|
|
|
{
|
|
|
|
if ('\\' == character)
|
|
|
|
character = '/';
|
|
|
|
|
|
|
|
return (character);
|
|
|
|
}
|
|
|
|
|
|
|
|
//***************************************************************
|
|
|
|
//
|
|
|
|
// invert_forwardslash()
|
|
|
|
//
|
|
|
|
// Examines a supplied ASCII character and (if the character is
|
|
|
|
// a forward slash) converts it to a backslash,
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
|
|
|
// The supplied character (converted, if it was a fwd slash)
|
|
|
|
//
|
|
|
|
char invert_forwardslash(char character)
|
|
|
|
{
|
|
|
|
if ('/' == character)
|
|
|
|
character = '\\';
|
|
|
|
|
|
|
|
return (character);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//***************************************************************
|
|
|
|
//
|
|
|
|
// pread()
|
|
|
|
//
|
|
|
|
// Emulates pread() using _lseek()/_read()/_lseek().
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
|
|
|
// On Success: The number of bytes read from the file
|
|
|
|
// On Failure: -1
|
|
|
|
//
|
2013-12-01 09:26:08 -05:00
|
|
|
LIBPBD_API ssize_t PBD_APICALLTYPE
|
2013-07-17 09:53:17 -04:00
|
|
|
pread(int handle, void *buf, size_t nbytes, off_t offset)
|
|
|
|
{
|
|
|
|
int old_errno;
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
off_t old_offset = _tell(handle);
|
|
|
|
|
|
|
|
if (0 > old_offset)
|
|
|
|
ret = (-1);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_lseek(handle, offset, SEEK_SET);
|
|
|
|
ret = _read(handle, buf, nbytes);
|
|
|
|
|
|
|
|
old_errno = errno;
|
|
|
|
_lseek(handle, old_offset, SEEK_SET);
|
|
|
|
errno = old_errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//***************************************************************
|
|
|
|
//
|
|
|
|
// pwrite()
|
|
|
|
//
|
|
|
|
// Emulates pwrite() using _lseek()/_write()/_lseek().
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
|
|
|
// On Success: The number of bytes written to the file
|
|
|
|
// On Failure: -1
|
|
|
|
//
|
2013-12-01 09:26:08 -05:00
|
|
|
LIBPBD_API ssize_t PBD_APICALLTYPE
|
2013-07-17 09:53:17 -04:00
|
|
|
pwrite(int handle, const void *buf, size_t nbytes, off_t offset)
|
|
|
|
{
|
|
|
|
int old_errno;
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
off_t old_offset = _lseek(handle, offset, SEEK_SET);
|
|
|
|
|
|
|
|
if (0 > old_offset)
|
|
|
|
ret = (-1);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ret = _write(handle, buf, nbytes);
|
|
|
|
|
|
|
|
old_errno = errno;
|
|
|
|
_lseek(handle, old_offset, SEEK_SET);
|
|
|
|
errno = old_errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
2016-08-19 07:56:34 -04:00
|
|
|
#if defined(_MSC_VER) && (_MSC_VER < 1800)
|
2016-08-19 05:19:32 -04:00
|
|
|
//***************************************************************
|
|
|
|
//
|
2016-08-19 07:56:34 -04:00
|
|
|
// expm1()
|
2016-08-19 05:19:32 -04:00
|
|
|
//
|
2016-08-19 07:56:34 -04:00
|
|
|
// Emulates C99 expm1() using exp().
|
2016-08-19 05:19:32 -04:00
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
2016-08-19 07:56:34 -04:00
|
|
|
// On Success: (('e' raised to the power of 'x') - 1)
|
|
|
|
// (e.g. expm1(1) == 1.7182818).
|
|
|
|
// On Failure: None, except that calling exp(x) should generate
|
|
|
|
// an appropriate error for us (such as INF etc).
|
2016-08-19 05:19:32 -04:00
|
|
|
//
|
2016-08-19 07:56:34 -04:00
|
|
|
LIBPBD_API double PBD_APICALLTYPE
|
|
|
|
expm1(double x)
|
2016-08-19 05:19:32 -04:00
|
|
|
{
|
2016-08-19 07:56:34 -04:00
|
|
|
return (exp(x) - (double)1.0);
|
2016-08-19 05:19:32 -04:00
|
|
|
}
|
|
|
|
|
2013-08-04 10:36:07 -04:00
|
|
|
//***************************************************************
|
|
|
|
//
|
2016-08-19 07:56:34 -04:00
|
|
|
// log1p()
|
2013-08-04 10:36:07 -04:00
|
|
|
//
|
2016-08-19 07:56:34 -04:00
|
|
|
// Emulates C99 log1p() using log().
|
2013-08-04 10:36:07 -04:00
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
2016-08-19 07:56:34 -04:00
|
|
|
// On Success: The natural logarithm of (1 + x)
|
|
|
|
// (e.g. log1p(1) == 0.69314718).
|
|
|
|
// On Failure: None, except that calling log(x) should generate
|
|
|
|
// an appropriate error for us (such as ERANGE etc).
|
2013-08-04 10:36:07 -04:00
|
|
|
//
|
2013-12-01 09:26:08 -05:00
|
|
|
LIBPBD_API double PBD_APICALLTYPE
|
2016-08-19 07:56:34 -04:00
|
|
|
log1p(double x)
|
2013-08-04 10:36:07 -04:00
|
|
|
{
|
2016-08-19 07:56:34 -04:00
|
|
|
return (log(x + (double)1.0));
|
2013-08-04 10:36:07 -04:00
|
|
|
}
|
|
|
|
|
2014-11-24 07:56:07 -05:00
|
|
|
//***************************************************************
|
|
|
|
//
|
2016-08-19 07:56:34 -04:00
|
|
|
// roundf()
|
2014-11-24 07:56:07 -05:00
|
|
|
//
|
2016-08-19 07:56:34 -04:00
|
|
|
// Emulates roundf() using floorf().
|
2014-11-24 07:56:07 -05:00
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
2016-08-19 07:56:34 -04:00
|
|
|
// On Success: The largest integer that is less than or
|
|
|
|
// equal to 'x'.
|
2014-11-24 07:56:07 -05:00
|
|
|
// On Failure: None
|
|
|
|
//
|
2016-08-19 07:56:34 -04:00
|
|
|
LIBPBD_API float PBD_APICALLTYPE
|
|
|
|
roundf(float x)
|
2014-11-24 07:56:07 -05:00
|
|
|
{
|
2016-08-19 07:56:34 -04:00
|
|
|
return (floorf(x));
|
2014-11-24 07:56:07 -05:00
|
|
|
}
|
|
|
|
|
2016-05-28 14:07:22 -04:00
|
|
|
//***************************************************************
|
|
|
|
//
|
2016-08-19 07:56:34 -04:00
|
|
|
// round()
|
2016-05-28 14:07:22 -04:00
|
|
|
//
|
2016-08-19 07:56:34 -04:00
|
|
|
// Emulates round() using floor().
|
2016-05-28 14:07:22 -04:00
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
2016-08-19 07:56:34 -04:00
|
|
|
// On Success: The largest integer that is less than or
|
|
|
|
// equal to 'x'.
|
|
|
|
// On Failure: None
|
2016-05-28 14:07:22 -04:00
|
|
|
//
|
|
|
|
LIBPBD_API double PBD_APICALLTYPE
|
2016-08-19 07:56:34 -04:00
|
|
|
round(double x)
|
2016-05-28 14:07:22 -04:00
|
|
|
{
|
2016-08-19 07:56:34 -04:00
|
|
|
return (floor(x));
|
2016-05-28 14:07:22 -04:00
|
|
|
}
|
2016-08-19 07:56:34 -04:00
|
|
|
#endif
|
2016-05-28 14:07:22 -04:00
|
|
|
|
2016-08-19 07:56:34 -04:00
|
|
|
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
2016-05-28 14:07:22 -04:00
|
|
|
//***************************************************************
|
|
|
|
//
|
2016-08-19 07:56:34 -04:00
|
|
|
// log2()
|
2016-05-28 14:07:22 -04:00
|
|
|
//
|
2016-08-19 07:56:34 -04:00
|
|
|
// Emulates C99 log2() using log().
|
2016-05-28 14:07:22 -04:00
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
2016-08-19 07:56:34 -04:00
|
|
|
// On Success: The binary (base-2) logarithm of 'x'
|
|
|
|
// (e.g. log2(1024) == 10).
|
2016-05-28 14:07:22 -04:00
|
|
|
// On Failure: None, except that calling log(x) should generate
|
|
|
|
// an appropriate error for us (such as ERANGE etc).
|
|
|
|
//
|
|
|
|
LIBPBD_API double PBD_APICALLTYPE
|
2016-08-19 07:56:34 -04:00
|
|
|
log2(double x)
|
2016-05-28 14:07:22 -04:00
|
|
|
{
|
2016-08-19 07:56:34 -04:00
|
|
|
return (log(x) / log((double)2.0));
|
2016-05-28 14:07:22 -04:00
|
|
|
}
|
|
|
|
|
2015-01-10 06:56:24 -05:00
|
|
|
//***************************************************************
|
|
|
|
//
|
2016-08-19 07:56:34 -04:00
|
|
|
// trunc()
|
2015-01-10 06:56:24 -05:00
|
|
|
//
|
2016-08-19 07:56:34 -04:00
|
|
|
// Emulates trunc() using floor() and ceil().
|
2015-01-10 06:56:24 -05:00
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
2016-08-19 07:56:34 -04:00
|
|
|
// On Success: The largest integer whose magnitude is less
|
|
|
|
// than or equal to 'x' (regardless of sign).
|
|
|
|
// On Failure: None
|
2015-01-10 06:56:24 -05:00
|
|
|
//
|
|
|
|
LIBPBD_API double PBD_APICALLTYPE
|
2016-08-19 07:56:34 -04:00
|
|
|
trunc(double x)
|
2015-01-10 06:56:24 -05:00
|
|
|
{
|
2016-08-19 07:56:34 -04:00
|
|
|
if (x < 0)
|
|
|
|
return (ceil(x));
|
|
|
|
|
|
|
|
return (floor(x));
|
2015-01-10 06:56:24 -05:00
|
|
|
}
|
2016-05-28 14:07:22 -04:00
|
|
|
#endif
|
2015-01-10 06:56:24 -05:00
|
|
|
|
2013-07-17 09:53:17 -04:00
|
|
|
namespace PBD {
|
|
|
|
|
|
|
|
//***************************************************************
|
|
|
|
//
|
|
|
|
// TestForMinimumSpecOS()
|
|
|
|
//
|
|
|
|
// Tests the user's OS to see if it is Win2K or later (could be
|
|
|
|
// expanded quite easily to accommodate other OS's)
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
|
|
|
// On Success: TRUE (if the user's OS matches the minimum spec)
|
|
|
|
// On Failure: FALSE otherwise
|
|
|
|
//
|
2013-12-01 09:26:08 -05:00
|
|
|
LIBPBD_API bool PBD_APICALLTYPE
|
2013-07-17 09:53:17 -04:00
|
|
|
TestForMinimumSpecOS(char *revision /* currently ignored */)
|
|
|
|
{
|
|
|
|
bool bRet = true;
|
|
|
|
#ifdef PLATFORM_WINDOWS
|
|
|
|
bRet = false;
|
|
|
|
HINSTANCE hKernelDll = (HINSTANCE)dlopen("kernel32.dll", RTLD_NOW);
|
|
|
|
|
|
|
|
if (hKernelDll)
|
|
|
|
{
|
|
|
|
// 'CreateHardLink()' is only available from Win2K onwards.
|
|
|
|
if (NULL != dlsym(hKernelDll, "CreateHardLinkA"))
|
|
|
|
bRet = true;
|
|
|
|
|
|
|
|
dlclose(hKernelDll);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
// Other OS's could be accommodated here
|
|
|
|
|
|
|
|
return (bRet);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//***************************************************************
|
|
|
|
//
|
|
|
|
// realpath()
|
|
|
|
//
|
|
|
|
// Emulates POSIX realpath() using Win32 _fullpath().
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
|
|
|
// On Success: A pointer to the resolved (absolute) path
|
|
|
|
// On Failure: NULL
|
|
|
|
//
|
2013-12-01 09:26:08 -05:00
|
|
|
LIBPBD_API char* PBD_APICALLTYPE
|
2013-07-17 09:53:17 -04:00
|
|
|
realpath (const char *original_path, char resolved_path[_MAX_PATH+1])
|
|
|
|
{
|
|
|
|
char *pRet = NULL;
|
|
|
|
bool bIsSymLink = 0; // We'll probably need to test the incoming path
|
|
|
|
// to find out if it points to a Windows shortcut
|
|
|
|
// (or a hard link) and set this appropriately.
|
|
|
|
if (bIsSymLink)
|
|
|
|
{
|
|
|
|
// At the moment I'm not sure if Windows '_fullpath()' is directly
|
|
|
|
// equivalent to POSIX 'realpath()' - in as much as the latter will
|
|
|
|
// resolve the supplied path if it happens to point to a symbolic
|
|
|
|
// link ('_fullpath()' probably DOESN'T do this but I'm not really
|
|
|
|
// sure if Ardour needs such functionality anyway). Therefore we'll
|
|
|
|
// possibly need to add that functionality here at a later date.
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char temp[(MAX_PATH+1)*6]; // Allow for maximum length of a path in UTF8 characters
|
|
|
|
|
|
|
|
// POSIX 'realpath()' requires that the buffer size is at
|
|
|
|
// least PATH_MAX+1, so assume that the user knew this !!
|
|
|
|
pRet = _fullpath(temp, Glib::locale_from_utf8(original_path).c_str(), _MAX_PATH);
|
|
|
|
if (NULL != pRet)
|
|
|
|
strcpy(resolved_path, Glib::locale_to_utf8(temp).c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
return (pRet);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//***************************************************************
|
|
|
|
//
|
|
|
|
// opendir()
|
|
|
|
//
|
|
|
|
// Creates a pointer to a DIR structure, appropriately filled in
|
|
|
|
// and ready to begin a directory search iteration.
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
|
|
|
// On Success: Pointer to a (heap based) DIR structure
|
|
|
|
// On Failure: NULL
|
|
|
|
//
|
2013-12-01 09:26:08 -05:00
|
|
|
LIBPBD_API DIR* PBD_APICALLTYPE
|
2013-07-17 09:53:17 -04:00
|
|
|
opendir (const char *szPath)
|
|
|
|
{
|
|
|
|
wchar_t wpath[PATH_MAX+1];
|
|
|
|
unsigned int rc;
|
|
|
|
DIR *pDir = 0;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
|
|
|
if (!szPath)
|
|
|
|
errno = EFAULT;
|
|
|
|
|
|
|
|
if ((!errno) && ('\0' == szPath[0]))
|
|
|
|
errno = ENOTDIR;
|
|
|
|
|
|
|
|
// Determine if the given path really is a directory
|
|
|
|
|
|
|
|
if (!errno)
|
|
|
|
if (0 == MultiByteToWideChar (CP_UTF8, 0, (LPCSTR)szPath, -1, (LPWSTR)wpath, sizeof(wpath)))
|
|
|
|
errno = EFAULT;
|
|
|
|
|
|
|
|
if ((!errno) && ((rc = GetFileAttributesW(wpath)) == -1))
|
|
|
|
errno = ENOENT;
|
|
|
|
|
|
|
|
if ((!errno) && (!(rc & FILE_ATTRIBUTE_DIRECTORY)))
|
|
|
|
// Error. Entry exists but not a directory. */
|
|
|
|
errno = ENOTDIR;
|
|
|
|
|
|
|
|
if (!errno)
|
|
|
|
{
|
|
|
|
// Allocate enough memory to store DIR structure, plus
|
|
|
|
// the complete directory path originally supplied.
|
|
|
|
pDir = (DIR *)malloc(sizeof(DIR) + strlen(szPath) + strlen("\\") + strlen ("*"));
|
|
|
|
|
|
|
|
if (!pDir)
|
|
|
|
{
|
|
|
|
// Error - out of memory
|
|
|
|
errno = ENOMEM;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!errno)
|
|
|
|
{
|
|
|
|
// Create the search expression
|
|
|
|
strcpy(pDir->dd_name, szPath);
|
|
|
|
|
|
|
|
// Add a backslash if the path doesn't already end with one
|
|
|
|
if (pDir->dd_name[0] != '\0' &&
|
|
|
|
pDir->dd_name[strlen(pDir->dd_name) - 1] != '/' &&
|
|
|
|
pDir->dd_name[strlen(pDir->dd_name) - 1] != '\\')
|
|
|
|
{
|
|
|
|
strcat (pDir->dd_name, "\\");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the search pattern
|
|
|
|
strcat(pDir->dd_name, "*");
|
|
|
|
|
|
|
|
// Initialize handle to -1 so that a premature closedir()
|
|
|
|
// doesn't try to call _findclose() on it.
|
|
|
|
pDir->dd_handle = (-1);
|
|
|
|
|
|
|
|
// Initialize the status
|
|
|
|
pDir->dd_stat = 0;
|
|
|
|
|
|
|
|
// Initialize the dirent structure. 'ino' and 'reclen' are invalid under Win32
|
|
|
|
// and 'name' simply points at the appropriate part of the findfirst_t struct.
|
|
|
|
pDir->dd_dir.d_ino = 0;
|
|
|
|
pDir->dd_dir.d_reclen = 0;
|
|
|
|
pDir->dd_dir.d_namlen = 0;
|
|
|
|
strcpy(pDir->dd_dir.d_name, pDir->dd_dta.name);
|
|
|
|
|
|
|
|
return (pDir); // Succeeded
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pDir)
|
|
|
|
free (pDir);
|
|
|
|
return (DIR *) 0; // Failed
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//***************************************************************
|
|
|
|
//
|
|
|
|
// readdir()
|
|
|
|
//
|
|
|
|
// Return a pointer to a dirent struct, filled with information
|
|
|
|
// about the next entry in the directory.
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
|
|
|
// On Success: A pointer to the supplied DIR's 'dirent' struct
|
|
|
|
// On Failure: NULL
|
|
|
|
//
|
2013-12-01 09:26:08 -05:00
|
|
|
LIBPBD_API struct dirent* PBD_APICALLTYPE
|
2013-07-17 09:53:17 -04:00
|
|
|
readdir (DIR* pDir)
|
|
|
|
{
|
|
|
|
int old_errno = 0;
|
|
|
|
errno = 0;
|
|
|
|
|
|
|
|
// Check for valid DIR struct
|
|
|
|
if (!pDir)
|
|
|
|
errno = EFAULT;
|
|
|
|
|
|
|
|
if ((strcmp(pDir->dd_dir.d_name, pDir->dd_dta.name)) && (!errno))
|
|
|
|
// The structure does not seem to be set up correctly
|
|
|
|
errno = EINVAL;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (pDir->dd_stat < 0)
|
|
|
|
{
|
|
|
|
// We have already returned all files in this directory
|
|
|
|
// (or the structure has an invalid dd_stat).
|
|
|
|
return (struct dirent *)0;
|
|
|
|
}
|
|
|
|
else if (pDir->dd_stat == 0)
|
|
|
|
{
|
|
|
|
// We haven't started the search yet.
|
|
|
|
// Start the search
|
|
|
|
pDir->dd_handle = _findfirst (Glib::locale_from_utf8(pDir->dd_name).c_str(), &(pDir->dd_dta));
|
|
|
|
|
|
|
|
if (pDir->dd_handle == -1)
|
|
|
|
// The directory is empty
|
|
|
|
pDir->dd_stat = -1;
|
|
|
|
else
|
|
|
|
pDir->dd_stat = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Do not return ENOENT on last file in directory
|
|
|
|
old_errno = errno;
|
|
|
|
|
|
|
|
// Get the next search entry
|
|
|
|
if (_findnext (pDir->dd_handle, &(pDir->dd_dta)))
|
|
|
|
{
|
|
|
|
// We are off the end or otherwise error
|
|
|
|
errno = old_errno;
|
|
|
|
_findclose (pDir->dd_handle);
|
|
|
|
pDir->dd_handle = -1;
|
|
|
|
pDir->dd_stat = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
// Update to indicate the correct status number
|
|
|
|
pDir->dd_stat++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pDir->dd_stat > 0)
|
|
|
|
{
|
|
|
|
// We successfully got an entry. Details about the file are
|
|
|
|
// already appropriately filled in except for the length of
|
|
|
|
// file name.
|
|
|
|
strcpy(pDir->dd_dir.d_name, pDir->dd_dta.name);
|
|
|
|
pDir->dd_dir.d_namlen = strlen (pDir->dd_dir.d_name);
|
|
|
|
return (&pDir->dd_dir); // Succeeded
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (struct dirent *) 0; // Failed
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//***************************************************************
|
|
|
|
//
|
|
|
|
// closedir()
|
|
|
|
//
|
|
|
|
// Frees the resources allocated by opendir().
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
|
|
|
// On Success: 0
|
|
|
|
// On Failure: -1
|
|
|
|
//
|
2013-12-01 09:26:08 -05:00
|
|
|
LIBPBD_API int PBD_APICALLTYPE
|
2013-07-17 09:53:17 -04:00
|
|
|
closedir (DIR *pDir)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
|
|
|
if (!pDir)
|
|
|
|
errno = EFAULT;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ((-1) != pDir->dd_handle)
|
|
|
|
rc = _findclose (pDir->dd_handle);
|
|
|
|
|
|
|
|
// Free the DIR structure
|
|
|
|
free (pDir);
|
|
|
|
|
|
|
|
return rc; // Succeeded
|
|
|
|
}
|
|
|
|
|
|
|
|
return (-1); // Failed
|
|
|
|
}
|
|
|
|
|
|
|
|
//***************************************************************
|
|
|
|
//
|
|
|
|
// mkstemp()
|
|
|
|
//
|
|
|
|
// Emulates Linux mkstemp() using Win32 _mktemp() and _open() etc.
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
|
|
|
// On Success: A file descriptor for the opened file.
|
|
|
|
// On Failure: (-1)
|
|
|
|
//
|
2013-12-01 09:26:08 -05:00
|
|
|
LIBPBD_API int PBD_APICALLTYPE
|
2013-07-17 09:53:17 -04:00
|
|
|
mkstemp (char *template_name)
|
|
|
|
{
|
|
|
|
int ret = (-1);
|
|
|
|
char *szFileName;
|
|
|
|
char szTempPath[PATH_MAX+100]; // Just ensure we have plenty of buffer space
|
|
|
|
|
|
|
|
if (NULL != (szFileName = _mktemp(template_name)))
|
|
|
|
{
|
|
|
|
if (0 != ::GetTempPathA(sizeof(szTempPath), szTempPath))
|
|
|
|
{
|
|
|
|
strcat(szTempPath, szFileName);
|
|
|
|
ret = _open(szTempPath, (_O_CREAT|_O_BINARY|_O_TEMPORARY|_O_RDWR|_O_TRUNC), _S_IWRITE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//***************************************************************
|
|
|
|
//
|
|
|
|
// ntfs_link()
|
|
|
|
//
|
|
|
|
// Emulates Linux link() using Win32 CreateHardLink()(NTFS only).
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
|
|
|
// On Success: Non-zero.
|
|
|
|
// On Failure: Zero (call 'GetLastError()' to retrieve info)
|
|
|
|
//
|
2013-12-01 09:26:08 -05:00
|
|
|
LIBPBD_API int PBD_APICALLTYPE
|
2013-07-17 09:53:17 -04:00
|
|
|
ntfs_link (const char *existing_filepath, const char *link_filepath)
|
|
|
|
{
|
|
|
|
int ret = 1; // 'ERROR_INVALID_FUNCTION'
|
|
|
|
bool bValidPath = false;
|
|
|
|
|
|
|
|
// Make sure we've been sent a valid input string
|
|
|
|
if (existing_filepath && link_filepath)
|
|
|
|
{
|
|
|
|
std::string strRoot = existing_filepath;
|
|
|
|
|
|
|
|
if ((1 < strRoot.length()) && ('\\' == existing_filepath[0]) && ('\\' == existing_filepath[1]))
|
|
|
|
{
|
|
|
|
int slashcnt = 0;
|
|
|
|
|
|
|
|
// We've been sent a network path. Convert backslashes to forward slashes temporarily.
|
|
|
|
std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_backslash);
|
|
|
|
|
|
|
|
// Now, if there are less than four slashes, add a fourth one or abort
|
|
|
|
std::string::iterator iter = strRoot.begin();
|
|
|
|
while ((slashcnt < 4) && (iter != strRoot.end()))
|
|
|
|
{
|
|
|
|
if ('/' == (*iter))
|
|
|
|
slashcnt++;
|
|
|
|
|
|
|
|
++iter;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (slashcnt > 2)
|
|
|
|
{
|
|
|
|
// If only 3 slashes were counted, add a trailing slash
|
|
|
|
if (slashcnt == 3)
|
|
|
|
strRoot += '/';
|
|
|
|
|
|
|
|
// Now find the position of the fourth slash
|
|
|
|
iter = strRoot.begin();
|
|
|
|
int charcnt = 0;
|
|
|
|
for (slashcnt=0; slashcnt<4;)
|
|
|
|
{
|
|
|
|
charcnt++;
|
|
|
|
|
|
|
|
if ('/' == (*iter))
|
|
|
|
slashcnt++;
|
|
|
|
|
|
|
|
if (++iter == strRoot.end())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
strRoot.resize(charcnt);
|
|
|
|
bValidPath = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Assume a standard Windows style path
|
|
|
|
if (1 < strRoot.length() && (':' == existing_filepath[1]))
|
|
|
|
{
|
|
|
|
// Convert backslashes to forward slashes temporarily.
|
|
|
|
std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_backslash);
|
|
|
|
|
|
|
|
if (2 == strRoot.length())
|
|
|
|
strRoot += '/';
|
|
|
|
|
|
|
|
if ('/' == strRoot[2])
|
|
|
|
{
|
|
|
|
strRoot.resize(3);
|
|
|
|
bValidPath = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bValidPath)
|
|
|
|
{
|
|
|
|
char szFileSystemType[_MAX_PATH+1];
|
|
|
|
|
|
|
|
// Restore the original backslashes
|
|
|
|
std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_forwardslash);
|
|
|
|
|
|
|
|
// Windows only supports hard links for the NTFS filing
|
|
|
|
// system, so let's make sure that's what we're using!!
|
|
|
|
if (::GetVolumeInformationA(strRoot.c_str(), NULL, 0, NULL, NULL, NULL, szFileSystemType, _MAX_PATH+1))
|
|
|
|
{
|
|
|
|
std::string strRootFileSystemType = szFileSystemType;
|
|
|
|
std::transform(strRootFileSystemType.begin(), strRootFileSystemType.end(), strRootFileSystemType.begin(), ::toupper);
|
|
|
|
#if (_WIN32_WINNT >= 0x0500)
|
|
|
|
if (0 == strRootFileSystemType.compare("NTFS"))
|
|
|
|
{
|
|
|
|
if (TestForMinimumSpecOS()) // Hard links were only available from Win2K onwards
|
|
|
|
if (0 == CreateHardLinkA(link_filepath, existing_filepath, NULL))
|
|
|
|
{ // Note that the above API call cannot create a link to a directory, so
|
|
|
|
// should we also be checking that the supplied path was actually a file?
|
|
|
|
ret = GetLastError();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
SetLastError(ret = 0); // 'NO_ERROR'
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ret = 4300; // 'ERROR_INVALID_MEDIA'
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ret = 123; // 'ERROR_INVALID_NAME'
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ret = 161; // 'ERROR_BAD_PATHNAME'
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
{
|
|
|
|
SetLastError(ret);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//***************************************************************
|
|
|
|
//
|
|
|
|
// ntfs_unlink()
|
|
|
|
//
|
|
|
|
// Emulates Linux unlink() using Win32 DeleteFile()(NTFS only).
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
|
|
|
// On Success: Non-zero.
|
|
|
|
// On Failure: Zero (call 'GetLastError()' to retrieve info)
|
|
|
|
//
|
2013-12-01 09:26:08 -05:00
|
|
|
LIBPBD_API int PBD_APICALLTYPE
|
2013-07-17 09:53:17 -04:00
|
|
|
ntfs_unlink (const char *link_filepath)
|
|
|
|
{
|
|
|
|
int ret = 1; // 'ERROR_INVALID_FUNCTION'
|
|
|
|
bool bValidPath = false;
|
|
|
|
|
|
|
|
// Make sure we've been sent a valid input string
|
|
|
|
if (link_filepath)
|
|
|
|
{
|
|
|
|
std::string strRoot = link_filepath;
|
|
|
|
|
|
|
|
if ((1 < strRoot.length()) && ('\\' == link_filepath[0]) && ('\\' == link_filepath[1]))
|
|
|
|
{
|
|
|
|
int slashcnt = 0;
|
|
|
|
|
|
|
|
// We've been sent a network path. Convert backslashes to forward slashes temporarily.
|
|
|
|
std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_backslash);
|
|
|
|
|
|
|
|
// Now, if there are less than four slashes, add a fourth one or abort
|
|
|
|
std::string::iterator iter = strRoot.begin();
|
|
|
|
while ((slashcnt < 4) && (iter != strRoot.end()))
|
|
|
|
{
|
|
|
|
if ('/' == (*iter))
|
|
|
|
slashcnt++;
|
|
|
|
|
|
|
|
++iter;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (slashcnt > 2)
|
|
|
|
{
|
|
|
|
// If only 3 slashes were counted, add a trailing slash
|
|
|
|
if (slashcnt == 3)
|
|
|
|
strRoot += '/';
|
|
|
|
|
|
|
|
// Now find the position of the fourth slash
|
|
|
|
iter = strRoot.begin();
|
|
|
|
int charcnt = 0;
|
|
|
|
for (slashcnt=0; slashcnt<4;)
|
|
|
|
{
|
|
|
|
charcnt++;
|
|
|
|
|
|
|
|
if ('/' == (*iter))
|
|
|
|
slashcnt++;
|
|
|
|
|
|
|
|
if (++iter == strRoot.end())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
strRoot.resize(charcnt);
|
|
|
|
bValidPath = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Assume a standard Windows style path
|
|
|
|
if (1 < strRoot.length() && (':' == link_filepath[1]))
|
|
|
|
{
|
|
|
|
// Convert backslashes to forward slashes temporarily.
|
|
|
|
std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_backslash);
|
|
|
|
|
|
|
|
if (2 == strRoot.length())
|
|
|
|
strRoot += '/';
|
|
|
|
|
|
|
|
if ('/' == strRoot[2])
|
|
|
|
{
|
|
|
|
strRoot.resize(3);
|
|
|
|
bValidPath = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bValidPath)
|
|
|
|
{
|
|
|
|
char szFileSystemType[_MAX_PATH+1];
|
|
|
|
|
|
|
|
// Restore the original backslashes
|
|
|
|
std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_forwardslash);
|
|
|
|
|
|
|
|
// Windows only supports hard links for the NTFS filing
|
|
|
|
// system, so let's make sure that's what we're using!!
|
|
|
|
if (::GetVolumeInformationA(strRoot.c_str(), NULL, 0, NULL, NULL, NULL, szFileSystemType, _MAX_PATH+1))
|
|
|
|
{
|
|
|
|
std::string strRootFileSystemType = szFileSystemType;
|
|
|
|
std::transform(strRootFileSystemType.begin(), strRootFileSystemType.end(), strRootFileSystemType.begin(), ::toupper);
|
|
|
|
#if (_WIN32_WINNT >= 0x0500)
|
|
|
|
if (0 == strRootFileSystemType.compare("NTFS"))
|
|
|
|
if (TestForMinimumSpecOS()) // Hard links were only available from Win2K onwards
|
|
|
|
if (0 == DeleteFileA(link_filepath))
|
|
|
|
ret = GetLastError();
|
|
|
|
else
|
|
|
|
ret = 0; // 'NO_ERROR'
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ret = 123; // 'ERROR_INVALID_NAME'
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ret = 161; // 'ERROR_BAD_PATHNAME'
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
{
|
|
|
|
SetLastError(ret);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace PBD
|
|
|
|
|
|
|
|
|
|
|
|
//***************************************************************
|
|
|
|
//
|
|
|
|
// dlopen()
|
|
|
|
//
|
|
|
|
// Emulates POSIX dlopen() using Win32 LoadLibrary().
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
|
|
|
// On Success: A handle to the opened DLL
|
|
|
|
// On Failure: NULL
|
|
|
|
//
|
2013-12-01 09:26:08 -05:00
|
|
|
LIBPBD_API void* PBD_APICALLTYPE
|
2013-07-17 09:53:17 -04:00
|
|
|
dlopen (const char *file_name, int mode)
|
|
|
|
{
|
|
|
|
// Note that 'mode' is ignored in Win32
|
|
|
|
return(::LoadLibraryA(Glib::locale_from_utf8(file_name).c_str()));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//***************************************************************
|
|
|
|
//
|
|
|
|
// dlclose()
|
|
|
|
//
|
|
|
|
// Emulates POSIX dlclose() using Win32 FreeLibrary().
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
|
|
|
// On Success: A non-zero number
|
|
|
|
// On Failure: 0
|
|
|
|
//
|
2013-12-01 09:26:08 -05:00
|
|
|
LIBPBD_API int PBD_APICALLTYPE
|
2013-07-17 09:53:17 -04:00
|
|
|
dlclose (void *handle)
|
|
|
|
{
|
|
|
|
return (::FreeLibrary((HMODULE)handle));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//***************************************************************
|
|
|
|
//
|
|
|
|
// dlsym()
|
|
|
|
//
|
|
|
|
// Emulates POSIX dlsym() using Win32 GetProcAddress().
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
|
|
|
// On Success: A pointer to the found function or symbol
|
|
|
|
// On Failure: NULL
|
|
|
|
//
|
2013-12-01 09:26:08 -05:00
|
|
|
LIBPBD_API void* PBD_APICALLTYPE
|
2013-07-17 09:53:17 -04:00
|
|
|
dlsym (void *handle, const char *symbol_name)
|
|
|
|
{
|
|
|
|
// First test for RTLD_DEFAULT and RTLD_NEXT
|
|
|
|
if ((handle == 0/*RTLD_DEFAULT*/) || (handle == ((void *) -1L)/*RTLD_NEXT*/))
|
|
|
|
{
|
|
|
|
return 0; // Not yet supported for Win32
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return (::GetProcAddress((HMODULE)handle, symbol_name));
|
|
|
|
}
|
|
|
|
|
|
|
|
#define LOCAL_ERROR_BUF_SIZE 1024
|
|
|
|
static char szLastWinError[LOCAL_ERROR_BUF_SIZE];
|
|
|
|
//***************************************************************
|
|
|
|
//
|
|
|
|
// dlerror()
|
|
|
|
//
|
|
|
|
// Emulates POSIX dlerror() using Win32 GetLastError().
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
//
|
|
|
|
// On Success: The translated message corresponding to the
|
|
|
|
// last error
|
|
|
|
// On Failure: NULL (if the last error was ERROR_SUCCESS)
|
|
|
|
//
|
2013-12-01 09:26:08 -05:00
|
|
|
LIBPBD_API char* PBD_APICALLTYPE
|
2013-07-17 09:53:17 -04:00
|
|
|
dlerror ()
|
|
|
|
{
|
|
|
|
DWORD dwLastErrorId = GetLastError();
|
|
|
|
if (ERROR_SUCCESS == dwLastErrorId)
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (0 == FormatMessage(
|
|
|
|
FORMAT_MESSAGE_FROM_SYSTEM,
|
|
|
|
NULL,
|
|
|
|
dwLastErrorId,
|
|
|
|
0,
|
|
|
|
szLastWinError,
|
|
|
|
LOCAL_ERROR_BUF_SIZE,
|
|
|
|
0))
|
|
|
|
{
|
|
|
|
sprintf(szLastWinError, "Could not decipher the previous error message");
|
|
|
|
}
|
|
|
|
|
|
|
|
// POSIX dlerror() seems to reset the
|
|
|
|
// error system, so emulate that here
|
|
|
|
SetLastError(ERROR_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(szLastWinError);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // COMPILER_MSVC
|