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 
compat_getenv(const char * name)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. */
u2wpath(const char * upath)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. */
w2upath(const wchar_t * wpath)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. */
wpath_need_elongation(wchar_t * wpath)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. */
wlongpath(wchar_t * wpath)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. */
u2wlongpath(const char * upath)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 
u2wlongpath(const char * upath)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. */
compat_open(const char * filename,int flags)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(). */
compat_fopen(const char * filename,const char * mode)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 
compat_fdopen(int fd,const char * mode)248 FILE* compat_fdopen(int fd, const char *mode)
249 {
250 	return fdopen(fd, mode);
251 }
252 
compat_close(int infd)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 
compat_fclose(FILE * stream)262 int compat_fclose(FILE *stream)
263 {
264 	return fclose(stream);
265 }
266 
267 /* Windows Unicode stuff */
268 
269 #ifdef WANT_WIN32_UNICODE
win32_wide_utf8(const wchar_t * const wptr,char ** mbptr,size_t * buflen)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 
win32_utf8_wide(const char * const mbptr,wchar_t ** wptr,size_t * buflen)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 
compat_catpath(const char * prefix,const char * path)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 
compat_isdir(const char * path)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 
compat_diropen(char * path)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 
compat_dirclose(struct compat_dir * cd)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 
compat_nextfile(struct compat_dir * cd)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 
compat_nextdir(struct compat_dir * cd)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 
compat_dlopen(const char * path)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 
compat_dlsym(void * handle,const char * name)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 
compat_dlclose(void * handle)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 */
unintr_write(int fd,void const * buffer,size_t bytes)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. */
unintr_read(int fd,void * buffer,size_t bytes)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)
catchsignal(int signum,void (* handler)())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