/*1compat: Some compatibility functions and header inclusions.2Basic standard C stuff, that may barely be above/around C89.34The mpg123 code is determined to keep it's legacy. A legacy of old, old UNIX.5It is envisioned to include this compat header instead of any of the "standard" headers, to catch compatibility issues.6So, don't include stdlib.h or string.h ... include compat.h.78copyright 2007-23 by the mpg123 project - free software under the terms of the LGPL 2.19see COPYING and AUTHORS files in distribution or http://mpg123.org10initially written by Thomas Orgis11*/1213#ifndef MPG123_COMPAT_H14#define MPG123_COMPAT_H1516#include "config.h"1718// We are using C99 now, including possibly single-precision math.19#define _ISO_C99_SOURCE2021#include <errno.h>2223/* realloc, size_t */24#include <stdlib.h>2526#include <stddef.h>2728#include <stdio.h>29#include <math.h>3031#ifdef HAVE_SIGNAL_H32#include <signal.h>33#else34#ifdef HAVE_SYS_SIGNAL_H35#include <sys/signal.h>36#endif37#endif3839#ifdef HAVE_UNISTD_H40#include <unistd.h>41#endif4243/* Types, types, types. */44/* Do we actually need these two in addition to sys/types.h? As replacement? */45#ifdef HAVE_SYS_TYPES_H46#include <sys/types.h>47#endif48#include <inttypes.h>49#include <stdint.h>50/* We want SIZE_MAX, etc. */51#include <limits.h>5253#ifndef SIZE_MAX54#define SIZE_MAX ((size_t)-1)55#endif56#ifndef SSIZE_MAX57#define SSIZE_MAX ((size_t)-1/2)58#endif59#ifndef PTRDIFF_MAX60#define PTRDIFF_MAX SSIZE_MAX61#endif62#ifndef ULONG_MAX63#define ULONG_MAX ((unsigned long)-1)64#endif6566#ifndef INT64_MAX67#define INT64_MAX 9223372036854775807LL68#endif69#ifndef INT64_MIN70#define INT64_MIN (-INT64_MAX - 1)71#endif72#ifndef INT32_MAX73#define INT32_MAX 2147483647L74#endif75#ifndef INT32_MIN76#define INT32_MIN (-INT32_MAX - 1)77#endif7879// Add two values (themselves assumed to be < limit), saturating to given limit.80#define SATURATE_ADD(inout, add, limit) inout = (limit-add >= inout) ? inout+add : limit;81#define SATURATE_SUB(inout, sub, limit) inout = (limit+sub >= inout) ? inout-sub : limit;8283#include <string.h>84#ifdef HAVE_STRINGS_H85#include <strings.h>86#endif8788#ifdef __OS2__89#include <float.h>90#endif9192#ifdef HAVE_SYS_TIME_H93#include <sys/time.h>94#endif95/* For select(), I need select.h according to POSIX 2001, else: sys/time.h sys/types.h unistd.h */96#ifdef HAVE_SYS_SELECT_H97#include <sys/select.h>98#endif99100/* INT123_compat_open makes little sense without */101#include <fcntl.h>102103/* To parse big numbers... */104#ifdef HAVE_ATOLL105#define atobigint atoll106#else107#define atobigint atol108#endif109110typedef unsigned char byte;111112// Annoying hackery to select a safe strtok variant. MS decided to call their strtok_r strtok_s, while113// C11 declares another strtok_s with different prototype. Thanks to you all.114#ifdef HAVE_STRTOK_R115#define INT123_compat_strtok(a, b, c) strtok_r((a), (b), (c))116#endif117118#if (defined(_UCRT) || defined(_MSC_VER) || (defined(__MINGW32__) || defined(__MINGW64__)) || (defined(__WATCOMC__) && defined(__NT__))) && !defined(__CYGWIN__)119#define MPG123_COMPAT_MSVCRT_IO120#ifndef INT123_compat_strtok121#define INT123_compat_strtok(a, b, c) strtok_s((a), (b), (c))122#endif123#endif124125#if defined(MPG123_COMPAT_MSVCRT_IO)126#if defined(_UCRT)127// needs to get checked separately from MSVC and MinGW becuase it is also used by native Clang on Windows128#ifndef MPG123_COMPAT_MSVCRT_IO_64129#define MPG123_COMPAT_MSVCRT_IO_64130#endif131#endif132#if defined(_MSC_VER)133#if (_MSC_VER >= 1200)134// >= VC6135#ifndef MPG123_COMPAT_MSVCRT_IO_64136#define MPG123_COMPAT_MSVCRT_IO_64137#endif138#endif139#endif140#if defined(__MINGW32__) || defined(__MINGW64__)141#if (defined(__MSVCRT__) || defined(_UCRT)) && !defined(__CRTDLL__)142#ifndef MPG123_COMPAT_MSVCRT_IO_64143#define MPG123_COMPAT_MSVCRT_IO_64144#endif145#endif146#endif147#if defined(__WATCOMC__) && defined(__NT__)148#if (__WATCOMC__ >= 1100)149#ifndef MPG123_COMPAT_MSVCRT_IO_64150#define MPG123_COMPAT_MSVCRT_IO_64151#endif152#endif153#endif154#endif155156#if defined(HAVE__SETMODE) || defined(HAVE_SETMODE) || defined(MPG123_COMPAT_MSVCRT_IO)157// For _setmode(), at least.158#include <io.h>159#endif160161#ifndef INT123_compat_strtok162#warning "no safe strtok found"163#define INT123_compat_strtok(a, b, c) strtok((a), (b))164#endif165166/* A safe realloc also for very old systems where realloc(NULL, size) returns NULL. */167void *INT123_safe_realloc(void *ptr, size_t size);168// Also freeing ptr if result is NULL. You can do169// ptr = INT123_safer_realloc(ptr, size)170// Also, ptr = INT123_safer_realloc(ptr, 0) will do free(ptr); ptr=NULL;.171void *INT123_safer_realloc(void *ptr, size_t size);172const char *INT123_strerror(int errnum);173174/* Roll our own strdup() that does not depend on libc feature test macros175and returns NULL on NULL input instead of crashing. */176char* INT123_compat_strdup(const char *s);177178/* Get an environment variable, possibly converted to UTF-8 from wide string.179The return value is a copy that you shall free. */180char *INT123_compat_getenv(const char* name);181182/**183* Opening a file handle can be different.184* This function here is defined to take a path in native encoding (ISO8859 / UTF-8 / ...), or, when MS Windows Unicode support is enabled, an UTF-8 string that will be converted back to native UCS-2 (wide character) before calling the system's open function.185* @param[in] wptr Pointer to wide string.186* @param[in] mbptr Pointer to multibyte string.187* @return file descriptor (>=0) or error code.188*/189int INT123_compat_open(const char *filename, int flags);190FILE* INT123_compat_fopen(const char *filename, const char *mode);191/**192* Also fdopen to avoid having to define POSIX macros in various source files.193*/194FILE* INT123_compat_fdopen(int fd, const char *mode);195196/**197* Closing a file handle can be platform specific.198* This function takes a file descriptor that is to be closed.199* @param[in] infd File descriptor to be closed.200* @return 0 if the file was successfully closed. A return value of -1 indicates an error.201*/202int INT123_compat_close(int infd);203int INT123_compat_fclose(FILE* stream);204205/**206* Setting binary mode on a descriptor, where necessary.207* We do not bother with errors. This has to work.208* You can enable or disable binary mode.209*/210void INT123_compat_binmode(int fd, int enable);211212/* Those do make sense in a separate file, but I chose to include them in compat.c because that's the one source whose object is shared between mpg123 and libmpg123 -- and both need the functionality internally. */213214#if defined (_WIN32) || defined (__CYGWIN__)215/**216* win32_uni2mbc217* Converts a null terminated UCS-2 string to a multibyte (UTF-8) equivalent.218* Caller is supposed to free allocated buffer.219* @param[in] wptr Pointer to wide string.220* @param[out] mbptr Pointer to multibyte string.221* @param[out] buflen Optional parameter for length of allocated buffer.222* @return status of WideCharToMultiByte conversion.223*224* WideCharToMultiByte - http://msdn.microsoft.com/en-us/library/dd374130(VS.85).aspx225*/226int INT123_win32_wide_utf8(const wchar_t * const wptr, char **mbptr, size_t * buflen);227228/**229* win32_uni2mbc230* Converts a null terminated UCS-2 string to a multibyte (UTF-7) equivalent.231* Caller is supposed to free allocated buffer.232* @param[in] wptr Pointer to wide string.233* @param[out] mbptr Pointer to multibyte string.234* @param[out] buflen Optional parameter for length of allocated buffer.235* @return status of WideCharToMultiByte conversion.236*237* WideCharToMultiByte - http://msdn.microsoft.com/en-us/library/dd374130(VS.85).aspx238*/239int INT123_win32_wide_utf7(const wchar_t * const wptr, char **mbptr, size_t * buflen);240241/**242* win32_mbc2uni243* Converts a null terminated UTF-8 string to a UCS-2 equivalent.244* Caller is supposed to free allocated buffer.245* @param[in] mbptr Pointer to multibyte string.246* @param[out] wptr Pointer to wide string.247* @param[out] buflen Optional parameter for length of allocated buffer.248* @return status of WideCharToMultiByte conversion.249*250* MultiByteToWideChar - http://msdn.microsoft.com/en-us/library/dd319072(VS.85).aspx251*/252253int INT123_win32_utf8_wide(const char *const mbptr, wchar_t **wptr, size_t *buflen);254#endif255256/*257A little bit of path abstraction: We always work with plain char strings258that usually represent POSIX-ish UTF-8 paths (something like c:/some/file259might appear). For Windows, those are converted to wide strings with \260instead of / and possible fun is had with prefixes to get around the old261path length limit. Outside of the compat library, that stuff should not262matter, although something like //?/UNC/server/some/file could be thrown263around as UTF-8 string, to be converted to a wide \\?\UNC\server\some\file264just before handing it to Windows API.265266There is a lot of unnecessary memory allocation and string copying because267of this, but this filesystem stuff is not really relevant to mpg123268performance, so the goal is to keep the code outside the compatibility layer269simple.270*/271272/*273Concatenate a prefix and a path, one of them alowed to be NULL.274If the path is already absolute, the prefix is ignored. Relative275parts (like /..) are resolved if this is sensible for the platform276(meaning: for Windows), else they are preserved (on POSIX, actual277file system access would be needed because of symlinks).278*/279char* INT123_compat_catpath(const char *prefix, const char* path);280281/* Return 1 if the given path indicates an existing directory,2820 otherwise. */283int INT123_compat_isdir(const char *path);284285/*286Directory traversal. This talks ASCII/UTF-8 paths externally, converts287to/from wchar_t internally if the platform wants that. Returning NULL288means failure to open/end of listing.289There is no promise about sorting entries.290*/291struct compat_dir;292/* Returns NULL if either directory failed to open or listing is empty.293Listing can still be empty even if non-NULL, so always rely on the294nextfile/nextdir functions. */295struct compat_dir* INT123_compat_diropen(char *path);296void INT123_compat_dirclose(struct compat_dir*);297/* Get the next entry that is a file (or symlink to one).298The returned string is a copy that needs to be freed after use. */299char* INT123_compat_nextfile(struct compat_dir*);300/* Get the next entry that is a directory (or symlink to one).301The returned string is a copy that needs to be freed after use. */302char* INT123_compat_nextdir (struct compat_dir*);303304#ifdef USE_MODULES305/*306For keeping the path mess local, a system-specific dlopen() variant307is contained in here, too. This is very thin wrapping, even sparing308definition of a handle type, just using void pointers.309Use of absolute paths is a good idea if you want to be sure which310file is openend, as default search paths vary.311*/312void *INT123_compat_dlopen (const char *path);313void *INT123_compat_dlsym (void *handle, const char* name);314void INT123_compat_dlclose(void *handle);315#endif316317/* Blocking write/read of data with signal resilience.318They continue after being interrupted by signals and always return the319amount of processed data (shortage indicating actual problem or EOF). */320size_t INT123_unintr_write(int fd, void const *buffer, size_t bytes);321size_t INT123_unintr_read (int fd, void *buffer, size_t bytes);322size_t INT123_unintr_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);323324/* OSX SDK defines an enum with "normal" as value. That clashes with325optimize.h */326#ifdef __APPLE__327#define normal mpg123_normal328#endif329330#include "../common/true.h"331332#if (!defined(WIN32) || defined (__CYGWIN__)) && defined(HAVE_SIGNAL_H)333void (*INT123_catchsignal(int signum, void(*handler)(int)))(int);334#endif335336// Some ancient toolchains miss the documented errno value.337#if defined(_WIN32) && !defined(EOVERFLOW)338#define EOVERFLOW 132339#endif340341#endif342343344