/* * Copyright (C) 2013-2016 John Emmas * * 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. */ #ifdef COMPILER_MSVC #include extern "C" WINBASEAPI BOOL WINAPI CreateHardLinkA( LPCSTR lpFileName, LPCSTR lpExistingFileName, LPSECURITY_ATTRIBUTES lpSecurityAttributes ); // Needs kernel32.lib on anything higher than Win2K #include #include #include #include #include #include #include #include #include #include #include // Should ensure that we include the right // version - but we'll check anyway, later #include #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 struct timezone { int tz_minuteswest; /* minutes W of Greenwich */ int tz_dsttime; /* type of dst correction */ }; LIBPBD_API int PBD_APICALLTYPE 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 (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 LIBPBD_API bool operator> (const pthread_t& lhs, const pthread_t& rhs) { return (std::greater()(lhs.p, rhs.p)); } LIBPBD_API bool operator< (const pthread_t& lhs, const pthread_t& rhs) { return (std::less()(lhs.p, rhs.p)); } LIBPBD_API bool operator!= (const pthread_t& lhs, const pthread_t& rhs) { return (std::not_equal_to()(lhs.p, rhs.p)); } LIBPBD_API bool operator== (const pthread_t& lhs, const pthread_t& rhs) { 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 // LIBPBD_API ssize_t PBD_APICALLTYPE 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 // LIBPBD_API ssize_t PBD_APICALLTYPE 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); } #if defined(_MSC_VER) && (_MSC_VER < 1800) //*************************************************************** // // expm1() // // Emulates C99 expm1() using exp(). // // Returns: // // 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). // LIBPBD_API double PBD_APICALLTYPE expm1(double x) { return (exp(x) - (double)1.0); } //*************************************************************** // // log1p() // // Emulates C99 log1p() using log(). // // Returns: // // 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). // LIBPBD_API double PBD_APICALLTYPE log1p(double x) { return (log(x + (double)1.0)); } //*************************************************************** // // roundf() // // Emulates roundf() using floorf(). // // Returns: // // On Success: The largest integer that is less than or // equal to 'x'. // On Failure: None // LIBPBD_API float PBD_APICALLTYPE roundf(float x) { return (floorf(x)); } //*************************************************************** // // round() // // Emulates round() using floor(). // // Returns: // // On Success: The largest integer that is less than or // equal to 'x'. // On Failure: None // LIBPBD_API double PBD_APICALLTYPE round(double x) { return (floor(x)); } #endif #if defined(_MSC_VER) && (_MSC_VER < 1900) //*************************************************************** // // log2() // // Emulates C99 log2() using log(). // // Returns: // // On Success: The binary (base-2) logarithm of 'x' // (e.g. log2(1024) == 10). // On Failure: None, except that calling log(x) should generate // an appropriate error for us (such as ERANGE etc). // LIBPBD_API double PBD_APICALLTYPE log2(double x) { return (log(x) / log((double)2.0)); } //*************************************************************** // // trunc() // // Emulates trunc() using floor() and ceil(). // // Returns: // // On Success: The largest integer whose magnitude is less // than or equal to 'x' (regardless of sign). // On Failure: None // LIBPBD_API double PBD_APICALLTYPE trunc(double x) { if (x < 0) return (ceil(x)); return (floor(x)); } #endif 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 // LIBPBD_API bool PBD_APICALLTYPE 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 // LIBPBD_API char* PBD_APICALLTYPE 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 // LIBPBD_API DIR* PBD_APICALLTYPE 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 // LIBPBD_API struct dirent* PBD_APICALLTYPE 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 // LIBPBD_API int PBD_APICALLTYPE 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) // LIBPBD_API int PBD_APICALLTYPE 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) // LIBPBD_API int PBD_APICALLTYPE 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) // LIBPBD_API int PBD_APICALLTYPE 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 // LIBPBD_API void* PBD_APICALLTYPE 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 // LIBPBD_API int PBD_APICALLTYPE 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 // LIBPBD_API void* PBD_APICALLTYPE 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) // LIBPBD_API char* PBD_APICALLTYPE 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