1 /* 2 compat: Some compatibility functions (basic memory & string stuff in separate file) 3 4 The mpg123 code is determined to keep it's legacy. A legacy of old, old UNIX. 5 So anything possibly somewhat advanced should be considered to be put here, with proper #ifdef;-) 6 7 copyright 2007-2016 by the mpg123 project - free software under the terms of the LGPL 2.1 8 see COPYING and AUTHORS files in distribution or http://mpg123.org 9 initially written by Thomas Orgis, Windows Unicode stuff by JonY. 10 */ 11 12 #include "config.h" 13 /* This source file does need _POSIX_SOURCE to get some sigaction. */ 14 #define _POSIX_SOURCE 15 #include "compat.h" 16 17 #ifdef _MSC_VER 18 #include <io.h> 19 20 #if(defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_APP)) 21 #define WINDOWS_UWP 22 #endif 23 24 #endif 25 #ifdef HAVE_SYS_STAT_H 26 # include <sys/stat.h> 27 #endif 28 #ifdef HAVE_DIRENT_H 29 # include <dirent.h> 30 #endif 31 32 /* Win32 is only supported with unicode now. These headers also cover 33 module stuff. The WANT_WIN32_UNICODE macro is synonymous with 34 "want windows-specific API, and only the unicode variants of which". */ 35 #ifdef WANT_WIN32_UNICODE 36 #include <wchar.h> 37 #include <windows.h> 38 #include <winnls.h> 39 #include <shlwapi.h> 40 #endif 41 42 #ifdef USE_MODULES 43 # ifdef HAVE_DLFCN_H 44 # include <dlfcn.h> 45 # endif 46 #endif 47 48 #include "debug.h" 49 50 #ifndef WINDOWS_UWP 51 52 char *compat_getenv(const char* name) 53 { 54 char *ret = NULL; 55 #ifdef WANT_WIN32_UNICODE 56 wchar_t *env; 57 wchar_t *wname = NULL; 58 if(win32_utf8_wide(name, &wname, NULL) > 0) 59 { 60 env = _wgetenv(wname); 61 free(wname); 62 if(env) 63 win32_wide_utf8(env, &ret, NULL); 64 } 65 #else 66 ret = getenv(name); 67 if(ret) 68 ret = compat_strdup(ret); 69 #endif 70 return ret; 71 } 72 73 #ifdef WANT_WIN32_UNICODE 74 75 /* Convert unix UTF-8 (or ASCII) paths to Windows wide character paths. */ 76 static wchar_t* u2wpath(const char *upath) 77 { 78 wchar_t* wpath, *p; 79 if(!upath || win32_utf8_wide(upath, &wpath, NULL) < 1) 80 return NULL; 81 for(p=wpath; *p; ++p) 82 if(*p == L'/') 83 *p = L'\\'; 84 return wpath; 85 } 86 87 /* Convert Windows wide character paths to unix UTF-8. */ 88 static char* w2upath(const wchar_t *wpath) 89 { 90 char* upath, *p; 91 if(!wpath || win32_wide_utf8(wpath, &upath, NULL) < 1) 92 return NULL; 93 for(p=upath; *p; ++p) 94 if(*p == '\\') 95 *p = '/'; 96 return upath; 97 } 98 99 /* An absolute path that is too long and not already marked with 100 \\?\ can be marked as a long one and still work. */ 101 static int wpath_need_elongation(wchar_t *wpath) 102 { 103 if( wpath && !PathIsRelativeW(wpath) 104 && wcslen(wpath) > MAX_PATH-1 105 && wcsncmp(L"\\\\?\\", wpath, 4) ) 106 return 1; 107 else 108 return 0; 109 } 110 111 /* Take any wide windows path and turn it into a path that is allowed 112 to be longer than MAX_PATH, if it is not already. */ 113 static wchar_t* wlongpath(wchar_t *wpath) 114 { 115 size_t len, plen; 116 const wchar_t *prefix = L""; 117 wchar_t *wlpath = NULL; 118 if(!wpath) 119 return NULL; 120 121 /* Absolute paths that do not start with \\?\ get that prepended 122 to allow them being long. */ 123 if(!PathIsRelativeW(wpath) && wcsncmp(L"\\\\?\\", wpath, 4)) 124 { 125 if(wcslen(wpath) >= 2 && PathIsUNCW(wpath)) 126 { 127 /* \\server\path -> \\?\UNC\server\path */ 128 prefix = L"\\\\?\\UNC"; 129 ++wpath; /* Skip the first \. */ 130 } 131 else /* c:\some/path -> \\?\c:\some\path */ 132 prefix = L"\\\\?\\"; 133 } 134 plen = wcslen(prefix); 135 len = plen + wcslen(wpath); 136 wlpath = malloc(len+1*sizeof(wchar_t)); 137 if(wlpath) 138 { 139 /* Brute force memory copying, swprintf is too dandy. */ 140 memcpy(wlpath, prefix, sizeof(wchar_t)*plen); 141 memcpy(wlpath+plen, wpath, sizeof(wchar_t)*(len-plen)); 142 wlpath[len] = 0; 143 } 144 return wlpath; 145 } 146 147 /* Convert unix path to wide windows path, optionally marking 148 it as long path if necessary. */ 149 static wchar_t* u2wlongpath(const char *upath) 150 { 151 wchar_t *wpath = NULL; 152 wchar_t *wlpath = NULL; 153 wpath = u2wpath(upath); 154 if(wpath_need_elongation(wpath)) 155 { 156 wlpath = wlongpath(wpath); 157 free(wpath); 158 wpath = wlpath; 159 } 160 return wpath; 161 } 162 163 #endif 164 165 #else 166 167 static wchar_t* u2wlongpath(const char *upath) 168 { 169 wchar_t* wpath, *p; 170 if (!upath || win32_utf8_wide(upath, &wpath, NULL) < 1) 171 return NULL; 172 for (p = wpath; *p; ++p) 173 if (*p == L'/') 174 *p = L'\\'; 175 return wpath; 176 } 177 178 #endif 179 180 /* Always add a default permission mask in case of flags|O_CREAT. */ 181 int compat_open(const char *filename, int flags) 182 { 183 int ret; 184 #if defined (WANT_WIN32_UNICODE) 185 wchar_t *frag = NULL; 186 187 frag = u2wlongpath(filename); 188 /* Fallback to plain open when ucs-2 conversion fails */ 189 if(!frag) 190 goto open_fallback; 191 192 /*Try _wopen */ 193 ret = _wopen(frag, flags|_O_BINARY, _S_IREAD | _S_IWRITE); 194 if(ret != -1 ) 195 goto open_ok; /* msdn says -1 means failure */ 196 197 open_fallback: 198 #endif 199 200 #if (defined(WIN32) && !defined (__CYGWIN__)) 201 /* MSDN says POSIX function is deprecated beginning in Visual C++ 2005 */ 202 /* Try plain old _open(), if it fails, do nothing */ 203 ret = _open(filename, flags|_O_BINARY, _S_IREAD | _S_IWRITE); 204 #else 205 ret = open(filename, flags, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); 206 #endif 207 208 #if defined (WANT_WIN32_UNICODE) 209 open_ok: 210 free(frag); 211 #endif 212 213 return ret; 214 } 215 216 /* Moved over from wav.c, logic with fallbacks added from the 217 example of compat_open(). */ 218 FILE* compat_fopen(const char *filename, const char *mode) 219 { 220 FILE* stream = NULL; 221 #ifdef WANT_WIN32_UNICODE 222 int cnt = 0; 223 wchar_t *wname = NULL; 224 wchar_t *wmode = NULL; 225 226 wname = u2wlongpath(filename); 227 if(!wname) 228 goto fopen_fallback; 229 cnt = win32_utf8_wide(mode, &wmode, NULL); 230 if( (wmode == NULL) || (cnt == 0)) 231 goto fopen_fallback; 232 233 stream = _wfopen(wname, wmode); 234 if(stream) goto fopen_ok; 235 236 fopen_fallback: 237 #endif 238 stream = fopen(filename, mode); 239 #ifdef WANT_WIN32_UNICODE 240 241 fopen_ok: 242 free(wmode); 243 free(wname); 244 #endif 245 return stream; 246 } 247 248 FILE* compat_fdopen(int fd, const char *mode) 249 { 250 return fdopen(fd, mode); 251 } 252 253 int compat_close(int infd) 254 { 255 #if (defined(WIN32) && !defined (__CYGWIN__)) /* MSDN says POSIX function is deprecated beginning in Visual C++ 2005 */ 256 return _close(infd); 257 #else 258 return close(infd); 259 #endif 260 } 261 262 int compat_fclose(FILE *stream) 263 { 264 return fclose(stream); 265 } 266 267 /* Windows Unicode stuff */ 268 269 #ifdef WANT_WIN32_UNICODE 270 int win32_wide_utf8(const wchar_t * const wptr, char **mbptr, size_t * buflen) 271 { 272 size_t len; 273 char *buf; 274 int ret = 0; 275 276 len = WideCharToMultiByte(CP_UTF8, 0, wptr, -1, NULL, 0, NULL, NULL); /* Get utf-8 string length */ 277 buf = calloc(len + 1, sizeof (char)); /* Can we assume sizeof char always = 1? */ 278 279 if(!buf) len = 0; 280 else { 281 if (len != 0) ret = WideCharToMultiByte(CP_UTF8, 0, wptr, -1, buf, len, NULL, NULL); /*Do actual conversion*/ 282 buf[len] = '0'; /* Must terminate */ 283 } 284 *mbptr = buf; /* Set string pointer to allocated buffer */ 285 if(buflen != NULL) *buflen = (len) * sizeof (char); /* Give length of allocated memory if needed. */ 286 return ret; 287 } 288 289 int win32_utf8_wide(const char *const mbptr, wchar_t **wptr, size_t *buflen) 290 { 291 size_t len; 292 wchar_t *buf; 293 int ret = 0; 294 295 len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mbptr, -1, NULL, 0); /* Get converted size */ 296 buf = calloc(len + 1, sizeof (wchar_t)); /* Allocate memory accordingly */ 297 298 if(!buf) len = 0; 299 else { 300 if (len != 0) ret = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, mbptr, -1, buf, len); /* Do conversion */ 301 buf[len] = L'0'; /* Must terminate */ 302 } 303 *wptr = buf; /* Set string pointer to allocated buffer */ 304 if (buflen != NULL) *buflen = len * sizeof (wchar_t); /* Give length of allocated memory if needed. */ 305 return ret; /* Number of characters written */ 306 } 307 #endif 308 309 #ifndef WINDOWS_UWP 310 311 /* 312 The Windows file and path stuff is an extract of jon_y's win32 loader 313 prototype from the loader_rework branch. It's been divided in to 314 reusable functons by ThOr in the hope to work out some generic-looking 315 loader code for both POSIX and Windows. The routines might be 316 helpful for consistent path work in other parts of mpg123, too. 317 318 This all is about getting some working code on a wide range of 319 systems while staying somewhat sane. If it does ridiculously inefficient 320 things with extraneous copies and grabbing of functions that made 321 it late to some official APIs, that's still fine with us. 322 */ 323 324 char* compat_catpath(const char *prefix, const char* path) 325 { 326 char *ret = NULL; 327 #ifdef WANT_WIN32_UNICODE 328 wchar_t *wprefix = NULL; /* Wide windows versions of */ 329 wchar_t *wpath = NULL; /* input arguments. */ 330 wchar_t *locwret = NULL; /* Tmp return value from LocalAlloc */ 331 /* 332 This variation of combinepath can work with long and UNC paths, but 333 is not officially exposed in any DLLs, It also allocates all its buffers 334 internally via LocalAlloc, avoiding buffer overflow problems. 335 ThOr: I presume this hack is for supporting pre-8 Windows, as 336 from Windows 8 on, this is documented in the API. 337 */ 338 HRESULT (__stdcall *mypac)( const wchar_t *in, const wchar_t* more 339 , unsigned long flags, wchar_t **out ) = NULL; 340 HMODULE pathcch = NULL; 341 342 if(!prefix && !path) 343 goto catpath_end; 344 wprefix = u2wpath(prefix); 345 wpath = u2wpath(path); 346 if((prefix && !wprefix) || (path && !wpath)) 347 goto catpath_end; 348 349 /* Again: I presume this whole fun is to get at PathAllocCombine 350 even when pathcch.h is not available (like in MinGW32). */ 351 if( (pathcch = GetModuleHandleA("kernelbase")) ) 352 mypac = (void *)GetProcAddress(pathcch, "PathAllocCombine"); 353 if(mypac) /* PATHCCH_ALLOW_LONG_PATH = 1 per API docs */ 354 { 355 debug("Actually calling PathAllocCombine!"); 356 mypac(wprefix, wpath, 1, &locwret); 357 } 358 else 359 { 360 /* Playing safe, if we'd care much about performance, this would be on 361 the stack. */ 362 locwret = LocalAlloc(LPTR, sizeof(wchar_t)*MAX_PATH); 363 if(locwret) 364 PathCombineW(locwret, wprefix, wpath); 365 } 366 ret = w2upath(locwret); 367 368 catpath_end: 369 LocalFree(locwret); 370 free(wprefix); 371 free(wpath); 372 #else 373 size_t len, prelen, patlen; 374 375 if(path && path[0] == '/') 376 prefix = NULL; /* Absolute path stays as it is. */ 377 prelen = prefix ? strlen(prefix) : 0; 378 patlen = path ? strlen(path) : 0; 379 /* Concatenate the two, put a / in between if both present. */ 380 len = ((prefix && path) ? 1 : 0) + prelen + patlen; 381 ret = malloc(len+1); 382 if(ret) 383 { 384 size_t off=0; 385 memcpy(ret, prefix, prelen); 386 if(prefix && path) 387 ret[prelen+(off++)] = '/'; 388 memcpy(ret+prelen+off, path, patlen); 389 ret[len] = 0; 390 } 391 #endif 392 return ret; 393 } 394 395 int compat_isdir(const char *path) 396 { 397 int ret = 0; 398 #ifdef WANT_WIN32_UNICODE 399 wchar_t *wpath; 400 wpath = u2wlongpath(path); 401 if(wpath) 402 { 403 DWORD attr = GetFileAttributesW(wpath); 404 if(attr != INVALID_FILE_ATTRIBUTES && attr & FILE_ATTRIBUTE_DIRECTORY) 405 ret=1; 406 free(wpath); 407 } 408 #else 409 struct stat sb; 410 if(path && !stat(path, &sb)) 411 { 412 if(S_ISDIR(sb.st_mode)) 413 ret=1; 414 } 415 #endif 416 return ret; 417 } 418 419 struct compat_dir 420 { 421 char *path; 422 #ifdef WANT_WIN32_UNICODE 423 int gotone; /* Got a result stored from FindFirstFileW. */ 424 WIN32_FIND_DATAW d; 425 HANDLE ffn; 426 #else 427 DIR* dir; 428 #endif 429 }; 430 431 struct compat_dir* compat_diropen(char *path) 432 { 433 struct compat_dir *cd; 434 if(!path) 435 return NULL; 436 cd = malloc(sizeof(*cd)); 437 if(!cd) 438 return NULL; 439 #ifdef WANT_WIN32_UNICODE 440 cd->gotone = 0; 441 { 442 char *pattern; 443 wchar_t *wpattern; 444 pattern = compat_catpath(path, "*"); 445 wpattern = u2wlongpath(pattern); 446 if(wpattern) 447 { 448 cd->ffn = FindFirstFileW(wpattern, &(cd->d)); 449 if(cd->ffn == INVALID_HANDLE_VALUE) 450 { 451 /* FindClose() only needed after successful first find, right? */ 452 free(cd); 453 cd = NULL; 454 } 455 else 456 cd->gotone = 1; 457 } 458 free(wpattern); 459 free(pattern); 460 } 461 #else 462 cd->dir = opendir(path); 463 if(!cd->dir) 464 { 465 free(cd); 466 cd = NULL; 467 } 468 #endif 469 if(cd) 470 { 471 cd->path = compat_strdup(path); 472 if(!cd->path) 473 { 474 compat_dirclose(cd); 475 cd = NULL; 476 } 477 } 478 return cd; 479 } 480 481 void compat_dirclose(struct compat_dir *cd) 482 { 483 if(cd) 484 { 485 free(cd->path); 486 #ifdef WANT_WIN32_UNICODE 487 FindClose(cd->ffn); 488 #else 489 closedir(cd->dir); 490 #endif 491 free(cd); 492 } 493 } 494 495 char* compat_nextfile(struct compat_dir *cd) 496 { 497 if(!cd) 498 return NULL; 499 #ifdef WANT_WIN32_UNICODE 500 while(cd->gotone || FindNextFileW(cd->ffn, &(cd->d))) 501 { 502 cd->gotone = 0; 503 if(!(cd->d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 504 { 505 char *ret; 506 win32_wide_utf8(cd->d.cFileName, &ret, NULL); 507 return ret; 508 } 509 } 510 #else 511 { 512 struct dirent *dp; 513 while((dp = readdir(cd->dir))) 514 { 515 struct stat fst; 516 char *fullpath = compat_catpath(cd->path, dp->d_name); 517 if(fullpath && !stat(fullpath, &fst) && S_ISREG(fst.st_mode)) 518 { 519 free(fullpath); 520 return compat_strdup(dp->d_name); 521 } 522 free(fullpath); 523 } 524 } 525 #endif 526 return NULL; 527 } 528 529 char* compat_nextdir(struct compat_dir *cd) 530 { 531 if(!cd) 532 return NULL; 533 #ifdef WANT_WIN32_UNICODE 534 while(cd->gotone || FindNextFileW(cd->ffn, &(cd->d))) 535 { 536 cd->gotone = 0; 537 if(cd->d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 538 { 539 char *ret; 540 win32_wide_utf8(cd->d.cFileName, &ret, NULL); 541 return ret; 542 } 543 } 544 #else 545 { 546 struct dirent *dp; 547 while((dp = readdir(cd->dir))) 548 { 549 struct stat fst; 550 char *fullpath = compat_catpath(cd->path, dp->d_name); 551 if(fullpath && !stat(fullpath, &fst) && S_ISDIR(fst.st_mode)) 552 { 553 free(fullpath); 554 return compat_strdup(dp->d_name); 555 } 556 free(fullpath); 557 } 558 } 559 #endif 560 return NULL; 561 } 562 563 #endif 564 565 #ifdef USE_MODULES 566 /* 567 This is what I expected the platform-specific dance for dynamic module 568 support to be. Little did I know about the peculiarities of (long) 569 paths and directory/file search on Windows. 570 */ 571 572 void *compat_dlopen(const char *path) 573 { 574 void *handle = NULL; 575 #ifdef WANT_WIN32_UNICODE 576 wchar_t *wpath; 577 wpath = u2wlongpath(path); 578 if(wpath) 579 handle = LoadLibraryW(wpath); 580 free(wpath); 581 #else 582 handle = dlopen(path, RTLD_NOW); 583 #endif 584 return handle; 585 } 586 587 void *compat_dlsym(void *handle, const char *name) 588 { 589 void *sym = NULL; 590 if(!handle) 591 return NULL; 592 #ifdef WANT_WIN32_UNICODE 593 sym = GetProcAddress(handle, name); 594 #else 595 sym = dlsym(handle, name); 596 #endif 597 return sym; 598 } 599 600 void compat_dlclose(void *handle) 601 { 602 if(!handle) 603 return; 604 #ifdef WANT_WIN32_UNICODE 605 FreeLibrary(handle); 606 #else 607 dlclose(handle); 608 #endif 609 } 610 611 #endif /* USE_MODULES */ 612 613 614 /* This shall survive signals and any return value less than given byte count 615 is an error */ 616 size_t unintr_write(int fd, void const *buffer, size_t bytes) 617 { 618 size_t written = 0; 619 while(bytes) 620 { 621 ssize_t part = write(fd, (char*)buffer+written, bytes); 622 if(part < 0 && errno != EINTR) 623 break; 624 bytes -= part; 625 written += part; 626 } 627 return written; 628 } 629 630 /* Same for reading the data. */ 631 size_t unintr_read(int fd, void *buffer, size_t bytes) 632 { 633 size_t got = 0; 634 while(bytes) 635 { 636 ssize_t part = read(fd, (char*)buffer+got, bytes); 637 if(part < 0 && errno != EINTR) 638 break; 639 bytes -= part; 640 got += part; 641 } 642 return got; 643 } 644 645 #ifndef NO_CATCHSIGNAL 646 #if (!defined(WIN32) || defined (__CYGWIN__)) && defined(HAVE_SIGNAL_H) 647 void (*catchsignal(int signum, void(*handler)()))() 648 { 649 struct sigaction new_sa; 650 struct sigaction old_sa; 651 652 #ifdef DONT_CATCH_SIGNALS 653 fprintf (stderr, "Not catching any signals.\n"); 654 return ((void (*)()) -1); 655 #endif 656 657 new_sa.sa_handler = handler; 658 sigemptyset(&new_sa.sa_mask); 659 new_sa.sa_flags = 0; 660 if(sigaction(signum, &new_sa, &old_sa) == -1) 661 return ((void (*)()) -1); 662 return (old_sa.sa_handler); 663 } 664 #endif 665 #endif 666