2012-02-03 07:05:11 +00:00
|
|
|
/*
|
|
|
|
* unicode/utf-8 I/O helpers and wrappers for Windows
|
|
|
|
*
|
|
|
|
* Contains parts based on libav code (http://libav.org).
|
|
|
|
*
|
2015-04-13 07:36:54 +00:00
|
|
|
* This file is part of mpv.
|
|
|
|
*
|
Relicense some non-MPlayer source files to LGPL 2.1 or later
This covers source files which were added in mplayer2 and mpv times
only, and where all code is covered by LGPL relicensing agreements.
There are probably more files to which this applies, but I'm being
conservative here.
A file named ao_sdl.c exists in MPlayer too, but the mpv one is a
complete rewrite, and was added some time after the original ao_sdl.c
was removed. The same applies to vo_sdl.c, for which the SDL2 API is
radically different in addition (MPlayer supports SDL 1.2 only).
common.c contains only code written by me. But common.h is a strange
case: although it originally was named mp_common.h and exists in MPlayer
too, by now it contains only definitions written by uau and me. The
exceptions are the CONTROL_ defines - thus not changing the license of
common.h yet.
codec_tags.c contained once large tables generated from MPlayer's
codecs.conf, but all of these tables were removed.
From demux_playlist.c I'm removing a code fragment from someone who was
not asked; this probably could be done later (see commit 15dccc37).
misc.c is a bit complicated to reason about (it was split off mplayer.c
and thus contains random functions out of this file), but actually all
functions have been added post-MPlayer. Except get_relative_time(),
which was written by uau, but looks similar to 3 different versions of
something similar in each of the Unix/win32/OSX timer source files. I'm
not sure what that means in regards to copyright, so I've just moved it
into another still-GPL source file for now.
screenshot.c once had some minor parts of MPlayer's vf_screenshot.c, but
they're all gone.
2016-01-19 17:36:06 +00:00
|
|
|
* mpv is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
2012-02-03 07:05:11 +00:00
|
|
|
*
|
2015-04-13 07:36:54 +00:00
|
|
|
* mpv is distributed in the hope that it will be useful,
|
2012-02-03 07:05:11 +00:00
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
Relicense some non-MPlayer source files to LGPL 2.1 or later
This covers source files which were added in mplayer2 and mpv times
only, and where all code is covered by LGPL relicensing agreements.
There are probably more files to which this applies, but I'm being
conservative here.
A file named ao_sdl.c exists in MPlayer too, but the mpv one is a
complete rewrite, and was added some time after the original ao_sdl.c
was removed. The same applies to vo_sdl.c, for which the SDL2 API is
radically different in addition (MPlayer supports SDL 1.2 only).
common.c contains only code written by me. But common.h is a strange
case: although it originally was named mp_common.h and exists in MPlayer
too, by now it contains only definitions written by uau and me. The
exceptions are the CONTROL_ defines - thus not changing the license of
common.h yet.
codec_tags.c contained once large tables generated from MPlayer's
codecs.conf, but all of these tables were removed.
From demux_playlist.c I'm removing a code fragment from someone who was
not asked; this probably could be done later (see commit 15dccc37).
misc.c is a bit complicated to reason about (it was split off mplayer.c
and thus contains random functions out of this file), but actually all
functions have been added post-MPlayer. Except get_relative_time(),
which was written by uau, but looks similar to 3 different versions of
something similar in each of the Unix/win32/OSX timer source files. I'm
not sure what that means in regards to copyright, so I've just moved it
into another still-GPL source file for now.
screenshot.c once had some minor parts of MPlayer's vf_screenshot.c, but
they're all gone.
2016-01-19 17:36:06 +00:00
|
|
|
* GNU Lesser General Public License for more details.
|
2012-02-03 07:05:11 +00:00
|
|
|
*
|
Relicense some non-MPlayer source files to LGPL 2.1 or later
This covers source files which were added in mplayer2 and mpv times
only, and where all code is covered by LGPL relicensing agreements.
There are probably more files to which this applies, but I'm being
conservative here.
A file named ao_sdl.c exists in MPlayer too, but the mpv one is a
complete rewrite, and was added some time after the original ao_sdl.c
was removed. The same applies to vo_sdl.c, for which the SDL2 API is
radically different in addition (MPlayer supports SDL 1.2 only).
common.c contains only code written by me. But common.h is a strange
case: although it originally was named mp_common.h and exists in MPlayer
too, by now it contains only definitions written by uau and me. The
exceptions are the CONTROL_ defines - thus not changing the license of
common.h yet.
codec_tags.c contained once large tables generated from MPlayer's
codecs.conf, but all of these tables were removed.
From demux_playlist.c I'm removing a code fragment from someone who was
not asked; this probably could be done later (see commit 15dccc37).
misc.c is a bit complicated to reason about (it was split off mplayer.c
and thus contains random functions out of this file), but actually all
functions have been added post-MPlayer. Except get_relative_time(),
which was written by uau, but looks similar to 3 different versions of
something similar in each of the Unix/win32/OSX timer source files. I'm
not sure what that means in regards to copyright, so I've just moved it
into another still-GPL source file for now.
screenshot.c once had some minor parts of MPlayer's vf_screenshot.c, but
they're all gone.
2016-01-19 17:36:06 +00:00
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
|
2012-02-03 07:05:11 +00:00
|
|
|
*/
|
2015-04-13 07:36:54 +00:00
|
|
|
|
2014-12-26 16:14:48 +00:00
|
|
|
#include <assert.h>
|
osdep: add mkostemps() emulation
Supposed to follow the standard function.
The standard function is not standard, but a GNU extension. Adding some
ifdef mess is pointless too - it has no advantages other than having a
mess, and not spotting implementation bugs in the emulation due to
running it only on "obscure" platforms (like Windows, so most computers
actually, except the developer's platform).
There is mkstemp(), which at least is in POSIX 2008. But it's 100%
useless, except in some obscure cases: it doesn't set O_CLOEXEC, nor can
you pass it to it. Without O_CLOEXEC, we'd leak the temporary file to
all child processes. (The fact that the file, which is expected to reach
double or tripple digit GB sizes, will be deleted only once all
processes unreference the FD, makes this sort of a big deal. You could
ftruncate() it, but that doesn't fix all the other problems.)
Why did POSIX standardize mkstemp() and O_CLOEXEC apparently at the same
time, but provided no way to pass O_CLOEXEC to mkstemp()? With the
introduction of O_CLOEXEC, they acknowledged that there's a need to
atomically set the FD_CLOEXEC flag when creating file descriptors.
(FD_CLOEXEC was standard before that, but setting it with fcntl() is
racy.) You're much more likely to need a temp file that is CLOEXEC
rather than the opposite, and even if they were somehow opposed to
CLOEXEC by default (such as for compat. reasons), surely POSIX could
have standardized mkostemp() too or instead.
And then there's the fact that this whole O_CLOEXEC mess is stupid.
Surely there would have been a better way to handle this, instead of
requiring adding O_CLOEXEC to almost ALL instances of open() in all code
that has been written ever. The justification for this is that the
historic default was wrong, and you can't change it (e.g. this won't
work: changing the behavior of exec() and not inherit the FD to the
child process, unless a hypothetical O_KEEP_EXEC flag is set).
But on the other hand, surely you could have introduced an exec()
variant which does close all FDs, except a whitelist of FDs passed to
it. Let's call it execve2(). In fact, I'm going to argue that exec()
call sites are the most aware of whether (and which) FDs to inherit.
Some programs even tried to explicitly iterate over all opened FDs and
explicitly close "unwanted" FDs (which of course was problematic for
other reasons), and such an execve2() call would have been the ideal
solution.
Maybe this proposed solution would have had problems too. But surely
revisiting and reviewing every exec*() call would have been simpler than
reviewing every open() call. And more importantly, having to extend
every damn library function that either calls open() or creates FDs in
some other way, like mkstemp().
What argument are there going to be against this? That there will be
library code that can't keep working correctly with processes that use
the "old" exec? Well, what about all my legacy library code that uses
open() incorrectly, and that will break no matter what?
Well, I'm not going to claim that I can come up with better solutions
than POSIX (generally or in this case), but this situation is ABSOLUTELY
ATROCIOUS. It makes win32 programming look attractive compared to POSIX,
that standard pandering to dead people from the past. (Note: not trying
to insult dead people.)
I'm not sure what POSIX is even doing. Anything useful? Doesn't look
like it to me. Are they paid? Why? They didn't even fix the locale mess,
nor do they intend to. I bet they're proud of discussing compatibility
to 70ies code day in and day out iwtohut ever producing anything useful.
What a load of crap. They seriously got to do better than this.
Oh, and my wrapper is probably buggy. Fortunately that doesn't matter.
Also I'm dumping this into io.h. Originally, io.h was just supposed to
replace broken implementation of standard functions by MinGW (and then
by Android), but whatever, just give a dumping ground for shit code.
2019-06-13 16:23:05 +00:00
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <unistd.h>
|
2013-11-30 22:17:20 +00:00
|
|
|
|
2016-01-11 18:03:40 +00:00
|
|
|
#include "mpv_talloc.h"
|
2013-12-17 01:18:16 +00:00
|
|
|
|
2013-11-20 17:12:58 +00:00
|
|
|
#include "config.h"
|
2022-08-15 01:28:54 +00:00
|
|
|
#include "misc/random.h"
|
2013-11-30 21:40:51 +00:00
|
|
|
#include "osdep/io.h"
|
2014-08-21 20:11:38 +00:00
|
|
|
#include "osdep/terminal.h"
|
2013-11-30 21:40:51 +00:00
|
|
|
|
2017-06-27 11:50:58 +00:00
|
|
|
#if HAVE_UWP
|
|
|
|
// Missing from MinGW headers.
|
|
|
|
#include <windows.h>
|
|
|
|
WINBASEAPI UINT WINAPI GetTempFileNameW(LPCWSTR lpPathName, LPCWSTR lpPrefixString,
|
|
|
|
UINT uUnique, LPWSTR lpTempFileName);
|
|
|
|
WINBASEAPI DWORD WINAPI GetCurrentDirectoryW(DWORD nBufferLength, LPWSTR lpBuffer);
|
|
|
|
WINBASEAPI DWORD WINAPI GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength,
|
|
|
|
LPWSTR lpBuffer, LPWSTR *lpFilePart);
|
|
|
|
#endif
|
|
|
|
|
2013-11-30 21:40:51 +00:00
|
|
|
// Set the CLOEXEC flag on the given fd.
|
|
|
|
// On error, false is returned (and errno set).
|
|
|
|
bool mp_set_cloexec(int fd)
|
|
|
|
{
|
2014-04-12 18:13:07 +00:00
|
|
|
#if defined(F_SETFD)
|
2013-11-30 21:40:51 +00:00
|
|
|
if (fd >= 0) {
|
|
|
|
int flags = fcntl(fd, F_GETFD);
|
|
|
|
if (flags == -1)
|
|
|
|
return false;
|
|
|
|
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
2013-11-20 17:12:58 +00:00
|
|
|
|
2014-05-29 21:56:56 +00:00
|
|
|
#ifdef __MINGW32__
|
2014-10-25 23:18:55 +00:00
|
|
|
int mp_make_cloexec_pipe(int pipes[2])
|
2014-05-29 21:56:56 +00:00
|
|
|
{
|
|
|
|
pipes[0] = pipes[1] = -1;
|
2014-07-25 12:28:14 +00:00
|
|
|
return -1;
|
2014-05-29 21:56:56 +00:00
|
|
|
}
|
|
|
|
#else
|
2014-10-25 23:18:55 +00:00
|
|
|
int mp_make_cloexec_pipe(int pipes[2])
|
2014-05-29 21:56:56 +00:00
|
|
|
{
|
|
|
|
if (pipe(pipes) != 0) {
|
|
|
|
pipes[0] = pipes[1] = -1;
|
2014-07-25 12:28:14 +00:00
|
|
|
return -1;
|
2014-05-29 21:56:56 +00:00
|
|
|
}
|
|
|
|
|
2014-10-25 23:18:55 +00:00
|
|
|
for (int i = 0; i < 2; i++)
|
2014-05-29 21:56:56 +00:00
|
|
|
mp_set_cloexec(pipes[i]);
|
2014-10-25 23:18:55 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef __MINGW32__
|
|
|
|
int mp_make_wakeup_pipe(int pipes[2])
|
|
|
|
{
|
2014-11-08 06:17:43 +00:00
|
|
|
return mp_make_cloexec_pipe(pipes);
|
2014-10-25 23:18:55 +00:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
// create a pipe, and set it to non-blocking (and also set FD_CLOEXEC)
|
|
|
|
int mp_make_wakeup_pipe(int pipes[2])
|
|
|
|
{
|
|
|
|
if (mp_make_cloexec_pipe(pipes) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
for (int i = 0; i < 2; i++) {
|
2014-05-29 21:56:56 +00:00
|
|
|
int val = fcntl(pipes[i], F_GETFL) | O_NONBLOCK;
|
|
|
|
fcntl(pipes[i], F_SETFL, val);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-07-29 01:24:52 +00:00
|
|
|
void mp_flush_wakeup_pipe(int pipe_end)
|
|
|
|
{
|
|
|
|
#ifndef __MINGW32__
|
|
|
|
char buf[100];
|
|
|
|
(void)read(pipe_end, buf, sizeof(buf));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2012-02-03 07:05:11 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
|
|
|
|
#include <windows.h>
|
|
|
|
#include <wchar.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
|
|
|
|
//copied and modified from libav
|
|
|
|
//http://git.libav.org/?p=libav.git;a=blob;f=libavformat/os_support.c;h=a0fcd6c9ba2be4b0dbcc476f6c53587345cc1152;hb=HEADl30
|
|
|
|
|
2015-08-01 18:51:52 +00:00
|
|
|
wchar_t *mp_from_utf8(void *talloc_ctx, const char *s)
|
2012-02-03 07:05:11 +00:00
|
|
|
{
|
|
|
|
int count = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0);
|
|
|
|
if (count <= 0)
|
|
|
|
abort();
|
2015-08-01 18:51:52 +00:00
|
|
|
wchar_t *ret = talloc_array(talloc_ctx, wchar_t, count);
|
2012-02-03 07:05:11 +00:00
|
|
|
MultiByteToWideChar(CP_UTF8, 0, s, -1, ret, count);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-08-01 18:51:52 +00:00
|
|
|
char *mp_to_utf8(void *talloc_ctx, const wchar_t *s)
|
2012-02-03 07:05:11 +00:00
|
|
|
{
|
|
|
|
int count = WideCharToMultiByte(CP_UTF8, 0, s, -1, NULL, 0, NULL, NULL);
|
|
|
|
if (count <= 0)
|
|
|
|
abort();
|
|
|
|
char *ret = talloc_array(talloc_ctx, char, count);
|
|
|
|
WideCharToMultiByte(CP_UTF8, 0, s, -1, ret, count, NULL, NULL);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // _WIN32
|
|
|
|
|
|
|
|
#ifdef __MINGW32__
|
|
|
|
|
|
|
|
#include <io.h>
|
|
|
|
#include <fcntl.h>
|
2013-09-18 15:49:10 +00:00
|
|
|
#include <pthread.h>
|
|
|
|
|
2017-10-23 10:01:45 +00:00
|
|
|
static void set_errno_from_lasterror(void)
|
2012-02-03 07:05:11 +00:00
|
|
|
{
|
2017-10-23 10:01:45 +00:00
|
|
|
// This just handles the error codes expected from CreateFile at the moment
|
|
|
|
switch (GetLastError()) {
|
|
|
|
case ERROR_FILE_NOT_FOUND:
|
|
|
|
errno = ENOENT;
|
|
|
|
break;
|
|
|
|
case ERROR_SHARING_VIOLATION:
|
|
|
|
case ERROR_ACCESS_DENIED:
|
|
|
|
errno = EACCES;
|
|
|
|
break;
|
|
|
|
case ERROR_FILE_EXISTS:
|
|
|
|
case ERROR_ALREADY_EXISTS:
|
|
|
|
errno = EEXIST;
|
|
|
|
break;
|
|
|
|
case ERROR_PIPE_BUSY:
|
|
|
|
errno = EAGAIN;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
errno = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static time_t filetime_to_unix_time(int64_t wintime)
|
|
|
|
{
|
|
|
|
static const int64_t hns_per_second = 10000000ll;
|
|
|
|
static const int64_t win_to_unix_epoch = 11644473600ll;
|
|
|
|
return wintime / hns_per_second - win_to_unix_epoch;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool get_file_ids_win8(HANDLE h, dev_t *dev, ino_t *ino)
|
|
|
|
{
|
|
|
|
FILE_ID_INFO ii;
|
|
|
|
if (!GetFileInformationByHandleEx(h, FileIdInfo, &ii, sizeof(ii)))
|
|
|
|
return false;
|
|
|
|
*dev = ii.VolumeSerialNumber;
|
|
|
|
// The definition of FILE_ID_128 differs between mingw-w64 and the Windows
|
|
|
|
// SDK, but we can ignore that by just memcpying it. This will also
|
|
|
|
// truncate the file ID on 32-bit Windows, which doesn't support __int128.
|
|
|
|
// 128-bit file IDs are only used for ReFS, so that should be okay.
|
|
|
|
assert(sizeof(*ino) <= sizeof(ii.FileId));
|
|
|
|
memcpy(ino, &ii.FileId, sizeof(*ino));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if HAVE_UWP
|
|
|
|
static bool get_file_ids(HANDLE h, dev_t *dev, ino_t *ino)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static bool get_file_ids(HANDLE h, dev_t *dev, ino_t *ino)
|
|
|
|
{
|
|
|
|
// GetFileInformationByHandle works on FAT partitions and Windows 7, but
|
|
|
|
// doesn't work in UWP and can produce non-unique IDs on ReFS
|
|
|
|
BY_HANDLE_FILE_INFORMATION bhfi;
|
|
|
|
if (!GetFileInformationByHandle(h, &bhfi))
|
|
|
|
return false;
|
|
|
|
*dev = bhfi.dwVolumeSerialNumber;
|
|
|
|
*ino = ((ino_t)bhfi.nFileIndexHigh << 32) | bhfi.nFileIndexLow;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Like fstat(), but with a Windows HANDLE
|
|
|
|
static int hstat(HANDLE h, struct mp_stat *buf)
|
|
|
|
{
|
|
|
|
// Handle special (or unknown) file types first
|
|
|
|
switch (GetFileType(h) & ~FILE_TYPE_REMOTE) {
|
|
|
|
case FILE_TYPE_PIPE:
|
|
|
|
*buf = (struct mp_stat){ .st_nlink = 1, .st_mode = _S_IFIFO | 0644 };
|
|
|
|
return 0;
|
|
|
|
case FILE_TYPE_CHAR: // character device
|
|
|
|
*buf = (struct mp_stat){ .st_nlink = 1, .st_mode = _S_IFCHR | 0644 };
|
|
|
|
return 0;
|
|
|
|
case FILE_TYPE_UNKNOWN:
|
|
|
|
errno = EBADF;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct mp_stat st = { 0 };
|
|
|
|
|
|
|
|
FILE_BASIC_INFO bi;
|
|
|
|
if (!GetFileInformationByHandleEx(h, FileBasicInfo, &bi, sizeof(bi))) {
|
|
|
|
errno = EBADF;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
st.st_atime = filetime_to_unix_time(bi.LastAccessTime.QuadPart);
|
|
|
|
st.st_mtime = filetime_to_unix_time(bi.LastWriteTime.QuadPart);
|
|
|
|
st.st_ctime = filetime_to_unix_time(bi.ChangeTime.QuadPart);
|
|
|
|
|
|
|
|
FILE_STANDARD_INFO si;
|
|
|
|
if (!GetFileInformationByHandleEx(h, FileStandardInfo, &si, sizeof(si))) {
|
|
|
|
errno = EBADF;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
st.st_nlink = si.NumberOfLinks;
|
|
|
|
|
|
|
|
// Here we pretend Windows has POSIX permissions by pretending all
|
|
|
|
// directories are 755 and regular files are 644
|
|
|
|
if (si.Directory) {
|
|
|
|
st.st_mode |= _S_IFDIR | 0755;
|
|
|
|
} else {
|
|
|
|
st.st_mode |= _S_IFREG | 0644;
|
|
|
|
st.st_size = si.EndOfFile.QuadPart;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!get_file_ids_win8(h, &st.st_dev, &st.st_ino)) {
|
|
|
|
// Fall back to the Windows 7 method (also used for FAT in Win8)
|
|
|
|
if (!get_file_ids(h, &st.st_dev, &st.st_ino)) {
|
|
|
|
errno = EBADF;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*buf = st;
|
|
|
|
return 0;
|
2014-10-17 19:46:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int mp_stat(const char *path, struct mp_stat *buf)
|
|
|
|
{
|
2015-08-01 18:51:52 +00:00
|
|
|
wchar_t *wpath = mp_from_utf8(NULL, path);
|
2017-10-23 10:01:45 +00:00
|
|
|
HANDLE h = CreateFileW(wpath, FILE_READ_ATTRIBUTES,
|
|
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
|
|
|
|
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | SECURITY_SQOS_PRESENT |
|
|
|
|
SECURITY_IDENTIFICATION, NULL);
|
2012-02-03 07:05:11 +00:00
|
|
|
talloc_free(wpath);
|
2017-10-23 10:01:45 +00:00
|
|
|
if (h == INVALID_HANDLE_VALUE) {
|
|
|
|
set_errno_from_lasterror();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ret = hstat(h, buf);
|
|
|
|
CloseHandle(h);
|
|
|
|
return ret;
|
2014-10-17 19:46:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int mp_fstat(int fd, struct mp_stat *buf)
|
|
|
|
{
|
2017-10-23 10:01:45 +00:00
|
|
|
HANDLE h = (HANDLE)_get_osfhandle(fd);
|
|
|
|
if (h == INVALID_HANDLE_VALUE) {
|
|
|
|
errno = EBADF;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
// Use mpv's hstat() function rather than MSVCRT's fstat() because mpv's
|
|
|
|
// supports directories and device/inode numbers.
|
|
|
|
return hstat(h, buf);
|
2012-02-03 07:05:11 +00:00
|
|
|
}
|
|
|
|
|
2017-06-27 11:50:58 +00:00
|
|
|
#if HAVE_UWP
|
|
|
|
static int mp_vfprintf(FILE *stream, const char *format, va_list args)
|
|
|
|
{
|
|
|
|
return vfprintf(stream, format, args);
|
|
|
|
}
|
|
|
|
#else
|
2014-08-24 10:56:24 +00:00
|
|
|
static int mp_check_console(HANDLE wstream)
|
2011-10-22 14:24:16 +00:00
|
|
|
{
|
2013-12-28 03:44:12 +00:00
|
|
|
if (wstream != INVALID_HANDLE_VALUE) {
|
|
|
|
unsigned int filetype = GetFileType(wstream);
|
2011-10-22 14:24:16 +00:00
|
|
|
|
2013-12-28 03:44:12 +00:00
|
|
|
if (!((filetype == FILE_TYPE_UNKNOWN) &&
|
|
|
|
(GetLastError() != ERROR_SUCCESS)))
|
2011-10-22 14:24:16 +00:00
|
|
|
{
|
2013-12-28 03:44:12 +00:00
|
|
|
filetype &= ~(FILE_TYPE_REMOTE);
|
|
|
|
|
|
|
|
if (filetype == FILE_TYPE_CHAR) {
|
|
|
|
DWORD ConsoleMode;
|
|
|
|
int ret = GetConsoleMode(wstream, &ConsoleMode);
|
|
|
|
|
|
|
|
if (!(!ret && (GetLastError() == ERROR_INVALID_HANDLE))) {
|
|
|
|
// This seems to be a console
|
|
|
|
return 1;
|
2011-10-22 14:24:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-12-28 03:44:12 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mp_vfprintf(FILE *stream, const char *format, va_list args)
|
|
|
|
{
|
|
|
|
int done = 0;
|
|
|
|
|
2014-08-24 10:56:24 +00:00
|
|
|
HANDLE wstream = INVALID_HANDLE_VALUE;
|
2013-12-28 03:44:12 +00:00
|
|
|
|
|
|
|
if (stream == stdout || stream == stderr) {
|
|
|
|
wstream = GetStdHandle(stream == stdout ?
|
|
|
|
STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mp_check_console(wstream)) {
|
|
|
|
size_t len = vsnprintf(NULL, 0, format, args) + 1;
|
|
|
|
char *buf = talloc_array(NULL, char, len);
|
|
|
|
|
|
|
|
if (buf) {
|
2014-08-21 20:11:38 +00:00
|
|
|
done = vsnprintf(buf, len, format, args);
|
|
|
|
mp_write_console_ansi(wstream, buf);
|
2013-12-28 03:44:12 +00:00
|
|
|
}
|
2014-08-21 20:11:38 +00:00
|
|
|
talloc_free(buf);
|
2013-12-28 03:44:12 +00:00
|
|
|
} else {
|
2011-10-22 14:24:16 +00:00
|
|
|
done = vfprintf(stream, format, args);
|
2013-12-28 03:44:12 +00:00
|
|
|
}
|
2011-10-22 14:24:16 +00:00
|
|
|
|
2012-04-06 21:42:02 +00:00
|
|
|
return done;
|
|
|
|
}
|
2017-06-27 11:50:58 +00:00
|
|
|
#endif
|
2012-04-06 21:42:02 +00:00
|
|
|
|
|
|
|
int mp_fprintf(FILE *stream, const char *format, ...)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
|
|
res = mp_vfprintf(stream, format, args);
|
2011-10-22 14:24:16 +00:00
|
|
|
va_end(args);
|
2012-04-06 21:42:02 +00:00
|
|
|
return res;
|
|
|
|
}
|
2011-10-22 14:24:16 +00:00
|
|
|
|
2012-04-06 21:42:02 +00:00
|
|
|
int mp_printf(const char *format, ...)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
|
|
res = mp_vfprintf(stdout, format, args);
|
|
|
|
va_end(args);
|
|
|
|
return res;
|
2011-10-22 14:24:16 +00:00
|
|
|
}
|
2012-02-03 07:05:11 +00:00
|
|
|
|
|
|
|
int mp_open(const char *filename, int oflag, ...)
|
|
|
|
{
|
2017-10-23 10:01:45 +00:00
|
|
|
// Always use all share modes, which is useful for opening files that are
|
|
|
|
// open in other processes, and also more POSIX-like
|
|
|
|
static const DWORD share =
|
|
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
|
|
|
|
// Setting FILE_APPEND_DATA and avoiding GENERIC_WRITE/FILE_WRITE_DATA
|
|
|
|
// will make the file handle use atomic append behavior
|
|
|
|
static const DWORD append =
|
|
|
|
FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA;
|
|
|
|
|
|
|
|
DWORD access = 0;
|
|
|
|
DWORD disposition = 0;
|
|
|
|
DWORD flags = 0;
|
|
|
|
|
|
|
|
switch (oflag & (_O_RDONLY | _O_RDWR | _O_WRONLY | _O_APPEND)) {
|
|
|
|
case _O_RDONLY:
|
|
|
|
access = GENERIC_READ;
|
|
|
|
flags |= FILE_FLAG_BACKUP_SEMANTICS; // For opening directories
|
|
|
|
break;
|
|
|
|
case _O_RDWR:
|
|
|
|
access = GENERIC_READ | GENERIC_WRITE;
|
|
|
|
break;
|
|
|
|
case _O_RDWR | _O_APPEND:
|
|
|
|
case _O_RDONLY | _O_APPEND:
|
|
|
|
access = GENERIC_READ | append;
|
|
|
|
break;
|
|
|
|
case _O_WRONLY:
|
|
|
|
access = GENERIC_WRITE;
|
|
|
|
break;
|
|
|
|
case _O_WRONLY | _O_APPEND:
|
|
|
|
access = append;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (oflag & (_O_CREAT | _O_EXCL | _O_TRUNC)) {
|
|
|
|
case 0:
|
|
|
|
case _O_EXCL: // Like MSVCRT, ignore invalid use of _O_EXCL
|
|
|
|
disposition = OPEN_EXISTING;
|
|
|
|
break;
|
|
|
|
case _O_TRUNC:
|
|
|
|
case _O_TRUNC | _O_EXCL:
|
|
|
|
disposition = TRUNCATE_EXISTING;
|
|
|
|
break;
|
|
|
|
case _O_CREAT:
|
|
|
|
disposition = OPEN_ALWAYS;
|
|
|
|
flags |= FILE_ATTRIBUTE_NORMAL;
|
|
|
|
break;
|
|
|
|
case _O_CREAT | _O_TRUNC:
|
|
|
|
disposition = CREATE_ALWAYS;
|
|
|
|
break;
|
|
|
|
case _O_CREAT | _O_EXCL:
|
|
|
|
case _O_CREAT | _O_EXCL | _O_TRUNC:
|
|
|
|
disposition = CREATE_NEW;
|
|
|
|
flags |= FILE_ATTRIBUTE_NORMAL;
|
|
|
|
break;
|
2012-02-03 07:05:11 +00:00
|
|
|
}
|
2017-10-23 10:01:45 +00:00
|
|
|
|
|
|
|
// Opening a named pipe as a file can allow the pipe server to impersonate
|
|
|
|
// mpv's process, which could be a security issue. Set SQOS flags, so pipe
|
|
|
|
// servers can only identify the mpv process, not impersonate it.
|
|
|
|
if (disposition != CREATE_NEW)
|
|
|
|
flags |= SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION;
|
|
|
|
|
|
|
|
// Keep the same semantics for some MSVCRT-specific flags
|
|
|
|
if (oflag & _O_TEMPORARY) {
|
|
|
|
flags |= FILE_FLAG_DELETE_ON_CLOSE;
|
|
|
|
access |= DELETE;
|
|
|
|
}
|
|
|
|
if (oflag & _O_SHORT_LIVED)
|
|
|
|
flags |= FILE_ATTRIBUTE_TEMPORARY;
|
|
|
|
if (oflag & _O_SEQUENTIAL) {
|
|
|
|
flags |= FILE_FLAG_SEQUENTIAL_SCAN;
|
|
|
|
} else if (oflag & _O_RANDOM) {
|
|
|
|
flags |= FILE_FLAG_RANDOM_ACCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Open the Windows file handle
|
2015-08-01 18:51:52 +00:00
|
|
|
wchar_t *wpath = mp_from_utf8(NULL, filename);
|
2017-10-23 10:01:45 +00:00
|
|
|
HANDLE h = CreateFileW(wpath, access, share, NULL, disposition, flags, NULL);
|
2012-02-03 07:05:11 +00:00
|
|
|
talloc_free(wpath);
|
2017-10-23 10:01:45 +00:00
|
|
|
if (h == INVALID_HANDLE_VALUE) {
|
|
|
|
set_errno_from_lasterror();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Map the Windows file handle to a CRT file descriptor. Note: MSVCRT only
|
|
|
|
// cares about the following oflags.
|
|
|
|
oflag &= _O_APPEND | _O_RDONLY | _O_RDWR | _O_WRONLY;
|
|
|
|
oflag |= _O_NOINHERIT; // We never create inheritable handles
|
|
|
|
int fd = _open_osfhandle((intptr_t)h, oflag);
|
|
|
|
if (fd < 0) {
|
|
|
|
CloseHandle(h);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fd;
|
2012-02-03 07:05:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int mp_creat(const char *filename, int mode)
|
|
|
|
{
|
2017-10-23 10:01:45 +00:00
|
|
|
return mp_open(filename, _O_CREAT | _O_WRONLY | _O_TRUNC, mode);
|
2012-02-03 07:05:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FILE *mp_fopen(const char *filename, const char *mode)
|
|
|
|
{
|
2017-10-23 10:01:45 +00:00
|
|
|
if (!mode[0]) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int rwmode;
|
|
|
|
int oflags = 0;
|
|
|
|
switch (mode[0]) {
|
|
|
|
case 'r':
|
|
|
|
rwmode = _O_RDONLY;
|
|
|
|
break;
|
|
|
|
case 'w':
|
|
|
|
rwmode = _O_WRONLY;
|
|
|
|
oflags |= _O_CREAT | _O_TRUNC;
|
|
|
|
break;
|
|
|
|
case 'a':
|
|
|
|
rwmode = _O_WRONLY;
|
|
|
|
oflags |= _O_CREAT | _O_APPEND;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
errno = EINVAL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse extra mode flags
|
|
|
|
for (const char *pos = mode + 1; *pos; pos++) {
|
|
|
|
switch (*pos) {
|
|
|
|
case '+': rwmode = _O_RDWR; break;
|
|
|
|
case 'x': oflags |= _O_EXCL; break;
|
|
|
|
// Ignore unknown flags (glibc does too)
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Open a CRT file descriptor
|
|
|
|
int fd = mp_open(filename, rwmode | oflags);
|
|
|
|
if (fd < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// Add 'b' to the mode so the CRT knows the file is opened in binary mode
|
|
|
|
char bmode[] = { mode[0], 'b', rwmode == _O_RDWR ? '+' : '\0', '\0' };
|
|
|
|
FILE *fp = fdopen(fd, bmode);
|
|
|
|
if (!fp) {
|
|
|
|
close(fd);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fp;
|
2012-02-03 07:05:11 +00:00
|
|
|
}
|
|
|
|
|
2014-02-03 21:11:33 +00:00
|
|
|
// Windows' MAX_PATH/PATH_MAX/FILENAME_MAX is fixed to 260, but this limit
|
2015-08-01 18:51:52 +00:00
|
|
|
// applies to unicode paths encoded with wchar_t (2 bytes on Windows). The UTF-8
|
|
|
|
// version could end up bigger in memory. In the worst case each wchar_t is
|
2014-02-03 21:11:33 +00:00
|
|
|
// encoded to 3 bytes in UTF-8, so in the worst case we have:
|
|
|
|
// wcslen(wpath) * 3 <= strlen(utf8path)
|
|
|
|
// Thus we need MP_PATH_MAX as the UTF-8/char version of PATH_MAX.
|
|
|
|
// Also make sure there's free space for the terminating \0.
|
|
|
|
// (For codepoints encoded as UTF-16 surrogate pairs, UTF-8 has the same length.)
|
|
|
|
#define MP_PATH_MAX (FILENAME_MAX * 3 + 1)
|
|
|
|
|
2012-02-03 07:05:11 +00:00
|
|
|
struct mp_dir {
|
|
|
|
DIR crap; // must be first member
|
|
|
|
_WDIR *wdir;
|
|
|
|
union {
|
|
|
|
struct dirent dirent;
|
|
|
|
// dirent has space only for FILENAME_MAX bytes. _wdirent has space for
|
2015-08-01 18:51:52 +00:00
|
|
|
// FILENAME_MAX wchar_t, which might end up bigger as UTF-8 in some
|
2012-02-03 07:05:11 +00:00
|
|
|
// cases. Guarantee we can always hold _wdirent.d_name converted to
|
|
|
|
// UTF-8 (see MP_PATH_MAX).
|
|
|
|
// This works because dirent.d_name is the last member of dirent.
|
|
|
|
char space[MP_PATH_MAX];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
DIR* mp_opendir(const char *path)
|
|
|
|
{
|
2015-08-01 18:51:52 +00:00
|
|
|
wchar_t *wpath = mp_from_utf8(NULL, path);
|
2012-02-03 07:05:11 +00:00
|
|
|
_WDIR *wdir = _wopendir(wpath);
|
|
|
|
talloc_free(wpath);
|
|
|
|
if (!wdir)
|
|
|
|
return NULL;
|
|
|
|
struct mp_dir *mpdir = talloc(NULL, struct mp_dir);
|
|
|
|
// DIR is supposed to be opaque, but unfortunately the MinGW headers still
|
|
|
|
// define it. Make sure nobody tries to use it.
|
|
|
|
memset(&mpdir->crap, 0xCD, sizeof(mpdir->crap));
|
|
|
|
mpdir->wdir = wdir;
|
|
|
|
return (DIR*)mpdir;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct dirent* mp_readdir(DIR *dir)
|
|
|
|
{
|
|
|
|
struct mp_dir *mpdir = (struct mp_dir*)dir;
|
|
|
|
struct _wdirent *wdirent = _wreaddir(mpdir->wdir);
|
|
|
|
if (!wdirent)
|
|
|
|
return NULL;
|
|
|
|
size_t buffersize = sizeof(mpdir->space) - offsetof(struct dirent, d_name);
|
|
|
|
WideCharToMultiByte(CP_UTF8, 0, wdirent->d_name, -1, mpdir->dirent.d_name,
|
|
|
|
buffersize, NULL, NULL);
|
|
|
|
mpdir->dirent.d_ino = 0;
|
|
|
|
mpdir->dirent.d_reclen = 0;
|
|
|
|
mpdir->dirent.d_namlen = strlen(mpdir->dirent.d_name);
|
|
|
|
return &mpdir->dirent;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mp_closedir(DIR *dir)
|
|
|
|
{
|
|
|
|
struct mp_dir *mpdir = (struct mp_dir*)dir;
|
|
|
|
int res = _wclosedir(mpdir->wdir);
|
|
|
|
talloc_free(mpdir);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mp_mkdir(const char *path, int mode)
|
|
|
|
{
|
2015-08-01 18:51:52 +00:00
|
|
|
wchar_t *wpath = mp_from_utf8(NULL, path);
|
2012-02-03 07:05:11 +00:00
|
|
|
int res = _wmkdir(wpath);
|
|
|
|
talloc_free(wpath);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2017-04-11 10:16:07 +00:00
|
|
|
char *mp_win32_getcwd(char *buf, size_t size)
|
|
|
|
{
|
2017-04-11 11:51:16 +00:00
|
|
|
if (size >= SIZE_MAX / 3 - 1) {
|
|
|
|
errno = ENOMEM;
|
2017-04-11 10:16:07 +00:00
|
|
|
return NULL;
|
2017-04-11 11:51:16 +00:00
|
|
|
}
|
|
|
|
size_t wbuffer = size * 3 + 1;
|
|
|
|
wchar_t *wres = talloc_array(NULL, wchar_t, wbuffer);
|
|
|
|
DWORD wlen = GetFullPathNameW(L".", wbuffer, wres, NULL);
|
|
|
|
if (wlen >= wbuffer || wlen == 0) {
|
|
|
|
talloc_free(wres);
|
|
|
|
errno = wlen ? ERANGE : ENOENT;
|
|
|
|
return NULL;
|
|
|
|
}
|
2017-04-11 10:16:07 +00:00
|
|
|
char *t = mp_to_utf8(NULL, wres);
|
2017-04-12 12:12:26 +00:00
|
|
|
talloc_free(wres);
|
2017-04-11 10:16:07 +00:00
|
|
|
size_t st = strlen(t);
|
|
|
|
if (st >= size) {
|
|
|
|
talloc_free(t);
|
|
|
|
errno = ERANGE;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
memcpy(buf, t, st + 1);
|
|
|
|
talloc_free(t);
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2013-09-18 15:49:10 +00:00
|
|
|
static char **utf8_environ;
|
|
|
|
static void *utf8_environ_ctx;
|
|
|
|
|
|
|
|
static void free_env(void)
|
|
|
|
{
|
|
|
|
talloc_free(utf8_environ_ctx);
|
|
|
|
utf8_environ_ctx = NULL;
|
|
|
|
utf8_environ = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note: UNIX getenv() returns static strings, and we try to do the same. Since
|
|
|
|
// using putenv() is not multithreading safe, we don't expect env vars to change
|
|
|
|
// at runtime, and converting/allocating them in advance is ok.
|
|
|
|
static void init_getenv(void)
|
|
|
|
{
|
2017-06-27 11:50:58 +00:00
|
|
|
#if !HAVE_UWP
|
2015-08-01 18:51:52 +00:00
|
|
|
wchar_t *wenv = GetEnvironmentStringsW();
|
2013-09-18 15:49:10 +00:00
|
|
|
if (!wenv)
|
|
|
|
return;
|
|
|
|
utf8_environ_ctx = talloc_new(NULL);
|
|
|
|
int num_env = 0;
|
|
|
|
while (1) {
|
|
|
|
size_t len = wcslen(wenv);
|
|
|
|
if (!len)
|
|
|
|
break;
|
|
|
|
char *s = mp_to_utf8(utf8_environ_ctx, wenv);
|
|
|
|
MP_TARRAY_APPEND(utf8_environ_ctx, utf8_environ, num_env, s);
|
|
|
|
wenv += len + 1;
|
|
|
|
}
|
|
|
|
MP_TARRAY_APPEND(utf8_environ_ctx, utf8_environ, num_env, NULL);
|
|
|
|
// Avoid showing up in leak detectors etc.
|
|
|
|
atexit(free_env);
|
2017-06-27 11:50:58 +00:00
|
|
|
#endif
|
2013-09-18 15:49:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
char *mp_getenv(const char *name)
|
|
|
|
{
|
|
|
|
static pthread_once_t once_init_getenv = PTHREAD_ONCE_INIT;
|
|
|
|
pthread_once(&once_init_getenv, init_getenv);
|
|
|
|
// Copied from musl, http://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
|
|
|
|
// Copyright © 2005-2013 Rich Felker, standard MIT license
|
|
|
|
int i;
|
|
|
|
size_t l = strlen(name);
|
|
|
|
if (!utf8_environ || !*name || strchr(name, '=')) return NULL;
|
|
|
|
for (i=0; utf8_environ[i] && (strncmp(name, utf8_environ[i], l)
|
|
|
|
|| utf8_environ[i][l] != '='); i++) {}
|
|
|
|
if (utf8_environ[i]) return utf8_environ[i] + l+1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2023-01-29 12:36:05 +00:00
|
|
|
char ***mp_penviron(void)
|
2020-08-05 11:57:23 +00:00
|
|
|
{
|
|
|
|
mp_getenv(""); // ensure init
|
|
|
|
return &utf8_environ; // `environ' should be an l-value
|
|
|
|
}
|
|
|
|
|
2014-10-17 19:37:38 +00:00
|
|
|
off_t mp_lseek(int fd, off_t offset, int whence)
|
|
|
|
{
|
|
|
|
HANDLE h = (HANDLE)_get_osfhandle(fd);
|
|
|
|
if (h != INVALID_HANDLE_VALUE && GetFileType(h) != FILE_TYPE_DISK) {
|
|
|
|
errno = ESPIPE;
|
|
|
|
return (off_t)-1;
|
|
|
|
}
|
2014-10-18 21:40:35 +00:00
|
|
|
return _lseeki64(fd, offset, whence);
|
2014-10-17 19:37:38 +00:00
|
|
|
}
|
|
|
|
|
2020-06-12 08:41:48 +00:00
|
|
|
_Thread_local
|
|
|
|
static struct {
|
|
|
|
DWORD errcode;
|
|
|
|
char *errstring;
|
|
|
|
} mp_dl_result = {
|
|
|
|
.errcode = 0,
|
|
|
|
.errstring = NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static void mp_dl_free(void)
|
|
|
|
{
|
|
|
|
if (mp_dl_result.errstring != NULL) {
|
|
|
|
talloc_free(mp_dl_result.errstring);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mp_dl_init(void)
|
|
|
|
{
|
|
|
|
atexit(mp_dl_free);
|
|
|
|
}
|
|
|
|
|
|
|
|
void *mp_dlopen(const char *filename, int flag)
|
|
|
|
{
|
|
|
|
wchar_t *wfilename = mp_from_utf8(NULL, filename);
|
|
|
|
HMODULE lib = LoadLibraryW(wfilename);
|
|
|
|
talloc_free(wfilename);
|
|
|
|
mp_dl_result.errcode = GetLastError();
|
|
|
|
return (void *)lib;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *mp_dlsym(void *handle, const char *symbol)
|
|
|
|
{
|
|
|
|
FARPROC addr = GetProcAddress((HMODULE)handle, symbol);
|
|
|
|
mp_dl_result.errcode = GetLastError();
|
|
|
|
return (void *)addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *mp_dlerror(void)
|
|
|
|
{
|
|
|
|
static pthread_once_t once_init_dlerror = PTHREAD_ONCE_INIT;
|
|
|
|
pthread_once(&once_init_dlerror, mp_dl_init);
|
|
|
|
mp_dl_free();
|
|
|
|
|
|
|
|
if (mp_dl_result.errcode == 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// convert error code to a string message
|
|
|
|
LPWSTR werrstring = NULL;
|
|
|
|
FormatMessageW(
|
|
|
|
FORMAT_MESSAGE_FROM_SYSTEM
|
|
|
|
| FORMAT_MESSAGE_IGNORE_INSERTS
|
|
|
|
| FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
|
|
|
NULL,
|
|
|
|
mp_dl_result.errcode,
|
|
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
|
|
|
|
(LPWSTR) &werrstring,
|
|
|
|
0,
|
|
|
|
NULL);
|
|
|
|
mp_dl_result.errcode = 0;
|
|
|
|
|
|
|
|
if (werrstring) {
|
|
|
|
mp_dl_result.errstring = mp_to_utf8(NULL, werrstring);
|
|
|
|
LocalFree(werrstring);
|
|
|
|
}
|
|
|
|
|
|
|
|
return mp_dl_result.errstring == NULL
|
|
|
|
? "unknown error"
|
|
|
|
: mp_dl_result.errstring;
|
|
|
|
}
|
|
|
|
|
2017-06-27 11:50:58 +00:00
|
|
|
#if HAVE_UWP
|
|
|
|
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
|
|
|
|
{
|
|
|
|
errno = ENOSYS;
|
|
|
|
return MAP_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
int munmap(void *addr, size_t length)
|
|
|
|
{
|
|
|
|
errno = ENOSYS;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int msync(void *addr, size_t length, int flags)
|
|
|
|
{
|
|
|
|
errno = ENOSYS;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#else
|
2014-12-26 16:14:48 +00:00
|
|
|
// Limited mmap() wrapper, inspired by:
|
|
|
|
// http://code.google.com/p/mman-win32/source/browse/trunk/mman.c
|
|
|
|
|
|
|
|
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
|
|
|
|
{
|
|
|
|
assert(addr == NULL); // not implemented
|
|
|
|
assert(flags == MAP_SHARED); // not implemented
|
|
|
|
|
|
|
|
HANDLE osf = (HANDLE)_get_osfhandle(fd);
|
|
|
|
if (!osf) {
|
|
|
|
errno = EBADF;
|
|
|
|
return MAP_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD protect = 0;
|
|
|
|
DWORD access = 0;
|
|
|
|
if (prot & PROT_WRITE) {
|
|
|
|
protect = PAGE_READWRITE;
|
|
|
|
access = FILE_MAP_WRITE;
|
|
|
|
} else if (prot & PROT_READ) {
|
|
|
|
protect = PAGE_READONLY;
|
|
|
|
access = FILE_MAP_READ;
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD l_low = (uint32_t)length;
|
|
|
|
DWORD l_high = ((uint64_t)length) >> 32;
|
|
|
|
HANDLE map = CreateFileMapping(osf, NULL, protect, l_high, l_low, NULL);
|
|
|
|
|
|
|
|
if (!map) {
|
|
|
|
errno = EACCES; // something random
|
|
|
|
return MAP_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD o_low = (uint32_t)offset;
|
|
|
|
DWORD o_high = ((uint64_t)offset) >> 32;
|
|
|
|
void *p = MapViewOfFile(map, access, o_high, o_low, length);
|
|
|
|
|
|
|
|
CloseHandle(map);
|
|
|
|
|
|
|
|
if (!p) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return MAP_FAILED;
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
int munmap(void *addr, size_t length)
|
|
|
|
{
|
|
|
|
UnmapViewOfFile(addr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int msync(void *addr, size_t length, int flags)
|
|
|
|
{
|
|
|
|
FlushViewOfFile(addr, length);
|
|
|
|
return 0;
|
|
|
|
}
|
2017-06-27 11:50:58 +00:00
|
|
|
#endif
|
2014-12-26 16:14:48 +00:00
|
|
|
|
2017-11-18 11:36:20 +00:00
|
|
|
locale_t newlocale(int category, const char *locale, locale_t base)
|
|
|
|
{
|
|
|
|
return (locale_t)1;
|
|
|
|
}
|
|
|
|
|
|
|
|
locale_t uselocale(locale_t locobj)
|
|
|
|
{
|
|
|
|
return (locale_t)1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void freelocale(locale_t locobj)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2012-02-03 07:05:11 +00:00
|
|
|
#endif // __MINGW32__
|
osdep: add mkostemps() emulation
Supposed to follow the standard function.
The standard function is not standard, but a GNU extension. Adding some
ifdef mess is pointless too - it has no advantages other than having a
mess, and not spotting implementation bugs in the emulation due to
running it only on "obscure" platforms (like Windows, so most computers
actually, except the developer's platform).
There is mkstemp(), which at least is in POSIX 2008. But it's 100%
useless, except in some obscure cases: it doesn't set O_CLOEXEC, nor can
you pass it to it. Without O_CLOEXEC, we'd leak the temporary file to
all child processes. (The fact that the file, which is expected to reach
double or tripple digit GB sizes, will be deleted only once all
processes unreference the FD, makes this sort of a big deal. You could
ftruncate() it, but that doesn't fix all the other problems.)
Why did POSIX standardize mkstemp() and O_CLOEXEC apparently at the same
time, but provided no way to pass O_CLOEXEC to mkstemp()? With the
introduction of O_CLOEXEC, they acknowledged that there's a need to
atomically set the FD_CLOEXEC flag when creating file descriptors.
(FD_CLOEXEC was standard before that, but setting it with fcntl() is
racy.) You're much more likely to need a temp file that is CLOEXEC
rather than the opposite, and even if they were somehow opposed to
CLOEXEC by default (such as for compat. reasons), surely POSIX could
have standardized mkostemp() too or instead.
And then there's the fact that this whole O_CLOEXEC mess is stupid.
Surely there would have been a better way to handle this, instead of
requiring adding O_CLOEXEC to almost ALL instances of open() in all code
that has been written ever. The justification for this is that the
historic default was wrong, and you can't change it (e.g. this won't
work: changing the behavior of exec() and not inherit the FD to the
child process, unless a hypothetical O_KEEP_EXEC flag is set).
But on the other hand, surely you could have introduced an exec()
variant which does close all FDs, except a whitelist of FDs passed to
it. Let's call it execve2(). In fact, I'm going to argue that exec()
call sites are the most aware of whether (and which) FDs to inherit.
Some programs even tried to explicitly iterate over all opened FDs and
explicitly close "unwanted" FDs (which of course was problematic for
other reasons), and such an execve2() call would have been the ideal
solution.
Maybe this proposed solution would have had problems too. But surely
revisiting and reviewing every exec*() call would have been simpler than
reviewing every open() call. And more importantly, having to extend
every damn library function that either calls open() or creates FDs in
some other way, like mkstemp().
What argument are there going to be against this? That there will be
library code that can't keep working correctly with processes that use
the "old" exec? Well, what about all my legacy library code that uses
open() incorrectly, and that will break no matter what?
Well, I'm not going to claim that I can come up with better solutions
than POSIX (generally or in this case), but this situation is ABSOLUTELY
ATROCIOUS. It makes win32 programming look attractive compared to POSIX,
that standard pandering to dead people from the past. (Note: not trying
to insult dead people.)
I'm not sure what POSIX is even doing. Anything useful? Doesn't look
like it to me. Are they paid? Why? They didn't even fix the locale mess,
nor do they intend to. I bet they're proud of discussing compatibility
to 70ies code day in and day out iwtohut ever producing anything useful.
What a load of crap. They seriously got to do better than this.
Oh, and my wrapper is probably buggy. Fortunately that doesn't matter.
Also I'm dumping this into io.h. Originally, io.h was just supposed to
replace broken implementation of standard functions by MinGW (and then
by Android), but whatever, just give a dumping ground for shit code.
2019-06-13 16:23:05 +00:00
|
|
|
|
|
|
|
int mp_mkostemps(char *template, int suffixlen, int flags)
|
|
|
|
{
|
|
|
|
size_t len = strlen(template);
|
|
|
|
char *t = len >= 6 + suffixlen ? &template[len - (6 + suffixlen)] : NULL;
|
|
|
|
if (!t || strncmp(t, "XXXXXX", 6) != 0) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t fuckshit = 0; fuckshit < UINT32_MAX; fuckshit++) {
|
|
|
|
// Using a random value may make it require fewer iterations (even if
|
|
|
|
// not truly random; just a counter would be sufficient).
|
2022-08-15 01:28:54 +00:00
|
|
|
size_t fuckmess = mp_rand_next();
|
osdep: add mkostemps() emulation
Supposed to follow the standard function.
The standard function is not standard, but a GNU extension. Adding some
ifdef mess is pointless too - it has no advantages other than having a
mess, and not spotting implementation bugs in the emulation due to
running it only on "obscure" platforms (like Windows, so most computers
actually, except the developer's platform).
There is mkstemp(), which at least is in POSIX 2008. But it's 100%
useless, except in some obscure cases: it doesn't set O_CLOEXEC, nor can
you pass it to it. Without O_CLOEXEC, we'd leak the temporary file to
all child processes. (The fact that the file, which is expected to reach
double or tripple digit GB sizes, will be deleted only once all
processes unreference the FD, makes this sort of a big deal. You could
ftruncate() it, but that doesn't fix all the other problems.)
Why did POSIX standardize mkstemp() and O_CLOEXEC apparently at the same
time, but provided no way to pass O_CLOEXEC to mkstemp()? With the
introduction of O_CLOEXEC, they acknowledged that there's a need to
atomically set the FD_CLOEXEC flag when creating file descriptors.
(FD_CLOEXEC was standard before that, but setting it with fcntl() is
racy.) You're much more likely to need a temp file that is CLOEXEC
rather than the opposite, and even if they were somehow opposed to
CLOEXEC by default (such as for compat. reasons), surely POSIX could
have standardized mkostemp() too or instead.
And then there's the fact that this whole O_CLOEXEC mess is stupid.
Surely there would have been a better way to handle this, instead of
requiring adding O_CLOEXEC to almost ALL instances of open() in all code
that has been written ever. The justification for this is that the
historic default was wrong, and you can't change it (e.g. this won't
work: changing the behavior of exec() and not inherit the FD to the
child process, unless a hypothetical O_KEEP_EXEC flag is set).
But on the other hand, surely you could have introduced an exec()
variant which does close all FDs, except a whitelist of FDs passed to
it. Let's call it execve2(). In fact, I'm going to argue that exec()
call sites are the most aware of whether (and which) FDs to inherit.
Some programs even tried to explicitly iterate over all opened FDs and
explicitly close "unwanted" FDs (which of course was problematic for
other reasons), and such an execve2() call would have been the ideal
solution.
Maybe this proposed solution would have had problems too. But surely
revisiting and reviewing every exec*() call would have been simpler than
reviewing every open() call. And more importantly, having to extend
every damn library function that either calls open() or creates FDs in
some other way, like mkstemp().
What argument are there going to be against this? That there will be
library code that can't keep working correctly with processes that use
the "old" exec? Well, what about all my legacy library code that uses
open() incorrectly, and that will break no matter what?
Well, I'm not going to claim that I can come up with better solutions
than POSIX (generally or in this case), but this situation is ABSOLUTELY
ATROCIOUS. It makes win32 programming look attractive compared to POSIX,
that standard pandering to dead people from the past. (Note: not trying
to insult dead people.)
I'm not sure what POSIX is even doing. Anything useful? Doesn't look
like it to me. Are they paid? Why? They didn't even fix the locale mess,
nor do they intend to. I bet they're proud of discussing compatibility
to 70ies code day in and day out iwtohut ever producing anything useful.
What a load of crap. They seriously got to do better than this.
Oh, and my wrapper is probably buggy. Fortunately that doesn't matter.
Also I'm dumping this into io.h. Originally, io.h was just supposed to
replace broken implementation of standard functions by MinGW (and then
by Android), but whatever, just give a dumping ground for shit code.
2019-06-13 16:23:05 +00:00
|
|
|
char crap[7] = "";
|
|
|
|
snprintf(crap, sizeof(crap), "%06zx", fuckmess);
|
|
|
|
memcpy(t, crap, 6);
|
|
|
|
|
|
|
|
int res = open(template, O_RDWR | O_CREAT | O_EXCL | flags, 0600);
|
|
|
|
if (res >= 0 || errno != EEXIST)
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
errno = EEXIST;
|
|
|
|
return -1;
|
|
|
|
}
|