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