68596e3463ab4c8b20fbb24e9e9cf263def2bc35
[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 //      roundf()
229 //
230 // Emulates roundf() using floorf().
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 float PBD_APICALLTYPE
239 roundf(float x)
240 {
241         return (floorf(x));
242 }
243
244 //***************************************************************
245 //
246 //      round()
247 //
248 // Emulates round() using floor().
249 //
250 //      Returns:
251 //
252 //    On Success: The largest integer that is less than or
253 //                equal to 'x'.
254 //    On Failure: None
255 //
256 LIBPBD_API double PBD_APICALLTYPE
257 round(double x)
258 {
259         return (floor(x));
260 }
261
262 //***************************************************************
263 //
264 //      trunc()
265 //
266 // Emulates trunc() using floor() and ceil().
267 //
268 //      Returns:
269 //
270 //    On Success: The largest integer whose magnitude is less
271 //                than or equal to 'x' (regardless of sign).
272 //    On Failure: None
273 //
274 LIBPBD_API double PBD_APICALLTYPE
275 trunc(double x)
276 {
277         if (x < 0)
278                 return (ceil(x));
279
280         return (floor(x));
281 }
282
283 #if defined(_MSC_VER) && (_MSC_VER < 1800)
284 //***************************************************************
285 //
286 //      expm1()
287 //
288 // Emulates C99 expm1() using exp().
289 //
290 //      Returns:
291 //
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).
296 //
297 LIBPBD_API double PBD_APICALLTYPE
298 expm1(double x)
299 {
300         return (exp(x) - (double)1.0);
301 }
302
303 //***************************************************************
304 //
305 //      log1p()
306 //
307 // Emulates C99 log1p() using log().
308 //
309 //      Returns:
310 //
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).
315 //
316 LIBPBD_API double PBD_APICALLTYPE
317 log1p(double x)
318 {
319         return (log(x + (double)1.0));
320 }
321 #endif
322
323 #if defined(_MSC_VER) && (_MSC_VER < 1900)
324 //***************************************************************
325 //
326 //      log2()
327 //
328 // Emulates C99 log2() using log().
329 //
330 //      Returns:
331 //
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).
336 //
337 LIBPBD_API double PBD_APICALLTYPE
338 log2(double x)
339 {
340         return (log(x) / log((double)2.0));
341 }
342 #endif
343
344 namespace PBD {
345
346 //***************************************************************
347 //
348 //      TestForMinimumSpecOS()
349 //
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)
352 //
353 //      Returns:
354 //
355 //    On Success: TRUE (if the user's OS matches the minimum spec)
356 //    On Failure: FALSE otherwise
357 //
358 LIBPBD_API bool PBD_APICALLTYPE
359 TestForMinimumSpecOS(char *revision /* currently ignored */)
360 {
361 bool bRet = true;
362 #ifdef PLATFORM_WINDOWS
363         bRet = false;
364         HINSTANCE hKernelDll = (HINSTANCE)dlopen("kernel32.dll", RTLD_NOW);
365
366         if (hKernelDll)
367         {
368                 // 'CreateHardLink()' is only available from Win2K onwards.
369                 if (NULL != dlsym(hKernelDll, "CreateHardLinkA"))
370                         bRet = true;
371
372                 dlclose(hKernelDll);
373         }
374 #endif
375         // Other OS's could be accommodated here
376
377         return (bRet);
378 }
379
380
381 //***************************************************************
382 //
383 //      realpath()
384 //
385 // Emulates POSIX realpath() using Win32 _fullpath().
386 //
387 //      Returns:
388 //
389 //    On Success: A pointer to the resolved (absolute) path
390 //    On Failure: NULL
391 //
392 LIBPBD_API char* PBD_APICALLTYPE
393 realpath (const char *original_path, char resolved_path[_MAX_PATH+1])
394 {
395 char *pRet = NULL;
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.
399         if (bIsSymLink)
400         {
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.
407         }
408         else
409         {
410                 char temp[(MAX_PATH+1)*6]; // Allow for maximum length of a path in UTF8 characters
411
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);
415                 if (NULL != pRet)
416                         strcpy(resolved_path, Glib::locale_to_utf8(temp).c_str());
417         }
418
419         return (pRet);
420 }
421
422
423 //***************************************************************
424 //
425 //      opendir()
426 //
427 // Creates a pointer to a DIR structure, appropriately filled in
428 // and ready to begin a directory search iteration.
429 //
430 //      Returns:
431 //
432 //    On Success: Pointer to a (heap based) DIR structure
433 //    On Failure: NULL
434 //
435 LIBPBD_API DIR* PBD_APICALLTYPE
436 opendir (const char *szPath)
437 {
438 wchar_t wpath[PATH_MAX+1];
439 unsigned int rc;
440 DIR *pDir = 0;
441
442         errno = 0;
443
444         if (!szPath)
445                 errno = EFAULT;
446
447         if ((!errno) && ('\0' == szPath[0]))
448                 errno = ENOTDIR;
449
450         // Determine if the given path really is a directory
451
452         if (!errno)
453                 if (0 == MultiByteToWideChar (CP_UTF8, 0, (LPCSTR)szPath, -1, (LPWSTR)wpath, sizeof(wpath)))
454                         errno = EFAULT;
455
456         if ((!errno) && ((rc = GetFileAttributesW(wpath)) == -1))
457                 errno = ENOENT;
458
459         if ((!errno) && (!(rc & FILE_ATTRIBUTE_DIRECTORY)))
460                 // Error. Entry exists but not a directory. */
461                 errno = ENOTDIR;
462
463         if (!errno)
464         {
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 ("*"));
468
469                 if (!pDir)
470                 {
471                         // Error - out of memory
472                         errno = ENOMEM;
473                 }
474         }
475
476         if (!errno)
477         {
478                 // Create the search expression
479                 strcpy(pDir->dd_name, szPath);
480
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] != '\\')
485                 {
486                         strcat (pDir->dd_name, "\\");
487                 }
488
489                 // Add the search pattern
490                 strcat(pDir->dd_name, "*");
491
492                 // Initialize handle to -1 so that a premature closedir()
493                 // doesn't try to call _findclose() on it.
494                 pDir->dd_handle = (-1);
495
496                 // Initialize the status
497                 pDir->dd_stat = 0;
498
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);
505
506                 return (pDir);  // Succeeded
507         }
508
509         if (pDir)
510                 free (pDir);
511         return (DIR *) 0; // Failed
512 }
513
514
515 //***************************************************************
516 //
517 //      readdir()
518 //
519 // Return a pointer to a dirent struct, filled with information
520 // about the next entry in the directory.
521 //
522 //      Returns:
523 //
524 //    On Success: A pointer to the supplied DIR's 'dirent' struct
525 //    On Failure: NULL
526 //
527 LIBPBD_API struct dirent* PBD_APICALLTYPE
528 readdir (DIR* pDir)
529 {
530 int old_errno = 0;
531 errno = 0;
532
533         // Check for valid DIR struct
534         if (!pDir)
535                 errno = EFAULT;
536
537         if ((strcmp(pDir->dd_dir.d_name, pDir->dd_dta.name)) && (!errno))
538                 // The structure does not seem to be set up correctly
539                 errno = EINVAL;
540         else
541         {
542                 if (pDir->dd_stat < 0)
543                 {
544                         // We have already returned all files in this directory
545                         // (or the structure has an invalid dd_stat).
546                         return (struct dirent *)0;
547                 }
548                 else if (pDir->dd_stat == 0)
549                 {
550                         // We haven't started the search yet.
551                         // Start the search
552                         pDir->dd_handle = _findfirst (Glib::locale_from_utf8(pDir->dd_name).c_str(), &(pDir->dd_dta));
553
554                         if (pDir->dd_handle == -1)
555                                 // The directory is empty
556                                 pDir->dd_stat = -1;
557                         else
558                                 pDir->dd_stat = 1;
559                 }
560                 else
561                 {
562                         // Do not return ENOENT on last file in directory
563                         old_errno = errno;
564
565                         // Get the next search entry
566                         if (_findnext (pDir->dd_handle, &(pDir->dd_dta)))
567                         {
568                                 // We are off the end or otherwise error
569                                 errno = old_errno;
570                                 _findclose (pDir->dd_handle);
571                                 pDir->dd_handle = -1;
572                                 pDir->dd_stat = -1;
573                         }
574                         else
575                                 // Update to indicate the correct status number
576                                 pDir->dd_stat++;
577                 }
578
579                 if (pDir->dd_stat > 0)
580                 {
581                         // We successfully got an entry. Details about the file are
582                         // already appropriately filled in except for the length of
583                         // file name.
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
587                 }
588         }
589
590         return (struct dirent *) 0; // Failed
591 }
592
593
594 //***************************************************************
595 //
596 //      closedir()
597 //
598 // Frees the resources allocated by opendir().
599 //
600 //      Returns:
601 //
602 //    On Success: 0
603 //    On Failure: -1
604 //
605 LIBPBD_API int PBD_APICALLTYPE
606 closedir (DIR *pDir)
607 {
608 int rc = 0;
609
610         errno = 0;
611
612         if (!pDir)
613                 errno = EFAULT;
614         else
615         {
616                 if ((-1) != pDir->dd_handle)
617                         rc = _findclose (pDir->dd_handle);
618
619                 // Free the DIR structure
620                 free (pDir);
621
622                 return rc; // Succeeded
623         }
624
625         return (-1); // Failed
626 }
627
628 //***************************************************************
629 //
630 //      mkstemp()
631 //
632 // Emulates Linux mkstemp() using Win32 _mktemp() and _open() etc.
633 //
634 //      Returns:
635 //
636 //    On Success: A file descriptor for the opened file.
637 //    On Failure: (-1)
638 //
639 LIBPBD_API int PBD_APICALLTYPE
640 mkstemp (char *template_name)
641 {
642 int ret = (-1);
643 char *szFileName;
644 char szTempPath[PATH_MAX+100]; // Just ensure we have plenty of buffer space
645
646         if (NULL != (szFileName = _mktemp(template_name)))
647         {
648                 if (0 != ::GetTempPathA(sizeof(szTempPath), szTempPath))
649                 {
650                         strcat(szTempPath, szFileName);
651                         ret = _open(szTempPath, (_O_CREAT|_O_BINARY|_O_TEMPORARY|_O_RDWR|_O_TRUNC), _S_IWRITE);
652                 }
653         }
654
655         return (ret);
656 }
657
658
659 //***************************************************************
660 //
661 //      ntfs_link()
662 //
663 // Emulates Linux link() using Win32 CreateHardLink()(NTFS only).
664 //
665 //      Returns:
666 //
667 //    On Success: Non-zero.
668 //    On Failure: Zero (call 'GetLastError()' to retrieve info)
669 //
670 LIBPBD_API int PBD_APICALLTYPE
671 ntfs_link (const char *existing_filepath, const char *link_filepath)
672 {
673 int ret = 1; // 'ERROR_INVALID_FUNCTION'
674 bool bValidPath = false;
675
676         // Make sure we've been sent a valid input string
677         if (existing_filepath && link_filepath)
678         {
679                 std::string strRoot = existing_filepath;
680
681                 if ((1 < strRoot.length()) && ('\\' == existing_filepath[0]) && ('\\' == existing_filepath[1]))
682                 {
683                         int slashcnt = 0;
684
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);
687
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()))
691                         {
692                                 if ('/' == (*iter))
693                                         slashcnt++;
694
695                                 ++iter;
696                         }
697
698                         if (slashcnt > 2)
699                         {
700                                 // If only 3 slashes were counted, add a trailing slash
701                                 if (slashcnt == 3)
702                                         strRoot += '/';
703
704                                 // Now find the position of the fourth slash
705                                 iter = strRoot.begin();
706                                 int charcnt = 0;
707                                 for (slashcnt=0; slashcnt<4;)
708                                 {
709                                         charcnt++;
710
711                                         if ('/' == (*iter))
712                                                 slashcnt++;
713
714                                         if (++iter == strRoot.end())
715                                                 break;
716                                 }
717
718                                 strRoot.resize(charcnt);
719                                 bValidPath = true;
720                         }
721                 }
722                 else
723                 {
724                         // Assume a standard Windows style path
725                         if (1 < strRoot.length() && (':' == existing_filepath[1]))
726                         {
727                                 // Convert backslashes to forward slashes temporarily.
728                                 std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_backslash);
729
730                                 if (2 == strRoot.length())
731                                         strRoot += '/';
732
733                                 if ('/' == strRoot[2])
734                                 {
735                                         strRoot.resize(3);
736                                         bValidPath = true;
737                                 }
738                         }
739                 }
740
741                 if (bValidPath)
742                 {
743                         char szFileSystemType[_MAX_PATH+1];
744
745                         // Restore the original backslashes
746                         std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_forwardslash);
747
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))
751                         {
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"))
756                                 {
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();
762                                                 }
763                                                 else
764                                                         SetLastError(ret = 0); // 'NO_ERROR'
765                                 }
766                                 else
767                                 {
768                                         ret = 4300; // 'ERROR_INVALID_MEDIA'
769                                 }
770 #endif
771                         }
772                 }
773                 else
774                         ret = 123; // 'ERROR_INVALID_NAME'
775         }
776         else
777                 ret = 161; // 'ERROR_BAD_PATHNAME'
778
779         if (ret)
780         {
781                 SetLastError(ret);
782                 return (-1);
783         }
784         else
785                 return (0);
786 }
787
788
789 //***************************************************************
790 //
791 //      ntfs_unlink()
792 //
793 // Emulates Linux unlink() using Win32 DeleteFile()(NTFS only).
794 //
795 //      Returns:
796 //
797 //    On Success: Non-zero.
798 //    On Failure: Zero (call 'GetLastError()' to retrieve info)
799 //
800 LIBPBD_API int PBD_APICALLTYPE
801 ntfs_unlink (const char *link_filepath)
802 {
803 int ret = 1; // 'ERROR_INVALID_FUNCTION'
804 bool bValidPath = false;
805
806         // Make sure we've been sent a valid input string
807         if (link_filepath)
808         {
809                 std::string strRoot = link_filepath;
810
811                 if ((1 < strRoot.length()) && ('\\' == link_filepath[0]) && ('\\' == link_filepath[1]))
812                 {
813                         int slashcnt = 0;
814
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);
817
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()))
821                         {
822                                 if ('/' == (*iter))
823                                         slashcnt++;
824
825                                 ++iter;
826                         }
827
828                         if (slashcnt > 2)
829                         {
830                                 // If only 3 slashes were counted, add a trailing slash
831                                 if (slashcnt == 3)
832                                         strRoot += '/';
833
834                                 // Now find the position of the fourth slash
835                                 iter = strRoot.begin();
836                                 int charcnt = 0;
837                                 for (slashcnt=0; slashcnt<4;)
838                                 {
839                                         charcnt++;
840
841                                         if ('/' == (*iter))
842                                                 slashcnt++;
843
844                                         if (++iter == strRoot.end())
845                                                 break;
846                                 }
847
848                                 strRoot.resize(charcnt);
849                                 bValidPath = true;
850                         }
851                 }
852                 else
853                 {
854                         // Assume a standard Windows style path
855                         if (1 < strRoot.length() && (':' == link_filepath[1]))
856                         {
857                                 // Convert backslashes to forward slashes temporarily.
858                                 std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_backslash);
859
860                                 if (2 == strRoot.length())
861                                         strRoot += '/';
862
863                                 if ('/' == strRoot[2])
864                                 {
865                                         strRoot.resize(3);
866                                         bValidPath = true;
867                                 }
868                         }
869                 }
870
871                 if (bValidPath)
872                 {
873                         char szFileSystemType[_MAX_PATH+1];
874
875                         // Restore the original backslashes
876                         std::transform(strRoot.begin(), strRoot.end(), strRoot.begin(), invert_forwardslash);
877
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))
881                         {
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();
889                                                 else
890                                                         ret = 0; // 'NO_ERROR'
891 #endif
892                         }
893                 }
894                 else
895                         ret = 123; // 'ERROR_INVALID_NAME'
896         }
897         else
898                 ret = 161; // 'ERROR_BAD_PATHNAME'
899
900         if (ret)
901         {
902                 SetLastError(ret);
903                 return (-1);
904         }
905         else
906                 return (0);
907 }
908
909 }  // namespace PBD
910
911
912 //***************************************************************
913 //
914 //      dlopen()
915 //
916 // Emulates POSIX dlopen() using Win32 LoadLibrary().
917 //
918 //      Returns:
919 //
920 //    On Success: A handle to the opened DLL
921 //    On Failure: NULL
922 //
923 LIBPBD_API void* PBD_APICALLTYPE
924 dlopen (const char *file_name, int mode)
925 {
926         // Note that 'mode' is ignored in Win32
927         return(::LoadLibraryA(Glib::locale_from_utf8(file_name).c_str()));
928 }
929
930
931 //***************************************************************
932 //
933 //      dlclose()
934 //
935 // Emulates POSIX dlclose() using Win32 FreeLibrary().
936 //
937 //      Returns:
938 //
939 //    On Success: A non-zero number
940 //    On Failure: 0
941 //
942 LIBPBD_API int PBD_APICALLTYPE
943 dlclose (void *handle)
944 {
945         return (::FreeLibrary((HMODULE)handle));
946 }
947
948
949 //***************************************************************
950 //
951 //      dlsym()
952 //
953 // Emulates POSIX dlsym() using Win32 GetProcAddress().
954 //
955 //      Returns:
956 //
957 //    On Success: A pointer to the found function or symbol
958 //    On Failure: NULL
959 //
960 LIBPBD_API void* PBD_APICALLTYPE
961 dlsym (void *handle, const char *symbol_name)
962 {
963         // First test for RTLD_DEFAULT and RTLD_NEXT
964         if ((handle == 0/*RTLD_DEFAULT*/) || (handle == ((void *) -1L)/*RTLD_NEXT*/))
965         {
966                 return 0; // Not yet supported for Win32
967         }
968         else
969                 return (::GetProcAddress((HMODULE)handle, symbol_name));
970 }
971
972 #define LOCAL_ERROR_BUF_SIZE 1024
973 static char szLastWinError[LOCAL_ERROR_BUF_SIZE];
974 //***************************************************************
975 //
976 //      dlerror()
977 //
978 // Emulates POSIX dlerror() using Win32 GetLastError().
979 //
980 //      Returns:
981 //
982 //    On Success: The translated message corresponding to the
983 //                last error
984 //    On Failure: NULL (if the last error was ERROR_SUCCESS)
985 //
986 LIBPBD_API char* PBD_APICALLTYPE
987 dlerror ()
988 {
989         DWORD dwLastErrorId = GetLastError();
990         if (ERROR_SUCCESS == dwLastErrorId)
991                 return 0;
992         else
993         {
994                 if (0 == FormatMessage(
995                                         FORMAT_MESSAGE_FROM_SYSTEM,
996                                         NULL,
997                                         dwLastErrorId,
998                                         0,
999                                         szLastWinError,
1000                                         LOCAL_ERROR_BUF_SIZE,
1001                                         0))
1002                 {
1003                         sprintf(szLastWinError, "Could not decipher the previous error message");
1004                 }
1005
1006                 // POSIX dlerror() seems to reset the
1007                 // error system, so emulate that here
1008                 SetLastError(ERROR_SUCCESS);
1009         }
1010
1011         return(szLastWinError);
1012 }
1013
1014 #endif  // COMPILER_MSVC