manually revert 4b3043cc and 141e6fb8181; add detailed explanatory comment
[ardour.git] / libs / pbd / msvc / msvc_pbd.cc
1 /*
2     Copyright (C) 2009 John Emmas
3
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.
8
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.
13
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.
17
18 */
19
20 #ifdef COMPILER_MSVC
21
22 #include <WTypes.h>
23
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
28
29 #include <algorithm>
30 #include <string>
31 #include <io.h>
32 #include <math.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <stdlib.h>
36 #include <sys/stat.h>
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
41
42 #include <glibmm.h>
43
44 #define DELTA_EPOCH_IN_MICROSECS  11644473600000000Ui64
45
46 struct timezone
47 {
48         int  tz_minuteswest; /* minutes W of Greenwich */
49         int  tz_dsttime;     /* type of dst correction */
50 };
51
52 LIBPBD_API int PBD_APICALLTYPE
53 gettimeofday(struct timeval *__restrict tv, __timezone_ptr_t tz) // Does this need to be exported ?
54 {
55 FILETIME ft;
56 unsigned __int64 tmpres = 0;
57 static int tzflag = 0;
58
59         if (NULL != tv)
60         {
61                 GetSystemTimeAsFileTime(&ft);
62
63                 tmpres |= ft.dwHighDateTime;
64                 tmpres <<= 32;
65                 tmpres |= ft.dwLowDateTime;
66
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);
72         }
73
74         if (NULL != tz)
75         {
76                 struct timezone *ptz = static_cast<struct timezone*> (tz);
77                 if (!tzflag)
78                 {
79                         _tzset();
80                         tzflag++;
81                 }
82                 if (ptz)
83                 {
84                         ptz->tz_minuteswest = _timezone / 60;
85                         ptz->tz_dsttime = _daylight;
86                 }
87         }
88
89         return 0;
90 }
91
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!"
96 #else
97 LIBPBD_API bool operator>  (const pthread_t& lhs, const pthread_t& rhs)
98 {
99         return (std::greater<void*>()(lhs.p, rhs.p));
100 }
101
102 LIBPBD_API bool operator<  (const pthread_t& lhs, const pthread_t& rhs)
103 {
104         return (std::less<void*>()(lhs.p, rhs.p));
105 }
106
107 LIBPBD_API bool operator!= (const pthread_t& lhs, const pthread_t& rhs)
108 {
109         return (std::not_equal_to<void*>()(lhs.p, rhs.p));
110 }
111
112 LIBPBD_API bool operator== (const pthread_t& lhs, const pthread_t& rhs)
113 {
114         return (!(lhs != rhs));
115 }
116 #endif
117
118 // Functions supplied (later) to std::transform
119 //***************************************************************
120 //
121 //      invert_backslash()
122 //
123 // Examines a supplied ASCII character and (if the character is
124 // a backslash) converts it to a forward slash,
125 //
126 //      Returns:
127 //
128 //    The supplied character (converted, if it was a backslash)
129 //
130 char invert_backslash(char character)
131 {
132         if ('\\' == character)
133                 character = '/';
134
135         return (character);
136 }
137
138 //***************************************************************
139 //
140 //      invert_forwardslash()
141 //
142 // Examines a supplied ASCII character and (if the character is
143 // a forward slash) converts it to a backslash,
144 //
145 //      Returns:
146 //
147 //    The supplied character (converted, if it was a fwd slash)
148 //
149 char invert_forwardslash(char character)
150 {
151         if ('/' == character)
152                 character = '\\';
153
154         return (character);
155 }
156
157
158 //***************************************************************
159 //
160 //      pread()
161 //
162 // Emulates pread() using _lseek()/_read()/_lseek().
163 //
164 //      Returns:
165 //
166 //    On Success: The number of bytes read from the file
167 //    On Failure: -1
168 //
169 LIBPBD_API ssize_t PBD_APICALLTYPE
170 pread(int handle, void *buf, size_t nbytes, off_t offset)
171 {
172 int old_errno;
173 ssize_t ret;
174
175         off_t old_offset = _tell(handle);
176
177         if (0 > old_offset)
178                 ret = (-1);
179         else
180         {
181                 _lseek(handle, offset, SEEK_SET);
182                 ret = _read(handle, buf, nbytes);
183
184                 old_errno = errno;
185                 _lseek(handle, old_offset, SEEK_SET);
186                 errno = old_errno;
187         }
188
189         return (ret);
190 }
191
192
193 //***************************************************************
194 //
195 //      pwrite()
196 //
197 // Emulates pwrite() using _lseek()/_write()/_lseek().
198 //
199 //      Returns:
200 //
201 //    On Success: The number of bytes written to the file
202 //    On Failure: -1
203 //
204 LIBPBD_API ssize_t PBD_APICALLTYPE
205 pwrite(int handle, const void *buf, size_t nbytes, off_t offset)
206 {
207 int old_errno;
208 ssize_t ret;
209
210         off_t old_offset = _lseek(handle, offset, SEEK_SET);
211
212         if (0 > old_offset)
213                 ret = (-1);
214         else
215         {
216                 ret = _write(handle, buf, nbytes);
217
218                 old_errno = errno;
219                 _lseek(handle, old_offset, SEEK_SET);
220                 errno = old_errno;
221         }
222
223         return (ret);
224 }
225
226 //***************************************************************
227 //
228 //      round()
229 //
230 // Emulates round() using floor().
231 //
232 //      Returns:
233 //
234 //    On Success: The largest integer that is less than or
235 //                equal to 'x'.
236 //    On Failure: None
237 //
238 LIBPBD_API double PBD_APICALLTYPE
239 round(double x)
240 {
241         return (floor(x));
242 }
243
244 //***************************************************************
245 //
246 //      trunc()
247 //
248 // Emulates trunc() using floor() and ceil().
249 //
250 //      Returns:
251 //
252 //    On Success: The largest integer whose magnitude is less
253 //                than or equal to 'x' (regardless of sign).
254 //    On Failure: None
255 //
256 LIBPBD_API double PBD_APICALLTYPE
257 trunc(double x)
258 {
259         if (x < 0)
260                 return (ceil(x));
261
262         return (floor(x));
263 }
264
265 //***************************************************************
266 //
267 //      log2()
268 //
269 // Emulates C99 log2() using log().
270 //
271 //      Returns:
272 //
273 //    On Success: The binary (base-2) logarithm of 'x'
274 //                (e.g. log2(1024) == 10).
275 //    On Failure: None, except that calling log(x) should generate
276 //                an appropriate error for us (such as ERANGE etc).
277 //
278 LIBPBD_API double PBD_APICALLTYPE
279 log2(double x)
280 {
281         return (log(x) / log((double)2.0));
282 }
283
284 namespace PBD {
285
286 //***************************************************************
287 //
288 //      TestForMinimumSpecOS()
289 //
290 // Tests the user's OS to see if it is Win2K or later (could be
291 // expanded quite easily to accommodate other OS's)
292 //
293 //      Returns:
294 //
295 //    On Success: TRUE (if the user's OS matches the minimum spec)
296 //    On Failure: FALSE otherwise
297 //
298 LIBPBD_API bool PBD_APICALLTYPE
299 TestForMinimumSpecOS(char *revision /* currently ignored */)
300 {
301 bool bRet = true;
302 #ifdef PLATFORM_WINDOWS
303         bRet = false;
304         HINSTANCE hKernelDll = (HINSTANCE)dlopen("kernel32.dll", RTLD_NOW);
305
306         if (hKernelDll)
307         {
308                 // 'CreateHardLink()' is only available from Win2K onwards.
309                 if (NULL != dlsym(hKernelDll, "CreateHardLinkA"))
310                         bRet = true;
311
312                 dlclose(hKernelDll);
313         }
314 #endif
315         // Other OS's could be accommodated here
316
317         return (bRet);
318 }
319
320
321 //***************************************************************
322 //
323 //      realpath()
324 //
325 // Emulates POSIX realpath() using Win32 _fullpath().
326 //
327 //      Returns:
328 //
329 //    On Success: A pointer to the resolved (absolute) path
330 //    On Failure: NULL
331 //
332 LIBPBD_API char* PBD_APICALLTYPE
333 realpath (const char *original_path, char resolved_path[_MAX_PATH+1])
334 {
335 char *pRet = NULL;
336 bool bIsSymLink = 0; // We'll probably need to test the incoming path
337                      // to find out if it points to a Windows shortcut
338                      // (or a hard link) and set this appropriately.
339         if (bIsSymLink)
340         {
341                 // At the moment I'm not sure if Windows '_fullpath()' is directly
342                 // equivalent to POSIX 'realpath()' - in as much as the latter will
343                 // resolve the supplied path if it happens to point to a symbolic
344                 // link ('_fullpath()' probably DOESN'T do this but I'm not really
345                 // sure if Ardour needs such functionality anyway). Therefore we'll
346                 // possibly need to add that functionality here at a later date.
347         }
348         else
349         {
350                 char temp[(MAX_PATH+1)*6]; // Allow for maximum length of a path in UTF8 characters
351
352                 // POSIX 'realpath()' requires that the buffer size is at
353                 // least PATH_MAX+1, so assume that the user knew this !!
354                 pRet = _fullpath(temp, Glib::locale_from_utf8(original_path).c_str(), _MAX_PATH);
355                 if (NULL != pRet)
356                         strcpy(resolved_path, Glib::locale_to_utf8(temp).c_str());
357         }
358
359         return (pRet);
360 }
361
362
363 //***************************************************************
364 //
365 //      opendir()
366 //
367 // Creates a pointer to a DIR structure, appropriately filled in
368 // and ready to begin a directory search iteration.
369 //
370 //      Returns:
371 //
372 //    On Success: Pointer to a (heap based) DIR structure
373 //    On Failure: NULL
374 //
375 LIBPBD_API DIR* PBD_APICALLTYPE
376 opendir (const char *szPath)
377 {
378 wchar_t wpath[PATH_MAX+1];
379 unsigned int rc;
380 DIR *pDir = 0;
381
382         errno = 0;
383
384         if (!szPath)
385                 errno = EFAULT;
386
387         if ((!errno) && ('\0' == szPath[0]))
388                 errno = ENOTDIR;
389
390         // Determine if the given path really is a directory
391
392         if (!errno)
393                 if (0 == MultiByteToWideChar (CP_UTF8, 0, (LPCSTR)szPath, -1, (LPWSTR)wpath, sizeof(wpath)))
394                         errno = EFAULT;
395
396         if ((!errno) && ((rc = GetFileAttributesW(wpath)) == -1))
397                 errno = ENOENT;
398
399         if ((!errno) && (!(rc & FILE_ATTRIBUTE_DIRECTORY)))
400                 // Error. Entry exists but not a directory. */
401                 errno = ENOTDIR;
402
403         if (!errno)
404         {
405                 // Allocate enough memory to store DIR structure, plus
406                 // the complete directory path originally supplied.
407                 pDir = (DIR *)malloc(sizeof(DIR) + strlen(szPath) + strlen("\\") + strlen ("*"));
408
409                 if (!pDir)
410                 {
411                         // Error - out of memory
412                         errno = ENOMEM;
413                 }
414         }
415
416         if (!errno)
417         {
418                 // Create the search expression
419                 strcpy(pDir->dd_name, szPath);
420
421                 // Add a backslash if the path doesn't already end with one
422                 if (pDir->dd_name[0] != '\0' &&
423                         pDir->dd_name[strlen(pDir->dd_name) - 1] != '/' &&
424                         pDir->dd_name[strlen(pDir->dd_name) - 1] != '\\')
425                 {
426                         strcat (pDir->dd_name, "\\");
427                 }
428
429                 // Add the search pattern
430                 strcat(pDir->dd_name, "*");
431
432                 // Initialize handle to -1 so that a premature closedir()
433                 // doesn't try to call _findclose() on it.
434                 pDir->dd_handle = (-1);
435
436                 // Initialize the status
437                 pDir->dd_stat = 0;
438
439                 // Initialize the dirent structure. 'ino' and 'reclen' are invalid under Win32
440                 // and 'name' simply points at the appropriate part of the findfirst_t struct.
441                 pDir->dd_dir.d_ino = 0;
442                 pDir->dd_dir.d_reclen = 0;
443                 pDir->dd_dir.d_namlen = 0;
444                 strcpy(pDir->dd_dir.d_name, pDir->dd_dta.name);
445
446                 return (pDir);  // Succeeded
447         }
448
449         if (pDir)
450                 free (pDir);
451         return (DIR *) 0; // Failed
452 }
453
454
455 //***************************************************************
456 //
457 //      readdir()
458 //
459 // Return a pointer to a dirent struct, filled with information
460 // about the next entry in the directory.
461 //
462 //      Returns:
463 //
464 //    On Success: A pointer to the supplied DIR's 'dirent' struct
465 //    On Failure: NULL
466 //
467 LIBPBD_API struct dirent* PBD_APICALLTYPE
468 readdir (DIR* pDir)
469 {
470 int old_errno = 0;
471 errno = 0;
472
473         // Check for valid DIR struct
474         if (!pDir)
475                 errno = EFAULT;
476
477         if ((strcmp(pDir->dd_dir.d_name, pDir->dd_dta.name)) && (!errno))
478                 // The structure does not seem to be set up correctly
479                 errno = EINVAL;
480         else
481         {
482                 if (pDir->dd_stat < 0)
483                 {
484                         // We have already returned all files in this directory
485                         // (or the structure has an invalid dd_stat).
486                         return (struct dirent *)0;
487                 }
488                 else if (pDir->dd_stat == 0)
489                 {
490                         // We haven't started the search yet.
491                         // Start the search
492                         pDir->dd_handle = _findfirst (Glib::locale_from_utf8(pDir->dd_name).c_str(), &(pDir->dd_dta));
493
494                         if (pDir->dd_handle == -1)
495                                 // The directory is empty
496                                 pDir->dd_stat = -1;
497                         else
498                                 pDir->dd_stat = 1;
499                 }
500                 else
501                 {
502                         // Do not return ENOENT on last file in directory
503                         old_errno = errno;
504
505                         // Get the next search entry
506                         if (_findnext (pDir->dd_handle, &(pDir->dd_dta)))
507                         {
508                                 // We are off the end or otherwise error
509                                 errno = old_errno;
510                                 _findclose (pDir->dd_handle);
511                                 pDir->dd_handle = -1;
512                                 pDir->dd_stat = -1;
513                         }
514                         else
515                                 // Update to indicate the correct status number
516                                 pDir->dd_stat++;
517                 }
518
519                 if (pDir->dd_stat > 0)
520                 {
521                         // We successfully got an entry. Details about the file are
522                         // already appropriately filled in except for the length of
523                         // file name.
524                         strcpy(pDir->dd_dir.d_name, pDir->dd_dta.name);
525                         pDir->dd_dir.d_namlen = strlen (pDir->dd_dir.d_name);
526                         return (&pDir->dd_dir); // Succeeded
527                 }
528         }
529
530         return (struct dirent *) 0; // Failed
531 }
532
533
534 //***************************************************************
535 //
536 //      closedir()
537 //
538 // Frees the resources allocated by opendir().
539 //
540 //      Returns:
541 //
542 //    On Success: 0
543 //    On Failure: -1
544 //
545 LIBPBD_API int PBD_APICALLTYPE
546 closedir (DIR *pDir)
547 {
548 int rc = 0;
549
550         errno = 0;
551
552         if (!pDir)
553                 errno = EFAULT;
554         else
555         {
556                 if ((-1) != pDir->dd_handle)
557                         rc = _findclose (pDir->dd_handle);
558
559                 // Free the DIR structure
560                 free (pDir);
561
562                 return rc; // Succeeded
563         }
564
565         return (-1); // Failed
566 }
567
568 //***************************************************************
569 //
570 //      mkstemp()
571 //
572 // Emulates Linux mkstemp() using Win32 _mktemp() and _open() etc.
573 //
574 //      Returns:
575 //
576 //    On Success: A file descriptor for the opened file.
577 //    On Failure: (-1)
578 //
579 LIBPBD_API int PBD_APICALLTYPE
580 mkstemp (char *template_name)
581 {
582 int ret = (-1);
583 char *szFileName;
584 char szTempPath[PATH_MAX+100]; // Just ensure we have plenty of buffer space
585
586         if (NULL != (szFileName = _mktemp(template_name)))
587         {
588                 if (0 != ::GetTempPathA(sizeof(szTempPath), szTempPath))
589                 {
590                         strcat(szTempPath, szFileName);
591                         ret = _open(szTempPath, (_O_CREAT|_O_BINARY|_O_TEMPORARY|_O_RDWR|_O_TRUNC), _S_IWRITE);
592                 }
593         }
594
595         return (ret);
596 }
597
598
599 //***************************************************************
600 //
601 //      ntfs_link()
602 //
603 // Emulates Linux link() using Win32 CreateHardLink()(NTFS only).
604 //
605 //      Returns:
606 //
607 //    On Success: Non-zero.
608 //    On Failure: Zero (call 'GetLastError()' to retrieve info)
609 //
610 LIBPBD_API int PBD_APICALLTYPE
611 ntfs_link (const char *existing_filepath, const char *link_filepath)
612 {
613 int ret = 1; // 'ERROR_INVALID_FUNCTION'
614 bool bValidPath = false;
615
616         // Make sure we've been sent a valid input string
617         if (existing_filepath && link_filepath)
618         {
619                 std::string strRoot = existing_filepath;
620
621                 if ((1 < strRoot.length()) && ('\\' == existing_filepath[0]) && ('\\' == existing_filepath[1]))
622                 {
623                         int slashcnt = 0;
624
625                         // We've been sent a network path. Convert backslashes to forward slashes temporarily.
626                         std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_backslash);
627
628                         // Now, if there are less than four slashes, add a fourth one or abort
629                         std::string::iterator iter = strRoot.begin();
630                         while ((slashcnt < 4) && (iter != strRoot.end()))
631                         {
632                                 if ('/' == (*iter))
633                                         slashcnt++;
634
635                                 ++iter;
636                         }
637
638                         if (slashcnt > 2)
639                         {
640                                 // If only 3 slashes were counted, add a trailing slash
641                                 if (slashcnt == 3)
642                                         strRoot += '/';
643
644                                 // Now find the position of the fourth slash
645                                 iter = strRoot.begin();
646                                 int charcnt = 0;
647                                 for (slashcnt=0; slashcnt<4;)
648                                 {
649                                         charcnt++;
650
651                                         if ('/' == (*iter))
652                                                 slashcnt++;
653
654                                         if (++iter == strRoot.end())
655                                                 break;
656                                 }
657
658                                 strRoot.resize(charcnt);
659                                 bValidPath = true;
660                         }
661                 }
662                 else
663                 {
664                         // Assume a standard Windows style path
665                         if (1 < strRoot.length() && (':' == existing_filepath[1]))
666                         {
667                                 // Convert backslashes to forward slashes temporarily.
668                                 std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_backslash);
669
670                                 if (2 == strRoot.length())
671                                         strRoot += '/';
672
673                                 if ('/' == strRoot[2])
674                                 {
675                                         strRoot.resize(3);
676                                         bValidPath = true;
677                                 }
678                         }
679                 }
680
681                 if (bValidPath)
682                 {
683                         char szFileSystemType[_MAX_PATH+1];
684
685                         // Restore the original backslashes
686                         std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_forwardslash);
687
688                         // Windows only supports hard links for the NTFS filing
689                         // system, so let's make sure that's what we're using!!
690                         if (::GetVolumeInformationA(strRoot.c_str(), NULL, 0, NULL, NULL, NULL, szFileSystemType, _MAX_PATH+1))
691                         {
692                                 std::string strRootFileSystemType = szFileSystemType;
693                                 std::transform(strRootFileSystemType.begin(), strRootFileSystemType.end(), strRootFileSystemType.begin(), ::toupper);
694 #if (_WIN32_WINNT >= 0x0500)
695                                 if (0 == strRootFileSystemType.compare("NTFS"))
696                                 {
697                                         if (TestForMinimumSpecOS()) // Hard links were only available from Win2K onwards
698                                                 if (0 == CreateHardLinkA(link_filepath, existing_filepath, NULL))
699                                                 {       // Note that the above API call cannot create a link to a directory, so
700                                                         // should we also be checking that the supplied path was actually a file?
701                                                         ret = GetLastError();
702                                                 }
703                                                 else
704                                                         SetLastError(ret = 0); // 'NO_ERROR'
705                                 }
706                                 else
707                                 {
708                                         ret = 4300; // 'ERROR_INVALID_MEDIA'
709                                 }
710 #endif
711                         }
712                 }
713                 else
714                         ret = 123; // 'ERROR_INVALID_NAME'
715         }
716         else
717                 ret = 161; // 'ERROR_BAD_PATHNAME'
718
719         if (ret)
720         {
721                 SetLastError(ret);
722                 return (-1);
723         }
724         else
725                 return (0);
726 }
727
728
729 //***************************************************************
730 //
731 //      ntfs_unlink()
732 //
733 // Emulates Linux unlink() using Win32 DeleteFile()(NTFS only).
734 //
735 //      Returns:
736 //
737 //    On Success: Non-zero.
738 //    On Failure: Zero (call 'GetLastError()' to retrieve info)
739 //
740 LIBPBD_API int PBD_APICALLTYPE
741 ntfs_unlink (const char *link_filepath)
742 {
743 int ret = 1; // 'ERROR_INVALID_FUNCTION'
744 bool bValidPath = false;
745
746         // Make sure we've been sent a valid input string
747         if (link_filepath)
748         {
749                 std::string strRoot = link_filepath;
750
751                 if ((1 < strRoot.length()) && ('\\' == link_filepath[0]) && ('\\' == link_filepath[1]))
752                 {
753                         int slashcnt = 0;
754
755                         // We've been sent a network path. Convert backslashes to forward slashes temporarily.
756                         std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_backslash);
757
758                         // Now, if there are less than four slashes, add a fourth one or abort
759                         std::string::iterator iter = strRoot.begin();
760                         while ((slashcnt < 4) && (iter != strRoot.end()))
761                         {
762                                 if ('/' == (*iter))
763                                         slashcnt++;
764
765                                 ++iter;
766                         }
767
768                         if (slashcnt > 2)
769                         {
770                                 // If only 3 slashes were counted, add a trailing slash
771                                 if (slashcnt == 3)
772                                         strRoot += '/';
773
774                                 // Now find the position of the fourth slash
775                                 iter = strRoot.begin();
776                                 int charcnt = 0;
777                                 for (slashcnt=0; slashcnt<4;)
778                                 {
779                                         charcnt++;
780
781                                         if ('/' == (*iter))
782                                                 slashcnt++;
783
784                                         if (++iter == strRoot.end())
785                                                 break;
786                                 }
787
788                                 strRoot.resize(charcnt);
789                                 bValidPath = true;
790                         }
791                 }
792                 else
793                 {
794                         // Assume a standard Windows style path
795                         if (1 < strRoot.length() && (':' == link_filepath[1]))
796                         {
797                                 // Convert backslashes to forward slashes temporarily.
798                                 std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_backslash);
799
800                                 if (2 == strRoot.length())
801                                         strRoot += '/';
802
803                                 if ('/' == strRoot[2])
804                                 {
805                                         strRoot.resize(3);
806                                         bValidPath = true;
807                                 }
808                         }
809                 }
810
811                 if (bValidPath)
812                 {
813                         char szFileSystemType[_MAX_PATH+1];
814
815                         // Restore the original backslashes
816                         std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_forwardslash);
817
818                         // Windows only supports hard links for the NTFS filing
819                         // system, so let's make sure that's what we're using!!
820                         if (::GetVolumeInformationA(strRoot.c_str(), NULL, 0, NULL, NULL, NULL, szFileSystemType, _MAX_PATH+1))
821                         {
822                                 std::string strRootFileSystemType = szFileSystemType;
823                                 std::transform(strRootFileSystemType.begin(), strRootFileSystemType.end(), strRootFileSystemType.begin(), ::toupper);
824 #if (_WIN32_WINNT >= 0x0500)
825                                 if (0 == strRootFileSystemType.compare("NTFS"))
826                                         if (TestForMinimumSpecOS()) // Hard links were only available from Win2K onwards
827                                                 if (0 == DeleteFileA(link_filepath))
828                                                         ret = GetLastError();
829                                                 else
830                                                         ret = 0; // 'NO_ERROR'
831 #endif
832                         }
833                 }
834                 else
835                         ret = 123; // 'ERROR_INVALID_NAME'
836         }
837         else
838                 ret = 161; // 'ERROR_BAD_PATHNAME'
839
840         if (ret)
841         {
842                 SetLastError(ret);
843                 return (-1);
844         }
845         else
846                 return (0);
847 }
848
849 }  // namespace PBD
850
851
852 //***************************************************************
853 //
854 //      dlopen()
855 //
856 // Emulates POSIX dlopen() using Win32 LoadLibrary().
857 //
858 //      Returns:
859 //
860 //    On Success: A handle to the opened DLL
861 //    On Failure: NULL
862 //
863 LIBPBD_API void* PBD_APICALLTYPE
864 dlopen (const char *file_name, int mode)
865 {
866         // Note that 'mode' is ignored in Win32
867         return(::LoadLibraryA(Glib::locale_from_utf8(file_name).c_str()));
868 }
869
870
871 //***************************************************************
872 //
873 //      dlclose()
874 //
875 // Emulates POSIX dlclose() using Win32 FreeLibrary().
876 //
877 //      Returns:
878 //
879 //    On Success: A non-zero number
880 //    On Failure: 0
881 //
882 LIBPBD_API int PBD_APICALLTYPE
883 dlclose (void *handle)
884 {
885         return (::FreeLibrary((HMODULE)handle));
886 }
887
888
889 //***************************************************************
890 //
891 //      dlsym()
892 //
893 // Emulates POSIX dlsym() using Win32 GetProcAddress().
894 //
895 //      Returns:
896 //
897 //    On Success: A pointer to the found function or symbol
898 //    On Failure: NULL
899 //
900 LIBPBD_API void* PBD_APICALLTYPE
901 dlsym (void *handle, const char *symbol_name)
902 {
903         // First test for RTLD_DEFAULT and RTLD_NEXT
904         if ((handle == 0/*RTLD_DEFAULT*/) || (handle == ((void *) -1L)/*RTLD_NEXT*/))
905         {
906                 return 0; // Not yet supported for Win32
907         }
908         else
909                 return (::GetProcAddress((HMODULE)handle, symbol_name));
910 }
911
912 #define LOCAL_ERROR_BUF_SIZE 1024
913 static char szLastWinError[LOCAL_ERROR_BUF_SIZE];
914 //***************************************************************
915 //
916 //      dlerror()
917 //
918 // Emulates POSIX dlerror() using Win32 GetLastError().
919 //
920 //      Returns:
921 //
922 //    On Success: The translated message corresponding to the
923 //                last error
924 //    On Failure: NULL (if the last error was ERROR_SUCCESS)
925 //
926 LIBPBD_API char* PBD_APICALLTYPE
927 dlerror ()
928 {
929         DWORD dwLastErrorId = GetLastError();
930         if (ERROR_SUCCESS == dwLastErrorId)
931                 return 0;
932         else
933         {
934                 if (0 == FormatMessage(
935                                         FORMAT_MESSAGE_FROM_SYSTEM,
936                                         NULL,
937                                         dwLastErrorId,
938                                         0,
939                                         szLastWinError,
940                                         LOCAL_ERROR_BUF_SIZE,
941                                         0))
942                 {
943                         sprintf(szLastWinError, "Could not decipher the previous error message");
944                 }
945
946                 // POSIX dlerror() seems to reset the
947                 // error system, so emulate that here
948                 SetLastError(ERROR_SUCCESS);
949         }
950
951         return(szLastWinError);
952 }
953
954 #endif  // COMPILER_MSVC