2 Copyright (C) 2009 John Emmas
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 extern "C" WINBASEAPI BOOL WINAPI
25 CreateHardLinkA( LPCSTR lpFileName,
26 LPCSTR lpExistingFileName,
27 LPSECURITY_ATTRIBUTES lpSecurityAttributes ); // Needs kernel32.lib on anything higher than Win2K
37 #include <pbd/error.h>
38 #include <ardourext/misc.h>
39 #include <ardourext/pthread.h> // Should ensure that we include the right
40 // version - but we'll check anyway, later
44 #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
48 int tz_minuteswest; /* minutes W of Greenwich */
49 int tz_dsttime; /* type of dst correction */
52 LIBPBD_API int PBD_APICALLTYPE
53 gettimeofday(struct timeval *__restrict tv, __timezone_ptr_t tz) // Does this need to be exported ?
56 unsigned __int64 tmpres = 0;
57 static int tzflag = 0;
61 GetSystemTimeAsFileTime(&ft);
63 tmpres |= ft.dwHighDateTime;
65 tmpres |= ft.dwLowDateTime;
67 /*converting file time to unix epoch*/
68 tmpres /= 10; /*convert into microseconds*/
69 tmpres -= DELTA_EPOCH_IN_MICROSECS;
70 tv->tv_sec = (long)(tmpres / 1000000UL);
71 tv->tv_usec = (long)(tmpres % 1000000UL);
76 struct timezone *ptz = static_cast<struct timezone*> (tz);
84 ptz->tz_minuteswest = _timezone / 60;
85 ptz->tz_dsttime = _daylight;
92 // Define the default comparison operators for Windows (ptw32) 'pthread_t' (not used
93 // by Ardour AFAIK but would be needed if an array of 'pthread_t' had to be sorted).
94 #ifndef PTHREAD_H // Defined by PTW32 (Linux and other versions define _PTHREAD_H)
95 #error "An incompatible version of 'pthread.h' is #included. Use only the Windows (ptw32) version!"
97 LIBPBD_API bool operator> (const pthread_t& lhs, const pthread_t& rhs)
99 return (std::greater<void*>()(lhs.p, rhs.p));
102 LIBPBD_API bool operator< (const pthread_t& lhs, const pthread_t& rhs)
104 return (std::less<void*>()(lhs.p, rhs.p));
107 LIBPBD_API bool operator!= (const pthread_t& lhs, const pthread_t& rhs)
109 return (std::not_equal_to<void*>()(lhs.p, rhs.p));
112 LIBPBD_API bool operator== (const pthread_t& lhs, const pthread_t& rhs)
114 return (!(lhs != rhs));
118 // Functions supplied (later) to std::transform
119 //***************************************************************
121 // invert_backslash()
123 // Examines a supplied ASCII character and (if the character is
124 // a backslash) converts it to a forward slash,
128 // The supplied character (converted, if it was a backslash)
130 char invert_backslash(char character)
132 if ('\\' == character)
138 //***************************************************************
140 // invert_forwardslash()
142 // Examines a supplied ASCII character and (if the character is
143 // a forward slash) converts it to a backslash,
147 // The supplied character (converted, if it was a fwd slash)
149 char invert_forwardslash(char character)
151 if ('/' == character)
158 //***************************************************************
162 // Emulates pread() using _lseek()/_read()/_lseek().
166 // On Success: The number of bytes read from the file
169 LIBPBD_API ssize_t PBD_APICALLTYPE
170 pread(int handle, void *buf, size_t nbytes, off_t offset)
175 off_t old_offset = _tell(handle);
181 _lseek(handle, offset, SEEK_SET);
182 ret = _read(handle, buf, nbytes);
185 _lseek(handle, old_offset, SEEK_SET);
193 //***************************************************************
197 // Emulates pwrite() using _lseek()/_write()/_lseek().
201 // On Success: The number of bytes written to the file
204 LIBPBD_API ssize_t PBD_APICALLTYPE
205 pwrite(int handle, const void *buf, size_t nbytes, off_t offset)
210 off_t old_offset = _lseek(handle, offset, SEEK_SET);
216 ret = _write(handle, buf, nbytes);
219 _lseek(handle, old_offset, SEEK_SET);
226 //***************************************************************
230 // Emulates roundf() using floorf().
234 // On Success: The largest integer that is less than or
238 LIBPBD_API float PBD_APICALLTYPE
244 //***************************************************************
248 // Emulates round() using floor().
252 // On Success: The largest integer that is less than or
256 LIBPBD_API double PBD_APICALLTYPE
262 //***************************************************************
266 // Emulates trunc() using floor() and ceil().
270 // On Success: The largest integer whose magnitude is less
271 // than or equal to 'x' (regardless of sign).
274 LIBPBD_API double PBD_APICALLTYPE
283 #if defined(_MSC_VER) && (_MSC_VER < 1800)
284 //***************************************************************
288 // Emulates C99 expm1() using exp().
292 // On Success: (('e' raised to the power of 'x') - 1)
293 // (e.g. expm1(1) == 1.7182818).
294 // On Failure: None, except that calling exp(x) should generate
295 // an appropriate error for us (such as INF etc).
297 LIBPBD_API double PBD_APICALLTYPE
300 return (exp(x) - (double)1.0);
303 //***************************************************************
307 // Emulates C99 log1p() using log().
311 // On Success: The natural logarithm of (1 + x)
312 // (e.g. log1p(1) == 0.69314718).
313 // On Failure: None, except that calling log(x) should generate
314 // an appropriate error for us (such as ERANGE etc).
316 LIBPBD_API double PBD_APICALLTYPE
319 return (log(x + (double)1.0));
323 #if defined(_MSC_VER) && (_MSC_VER < 1900)
324 //***************************************************************
328 // Emulates C99 log2() using log().
332 // On Success: The binary (base-2) logarithm of 'x'
333 // (e.g. log2(1024) == 10).
334 // On Failure: None, except that calling log(x) should generate
335 // an appropriate error for us (such as ERANGE etc).
337 LIBPBD_API double PBD_APICALLTYPE
340 return (log(x) / log((double)2.0));
346 //***************************************************************
348 // TestForMinimumSpecOS()
350 // Tests the user's OS to see if it is Win2K or later (could be
351 // expanded quite easily to accommodate other OS's)
355 // On Success: TRUE (if the user's OS matches the minimum spec)
356 // On Failure: FALSE otherwise
358 LIBPBD_API bool PBD_APICALLTYPE
359 TestForMinimumSpecOS(char *revision /* currently ignored */)
362 #ifdef PLATFORM_WINDOWS
364 HINSTANCE hKernelDll = (HINSTANCE)dlopen("kernel32.dll", RTLD_NOW);
368 // 'CreateHardLink()' is only available from Win2K onwards.
369 if (NULL != dlsym(hKernelDll, "CreateHardLinkA"))
375 // Other OS's could be accommodated here
381 //***************************************************************
385 // Emulates POSIX realpath() using Win32 _fullpath().
389 // On Success: A pointer to the resolved (absolute) path
392 LIBPBD_API char* PBD_APICALLTYPE
393 realpath (const char *original_path, char resolved_path[_MAX_PATH+1])
396 bool bIsSymLink = 0; // We'll probably need to test the incoming path
397 // to find out if it points to a Windows shortcut
398 // (or a hard link) and set this appropriately.
401 // At the moment I'm not sure if Windows '_fullpath()' is directly
402 // equivalent to POSIX 'realpath()' - in as much as the latter will
403 // resolve the supplied path if it happens to point to a symbolic
404 // link ('_fullpath()' probably DOESN'T do this but I'm not really
405 // sure if Ardour needs such functionality anyway). Therefore we'll
406 // possibly need to add that functionality here at a later date.
410 char temp[(MAX_PATH+1)*6]; // Allow for maximum length of a path in UTF8 characters
412 // POSIX 'realpath()' requires that the buffer size is at
413 // least PATH_MAX+1, so assume that the user knew this !!
414 pRet = _fullpath(temp, Glib::locale_from_utf8(original_path).c_str(), _MAX_PATH);
416 strcpy(resolved_path, Glib::locale_to_utf8(temp).c_str());
423 //***************************************************************
427 // Creates a pointer to a DIR structure, appropriately filled in
428 // and ready to begin a directory search iteration.
432 // On Success: Pointer to a (heap based) DIR structure
435 LIBPBD_API DIR* PBD_APICALLTYPE
436 opendir (const char *szPath)
438 wchar_t wpath[PATH_MAX+1];
447 if ((!errno) && ('\0' == szPath[0]))
450 // Determine if the given path really is a directory
453 if (0 == MultiByteToWideChar (CP_UTF8, 0, (LPCSTR)szPath, -1, (LPWSTR)wpath, sizeof(wpath)))
456 if ((!errno) && ((rc = GetFileAttributesW(wpath)) == -1))
459 if ((!errno) && (!(rc & FILE_ATTRIBUTE_DIRECTORY)))
460 // Error. Entry exists but not a directory. */
465 // Allocate enough memory to store DIR structure, plus
466 // the complete directory path originally supplied.
467 pDir = (DIR *)malloc(sizeof(DIR) + strlen(szPath) + strlen("\\") + strlen ("*"));
471 // Error - out of memory
478 // Create the search expression
479 strcpy(pDir->dd_name, szPath);
481 // Add a backslash if the path doesn't already end with one
482 if (pDir->dd_name[0] != '\0' &&
483 pDir->dd_name[strlen(pDir->dd_name) - 1] != '/' &&
484 pDir->dd_name[strlen(pDir->dd_name) - 1] != '\\')
486 strcat (pDir->dd_name, "\\");
489 // Add the search pattern
490 strcat(pDir->dd_name, "*");
492 // Initialize handle to -1 so that a premature closedir()
493 // doesn't try to call _findclose() on it.
494 pDir->dd_handle = (-1);
496 // Initialize the status
499 // Initialize the dirent structure. 'ino' and 'reclen' are invalid under Win32
500 // and 'name' simply points at the appropriate part of the findfirst_t struct.
501 pDir->dd_dir.d_ino = 0;
502 pDir->dd_dir.d_reclen = 0;
503 pDir->dd_dir.d_namlen = 0;
504 strcpy(pDir->dd_dir.d_name, pDir->dd_dta.name);
506 return (pDir); // Succeeded
511 return (DIR *) 0; // Failed
515 //***************************************************************
519 // Return a pointer to a dirent struct, filled with information
520 // about the next entry in the directory.
524 // On Success: A pointer to the supplied DIR's 'dirent' struct
527 LIBPBD_API struct dirent* PBD_APICALLTYPE
533 // Check for valid DIR struct
537 if ((strcmp(pDir->dd_dir.d_name, pDir->dd_dta.name)) && (!errno))
538 // The structure does not seem to be set up correctly
542 if (pDir->dd_stat < 0)
544 // We have already returned all files in this directory
545 // (or the structure has an invalid dd_stat).
546 return (struct dirent *)0;
548 else if (pDir->dd_stat == 0)
550 // We haven't started the search yet.
552 pDir->dd_handle = _findfirst (Glib::locale_from_utf8(pDir->dd_name).c_str(), &(pDir->dd_dta));
554 if (pDir->dd_handle == -1)
555 // The directory is empty
562 // Do not return ENOENT on last file in directory
565 // Get the next search entry
566 if (_findnext (pDir->dd_handle, &(pDir->dd_dta)))
568 // We are off the end or otherwise error
570 _findclose (pDir->dd_handle);
571 pDir->dd_handle = -1;
575 // Update to indicate the correct status number
579 if (pDir->dd_stat > 0)
581 // We successfully got an entry. Details about the file are
582 // already appropriately filled in except for the length of
584 strcpy(pDir->dd_dir.d_name, pDir->dd_dta.name);
585 pDir->dd_dir.d_namlen = strlen (pDir->dd_dir.d_name);
586 return (&pDir->dd_dir); // Succeeded
590 return (struct dirent *) 0; // Failed
594 //***************************************************************
598 // Frees the resources allocated by opendir().
605 LIBPBD_API int PBD_APICALLTYPE
616 if ((-1) != pDir->dd_handle)
617 rc = _findclose (pDir->dd_handle);
619 // Free the DIR structure
622 return rc; // Succeeded
625 return (-1); // Failed
628 //***************************************************************
632 // Emulates Linux mkstemp() using Win32 _mktemp() and _open() etc.
636 // On Success: A file descriptor for the opened file.
639 LIBPBD_API int PBD_APICALLTYPE
640 mkstemp (char *template_name)
644 char szTempPath[PATH_MAX+100]; // Just ensure we have plenty of buffer space
646 if (NULL != (szFileName = _mktemp(template_name)))
648 if (0 != ::GetTempPathA(sizeof(szTempPath), szTempPath))
650 strcat(szTempPath, szFileName);
651 ret = _open(szTempPath, (_O_CREAT|_O_BINARY|_O_TEMPORARY|_O_RDWR|_O_TRUNC), _S_IWRITE);
659 //***************************************************************
663 // Emulates Linux link() using Win32 CreateHardLink()(NTFS only).
667 // On Success: Non-zero.
668 // On Failure: Zero (call 'GetLastError()' to retrieve info)
670 LIBPBD_API int PBD_APICALLTYPE
671 ntfs_link (const char *existing_filepath, const char *link_filepath)
673 int ret = 1; // 'ERROR_INVALID_FUNCTION'
674 bool bValidPath = false;
676 // Make sure we've been sent a valid input string
677 if (existing_filepath && link_filepath)
679 std::string strRoot = existing_filepath;
681 if ((1 < strRoot.length()) && ('\\' == existing_filepath[0]) && ('\\' == existing_filepath[1]))
685 // We've been sent a network path. Convert backslashes to forward slashes temporarily.
686 std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_backslash);
688 // Now, if there are less than four slashes, add a fourth one or abort
689 std::string::iterator iter = strRoot.begin();
690 while ((slashcnt < 4) && (iter != strRoot.end()))
700 // If only 3 slashes were counted, add a trailing slash
704 // Now find the position of the fourth slash
705 iter = strRoot.begin();
707 for (slashcnt=0; slashcnt<4;)
714 if (++iter == strRoot.end())
718 strRoot.resize(charcnt);
724 // Assume a standard Windows style path
725 if (1 < strRoot.length() && (':' == existing_filepath[1]))
727 // Convert backslashes to forward slashes temporarily.
728 std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_backslash);
730 if (2 == strRoot.length())
733 if ('/' == strRoot[2])
743 char szFileSystemType[_MAX_PATH+1];
745 // Restore the original backslashes
746 std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_forwardslash);
748 // Windows only supports hard links for the NTFS filing
749 // system, so let's make sure that's what we're using!!
750 if (::GetVolumeInformationA(strRoot.c_str(), NULL, 0, NULL, NULL, NULL, szFileSystemType, _MAX_PATH+1))
752 std::string strRootFileSystemType = szFileSystemType;
753 std::transform(strRootFileSystemType.begin(), strRootFileSystemType.end(), strRootFileSystemType.begin(), ::toupper);
754 #if (_WIN32_WINNT >= 0x0500)
755 if (0 == strRootFileSystemType.compare("NTFS"))
757 if (TestForMinimumSpecOS()) // Hard links were only available from Win2K onwards
758 if (0 == CreateHardLinkA(link_filepath, existing_filepath, NULL))
759 { // Note that the above API call cannot create a link to a directory, so
760 // should we also be checking that the supplied path was actually a file?
761 ret = GetLastError();
764 SetLastError(ret = 0); // 'NO_ERROR'
768 ret = 4300; // 'ERROR_INVALID_MEDIA'
774 ret = 123; // 'ERROR_INVALID_NAME'
777 ret = 161; // 'ERROR_BAD_PATHNAME'
789 //***************************************************************
793 // Emulates Linux unlink() using Win32 DeleteFile()(NTFS only).
797 // On Success: Non-zero.
798 // On Failure: Zero (call 'GetLastError()' to retrieve info)
800 LIBPBD_API int PBD_APICALLTYPE
801 ntfs_unlink (const char *link_filepath)
803 int ret = 1; // 'ERROR_INVALID_FUNCTION'
804 bool bValidPath = false;
806 // Make sure we've been sent a valid input string
809 std::string strRoot = link_filepath;
811 if ((1 < strRoot.length()) && ('\\' == link_filepath[0]) && ('\\' == link_filepath[1]))
815 // We've been sent a network path. Convert backslashes to forward slashes temporarily.
816 std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_backslash);
818 // Now, if there are less than four slashes, add a fourth one or abort
819 std::string::iterator iter = strRoot.begin();
820 while ((slashcnt < 4) && (iter != strRoot.end()))
830 // If only 3 slashes were counted, add a trailing slash
834 // Now find the position of the fourth slash
835 iter = strRoot.begin();
837 for (slashcnt=0; slashcnt<4;)
844 if (++iter == strRoot.end())
848 strRoot.resize(charcnt);
854 // Assume a standard Windows style path
855 if (1 < strRoot.length() && (':' == link_filepath[1]))
857 // Convert backslashes to forward slashes temporarily.
858 std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_backslash);
860 if (2 == strRoot.length())
863 if ('/' == strRoot[2])
873 char szFileSystemType[_MAX_PATH+1];
875 // Restore the original backslashes
876 std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_forwardslash);
878 // Windows only supports hard links for the NTFS filing
879 // system, so let's make sure that's what we're using!!
880 if (::GetVolumeInformationA(strRoot.c_str(), NULL, 0, NULL, NULL, NULL, szFileSystemType, _MAX_PATH+1))
882 std::string strRootFileSystemType = szFileSystemType;
883 std::transform(strRootFileSystemType.begin(), strRootFileSystemType.end(), strRootFileSystemType.begin(), ::toupper);
884 #if (_WIN32_WINNT >= 0x0500)
885 if (0 == strRootFileSystemType.compare("NTFS"))
886 if (TestForMinimumSpecOS()) // Hard links were only available from Win2K onwards
887 if (0 == DeleteFileA(link_filepath))
888 ret = GetLastError();
890 ret = 0; // 'NO_ERROR'
895 ret = 123; // 'ERROR_INVALID_NAME'
898 ret = 161; // 'ERROR_BAD_PATHNAME'
912 //***************************************************************
916 // Emulates POSIX dlopen() using Win32 LoadLibrary().
920 // On Success: A handle to the opened DLL
923 LIBPBD_API void* PBD_APICALLTYPE
924 dlopen (const char *file_name, int mode)
926 // Note that 'mode' is ignored in Win32
927 return(::LoadLibraryA(Glib::locale_from_utf8(file_name).c_str()));
931 //***************************************************************
935 // Emulates POSIX dlclose() using Win32 FreeLibrary().
939 // On Success: A non-zero number
942 LIBPBD_API int PBD_APICALLTYPE
943 dlclose (void *handle)
945 return (::FreeLibrary((HMODULE)handle));
949 //***************************************************************
953 // Emulates POSIX dlsym() using Win32 GetProcAddress().
957 // On Success: A pointer to the found function or symbol
960 LIBPBD_API void* PBD_APICALLTYPE
961 dlsym (void *handle, const char *symbol_name)
963 // First test for RTLD_DEFAULT and RTLD_NEXT
964 if ((handle == 0/*RTLD_DEFAULT*/) || (handle == ((void *) -1L)/*RTLD_NEXT*/))
966 return 0; // Not yet supported for Win32
969 return (::GetProcAddress((HMODULE)handle, symbol_name));
972 #define LOCAL_ERROR_BUF_SIZE 1024
973 static char szLastWinError[LOCAL_ERROR_BUF_SIZE];
974 //***************************************************************
978 // Emulates POSIX dlerror() using Win32 GetLastError().
982 // On Success: The translated message corresponding to the
984 // On Failure: NULL (if the last error was ERROR_SUCCESS)
986 LIBPBD_API char* PBD_APICALLTYPE
989 DWORD dwLastErrorId = GetLastError();
990 if (ERROR_SUCCESS == dwLastErrorId)
994 if (0 == FormatMessage(
995 FORMAT_MESSAGE_FROM_SYSTEM,
1000 LOCAL_ERROR_BUF_SIZE,
1003 sprintf(szLastWinError, "Could not decipher the previous error message");
1006 // POSIX dlerror() seems to reset the
1007 // error system, so emulate that here
1008 SetLastError(ERROR_SUCCESS);
1011 return(szLastWinError);
1014 #endif // COMPILER_MSVC