mirror of
https://github.com/wolfpld/tracy.git
synced 2024-11-25 23:44:35 +00:00
Update nativefiledialog-extended to 3311592.
This commit is contained in:
parent
85102f7a19
commit
d6a607b9f0
@ -9,17 +9,23 @@
|
||||
#include <Availability.h>
|
||||
#include "nfd.h"
|
||||
|
||||
// At least one of NFD_NEEDS_ALLOWEDCONTENTTYPES and NFD_NEEDS_ALLOWEDFILETYPES will be defined
|
||||
#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && defined(__MAC_12_0) && \
|
||||
__MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_12_0
|
||||
#include <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
|
||||
#define NFD_NEEDS_ALLOWEDCONTENTTYPES
|
||||
#if !defined(__MAC_OS_X_VERSION_MIN_ALLOWED) || !defined(__MAC_12_0) || \
|
||||
__MAC_OS_X_VERSION_MIN_ALLOWED < __MAC_12_0
|
||||
#define NFD_NEEDS_ALLOWEDFILETYPES
|
||||
#endif
|
||||
// MacOS is deprecating the allowedFileTypes property in favour of allowedContentTypes, so we have
|
||||
// to introduce this breaking change. Define NFD_MACOS_ALLOWEDCONTENTTYPES to 1 to have it set the
|
||||
// allowedContentTypes property of the SavePanel or OpenPanel. Define
|
||||
// NFD_MACOS_ALLOWEDCONTENTTYPES to 0 to have it set the allowedFileTypes property of the SavePanel
|
||||
// or OpenPanel. If NFD_MACOS_ALLOWEDCONTENTTYPES is undefined, then it will set it to 1 if
|
||||
// __MAC_OS_X_VERSION_MIN_REQUIRED >= 11.0, and 0 otherwise.
|
||||
#if !defined(NFD_MACOS_ALLOWEDCONTENTTYPES)
|
||||
#if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || !defined(__MAC_11_0) || \
|
||||
__MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_11_0
|
||||
#define NFD_MACOS_ALLOWEDCONTENTTYPES 0
|
||||
#else
|
||||
#define NFD_NEEDS_ALLOWEDFILETYPES
|
||||
#define NFD_MACOS_ALLOWEDCONTENTTYPES 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if NFD_MACOS_ALLOWEDCONTENTTYPES == 1
|
||||
#include <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
|
||||
#endif
|
||||
|
||||
static const char* g_errorstr = NULL;
|
||||
@ -40,7 +46,7 @@ static void NFDi_Free(void* ptr) {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
#if defined(NFD_NEEDS_ALLOWEDCONTENTTYPES)
|
||||
#if NFD_MACOS_ALLOWEDCONTENTTYPES == 1
|
||||
// Returns an NSArray of UTType representing the content types.
|
||||
static NSArray* BuildAllowedContentTypes(const nfdnfilteritem_t* filterList,
|
||||
nfdfiltersize_t filterCount) {
|
||||
@ -81,9 +87,7 @@ static NSArray* BuildAllowedContentTypes(const nfdnfilteritem_t* filterList,
|
||||
|
||||
return returnArray;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(NFD_NEEDS_ALLOWEDFILETYPES)
|
||||
#else
|
||||
// Returns an NSArray of NSString representing the file types.
|
||||
static NSArray* BuildAllowedFileTypes(const nfdnfilteritem_t* filterList,
|
||||
nfdfiltersize_t filterCount) {
|
||||
@ -130,21 +134,8 @@ static void AddFilterListToDialog(NSSavePanel* dialog,
|
||||
assert(filterList);
|
||||
|
||||
// Make NSArray of file types and set it on the dialog
|
||||
// We use setAllowedFileTypes or setAllowedContentTypes depending on the OS version
|
||||
#if defined(NFD_NEEDS_ALLOWEDCONTENTTYPES) && defined(NFD_NEEDS_ALLOWEDFILETYPES)
|
||||
// If both are needed, it means we have to do a runtime check
|
||||
if (@available(macOS 12.0, *)) {
|
||||
NSArray* allowedContentTypes = BuildAllowedContentTypes(filterList, filterCount);
|
||||
[dialog setAllowedContentTypes:allowedContentTypes];
|
||||
} else {
|
||||
NSArray* allowedFileTypes = BuildAllowedFileTypes(filterList, filterCount);
|
||||
// Unfortunately @available doesn't silence deprecation warnings so we need these pragmas
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[dialog setAllowedFileTypes:allowedFileTypes];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
#elif defined(NFD_NEEDS_ALLOWEDCONTENTTYPES)
|
||||
// We use setAllowedFileTypes or setAllowedContentTypes depending on the deployment target
|
||||
#if NFD_MACOS_ALLOWEDCONTENTTYPES == 1
|
||||
NSArray* allowedContentTypes = BuildAllowedContentTypes(filterList, filterCount);
|
||||
[dialog setAllowedContentTypes:allowedContentTypes];
|
||||
#else
|
||||
|
@ -20,13 +20,12 @@
|
||||
#include "nfd.h"
|
||||
|
||||
/*
|
||||
Define NFD_PORTAL_AUTO_APPEND_FILE_EXTENSION to 0 if you don't want the file extension to be
|
||||
appended when missing. Linux programs usually doesn't append the file extension, but for consistency
|
||||
with other OSes we append it by default.
|
||||
Define NFD_APPEND_EXTENSION if you want the file extension to be appended when missing. Linux
|
||||
programs usually don't append the file extension, but for consistency with other OSes you might want
|
||||
to append it. However, when using portals, the file overwrite prompt and the Flatpak sandbox won't
|
||||
know that we appended an extension, so they will not check or whitelist the correct file. Enabling
|
||||
NFD_APPEND_EXTENSION is not recommended for portals.
|
||||
*/
|
||||
#ifndef NFD_PORTAL_AUTO_APPEND_FILE_EXTENSION
|
||||
#define NFD_PORTAL_AUTO_APPEND_FILE_EXTENSION 1
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
@ -720,14 +719,14 @@ nfdresult_t ReadResponseUrisSingle(DBusMessage* msg, const char*& file) {
|
||||
const nfdresult_t res = ReadResponseUris(msg, uri_iter);
|
||||
if (res != NFD_OKAY) return res; // can be NFD_CANCEL or NFD_ERROR
|
||||
if (dbus_message_iter_get_arg_type(&uri_iter) != DBUS_TYPE_STRING) {
|
||||
NFDi_SetError("D-Bus response signal URI sub iter is not an string.");
|
||||
NFDi_SetError("D-Bus response signal URI sub iter is not a string.");
|
||||
return NFD_ERROR;
|
||||
}
|
||||
dbus_message_iter_get_basic(&uri_iter, &file);
|
||||
return NFD_OKAY;
|
||||
}
|
||||
|
||||
#if NFD_PORTAL_AUTO_APPEND_FILE_EXTENSION == 1
|
||||
#ifdef NFD_APPEND_EXTENSION
|
||||
// Read the response URI and selected extension (in the form "*.abc" or "*") (if any). If response
|
||||
// was okay, then returns NFD_OKAY and set file and extn to them (the pointer is set to some string
|
||||
// owned by msg, so you should not manually free it). `file` is the user-entered file name, and
|
||||
@ -775,7 +774,7 @@ nfdresult_t ReadResponseUrisSingleAndCurrentExtension(DBusMessage* msg,
|
||||
}
|
||||
if (dbus_message_iter_get_arg_type(¤t_filter_struct_iter) !=
|
||||
DBUS_TYPE_ARRAY) {
|
||||
// NFDi_SetError("D-Bus response signal URI sub iter is not an string.");
|
||||
// NFDi_SetError("D-Bus response signal URI sub iter is not a string.");
|
||||
return NFD_OKAY;
|
||||
}
|
||||
DBusMessageIter current_filter_array_iter;
|
||||
@ -788,7 +787,7 @@ nfdresult_t ReadResponseUrisSingleAndCurrentExtension(DBusMessage* msg,
|
||||
DBusMessageIter current_filter_extn_iter;
|
||||
dbus_message_iter_recurse(¤t_filter_array_iter, ¤t_filter_extn_iter);
|
||||
if (dbus_message_iter_get_arg_type(¤t_filter_extn_iter) != DBUS_TYPE_UINT32) {
|
||||
// NFDi_SetError("D-Bus response signal URI sub iter is not an string.");
|
||||
// NFDi_SetError("D-Bus response signal URI sub iter is not a string.");
|
||||
return NFD_OKAY;
|
||||
}
|
||||
dbus_uint32_t type;
|
||||
@ -803,7 +802,7 @@ nfdresult_t ReadResponseUrisSingleAndCurrentExtension(DBusMessage* msg,
|
||||
return NFD_OKAY;
|
||||
}
|
||||
if (dbus_message_iter_get_arg_type(¤t_filter_extn_iter) != DBUS_TYPE_STRING) {
|
||||
// NFDi_SetError("D-Bus response signal URI sub iter is not an string.");
|
||||
// NFDi_SetError("D-Bus response signal URI sub iter is not a string.");
|
||||
return NFD_OKAY;
|
||||
}
|
||||
dbus_message_iter_get_basic(¤t_filter_extn_iter, &tmp_extn);
|
||||
@ -942,12 +941,69 @@ class DBusSignalSubscriptionHandler {
|
||||
}
|
||||
};
|
||||
|
||||
// Returns true if ch is in [0-9A-Za-z], false otherwise.
|
||||
bool IsHex(char ch) {
|
||||
return ('0' <= ch && ch <= '9') || ('A' <= ch && ch <= 'F') || ('a' <= ch && ch <= 'f');
|
||||
}
|
||||
|
||||
// Returns the hexadecimal value contained in the char. Precondition: IsHex(ch)
|
||||
char ParseHexUnchecked(char ch) {
|
||||
if ('0' <= ch && ch <= '9') return ch - '0';
|
||||
if ('A' <= ch && ch <= 'F') return ch - ('A' - 10);
|
||||
if ('a' <= ch && ch <= 'f') return ch - ('a' - 10);
|
||||
#if defined(__GNUC__)
|
||||
__builtin_unreachable();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns true if the given file URI is decodable (i.e. not malformed), and false otherwise.
|
||||
// If this function returns true, then `out` will be populated with the length of the decoded URI
|
||||
// and `fileUriEnd` will point to the trailing null byte of `fileUri`. Otherwise, `out` and
|
||||
// `fileUriEnd` will be unmodified.
|
||||
bool TryUriDecodeLen(const char* fileUri, size_t& out, const char*& fileUriEnd) {
|
||||
size_t len = 0;
|
||||
while (*fileUri) {
|
||||
if (*fileUri != '%') {
|
||||
++fileUri;
|
||||
} else {
|
||||
if (*(fileUri + 1) == '\0' || *(fileUri + 2) == '\0') {
|
||||
return false;
|
||||
}
|
||||
if (!IsHex(*(fileUri + 1)) || !IsHex(*(fileUri + 2))) {
|
||||
return false;
|
||||
}
|
||||
fileUri += 3;
|
||||
}
|
||||
++len;
|
||||
}
|
||||
out = len;
|
||||
fileUriEnd = fileUri;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Decodes the given URI and writes it to `outPath`. The caller must ensure that the given URI is
|
||||
// not malformed (typically with a prior call to `TryUriDecodeLen`). This function does not write
|
||||
// any trailing null character.
|
||||
char* UriDecodeUnchecked(const char* fileUri, const char* fileUriEnd, char* outPath) {
|
||||
while (fileUri != fileUriEnd) {
|
||||
if (*fileUri != '%') {
|
||||
*outPath++ = *fileUri++;
|
||||
} else {
|
||||
++fileUri;
|
||||
const char high_nibble = ParseHexUnchecked(*fileUri++);
|
||||
const char low_nibble = ParseHexUnchecked(*fileUri++);
|
||||
*outPath++ = (high_nibble << 4) | low_nibble;
|
||||
}
|
||||
}
|
||||
return outPath;
|
||||
}
|
||||
|
||||
constexpr const char FILE_URI_PREFIX[] = "file://";
|
||||
constexpr size_t FILE_URI_PREFIX_LEN = sizeof(FILE_URI_PREFIX) - 1;
|
||||
|
||||
// If fileUri starts with "file://", strips that prefix and copies it to a new buffer, and make
|
||||
// outPath point to it, and returns NFD_OKAY. Otherwise, does not modify outPath and returns
|
||||
// NFD_ERROR (with the correct error set)
|
||||
// If fileUri starts with "file://", strips that prefix and URI-decodes the remaining part to a new
|
||||
// buffer, and make outPath point to it, and returns NFD_OKAY. Otherwise, does not modify outPath
|
||||
// and returns NFD_ERROR (with the correct error set)
|
||||
nfdresult_t AllocAndCopyFilePath(const char* fileUri, char*& outPath) {
|
||||
const char* prefix_begin = FILE_URI_PREFIX;
|
||||
const char* const prefix_end = FILE_URI_PREFIX + FILE_URI_PREFIX_LEN;
|
||||
@ -957,14 +1013,20 @@ nfdresult_t AllocAndCopyFilePath(const char* fileUri, char*& outPath) {
|
||||
return NFD_ERROR;
|
||||
}
|
||||
}
|
||||
size_t len = strlen(fileUri);
|
||||
char* path_without_prefix = NFDi_Malloc<char>(len + 1);
|
||||
copy(fileUri, fileUri + (len + 1), path_without_prefix);
|
||||
size_t decoded_len;
|
||||
const char* file_uri_end;
|
||||
if (!TryUriDecodeLen(fileUri, decoded_len, file_uri_end)) {
|
||||
NFDi_SetError("D-Bus freedesktop portal returned a malformed URI.");
|
||||
return NFD_ERROR;
|
||||
}
|
||||
char* const path_without_prefix = NFDi_Malloc<char>(decoded_len + 1);
|
||||
char* const out_end = UriDecodeUnchecked(fileUri, file_uri_end, path_without_prefix);
|
||||
*out_end = '\0';
|
||||
outPath = path_without_prefix;
|
||||
return NFD_OKAY;
|
||||
}
|
||||
|
||||
#if NFD_PORTAL_AUTO_APPEND_FILE_EXTENSION == 1
|
||||
#ifdef NFD_APPEND_EXTENSION
|
||||
bool TryGetValidExtension(const char* extn,
|
||||
const char*& trimmed_extn,
|
||||
const char*& trimmed_extn_end) {
|
||||
@ -994,10 +1056,18 @@ nfdresult_t AllocAndCopyFilePathWithExtn(const char* fileUri, const char* extn,
|
||||
}
|
||||
}
|
||||
|
||||
const char* file_end = fileUri;
|
||||
for (; *file_end != '\0'; ++file_end)
|
||||
;
|
||||
const char* file_it = file_end;
|
||||
size_t decoded_len;
|
||||
const char* file_uri_end;
|
||||
if (!TryUriDecodeLen(fileUri, decoded_len, file_uri_end)) {
|
||||
NFDi_SetError("D-Bus freedesktop portal returned a malformed URI.");
|
||||
return NFD_ERROR;
|
||||
}
|
||||
|
||||
const char* file_it = file_uri_end;
|
||||
// The following loop condition is safe because `FILE_URI_PREFIX` ends with '/',
|
||||
// so we won't iterate past the beginning of the URI.
|
||||
// Also in UTF-8 all non-ASCII code points are encoded using bytes 128-255 so every '.' or '/'
|
||||
// is also '.' or '/' in UTF-8.
|
||||
do {
|
||||
--file_it;
|
||||
} while (*file_it != '/' && *file_it != '.');
|
||||
@ -1005,16 +1075,17 @@ nfdresult_t AllocAndCopyFilePathWithExtn(const char* fileUri, const char* extn,
|
||||
const char* trimmed_extn_end; // includes the '\0'
|
||||
if (*file_it == '.' || !TryGetValidExtension(extn, trimmed_extn, trimmed_extn_end)) {
|
||||
// has file extension already or no valid extension in `extn`
|
||||
++file_end; // includes the '\0'
|
||||
char* path_without_prefix = NFDi_Malloc<char>(file_end - fileUri);
|
||||
copy(fileUri, file_end, path_without_prefix);
|
||||
char* const path_without_prefix = NFDi_Malloc<char>(decoded_len + 1);
|
||||
char* const out_end = UriDecodeUnchecked(fileUri, file_uri_end, path_without_prefix);
|
||||
*out_end = '\0';
|
||||
outPath = path_without_prefix;
|
||||
} else {
|
||||
// no file extension and we have a valid extension
|
||||
char* path_without_prefix =
|
||||
NFDi_Malloc<char>((file_end - fileUri) + (trimmed_extn_end - trimmed_extn));
|
||||
char* out = copy(fileUri, file_end, path_without_prefix);
|
||||
copy(trimmed_extn, trimmed_extn_end, out);
|
||||
char* const path_without_prefix =
|
||||
NFDi_Malloc<char>(decoded_len + (trimmed_extn_end - trimmed_extn));
|
||||
char* const out_mid = UriDecodeUnchecked(fileUri, file_uri_end, path_without_prefix);
|
||||
char* const out_end = copy(trimmed_extn, trimmed_extn_end, out_mid);
|
||||
*out_end = '\0';
|
||||
outPath = path_without_prefix;
|
||||
}
|
||||
return NFD_OKAY;
|
||||
@ -1244,15 +1315,15 @@ nfdresult_t NFD_OpenDialogN(nfdnchar_t** outPath,
|
||||
}
|
||||
DBusMessage_Guard msg_guard(msg);
|
||||
|
||||
const char* file;
|
||||
const char* uri;
|
||||
{
|
||||
const nfdresult_t res = ReadResponseUrisSingle(msg, file);
|
||||
const nfdresult_t res = ReadResponseUrisSingle(msg, uri);
|
||||
if (res != NFD_OKAY) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return AllocAndCopyFilePath(file, *outPath);
|
||||
return AllocAndCopyFilePath(uri, *outPath);
|
||||
}
|
||||
|
||||
nfdresult_t NFD_OpenDialogMultipleN(const nfdpathset_t** outPaths,
|
||||
@ -1295,27 +1366,27 @@ nfdresult_t NFD_SaveDialogN(nfdnchar_t** outPath,
|
||||
}
|
||||
DBusMessage_Guard msg_guard(msg);
|
||||
|
||||
#if NFD_PORTAL_AUTO_APPEND_FILE_EXTENSION == 1
|
||||
const char* file;
|
||||
#ifdef NFD_APPEND_EXTENSION
|
||||
const char* uri;
|
||||
const char* extn;
|
||||
{
|
||||
const nfdresult_t res = ReadResponseUrisSingleAndCurrentExtension(msg, file, extn);
|
||||
const nfdresult_t res = ReadResponseUrisSingleAndCurrentExtension(msg, uri, extn);
|
||||
if (res != NFD_OKAY) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return AllocAndCopyFilePathWithExtn(file, extn, *outPath);
|
||||
return AllocAndCopyFilePathWithExtn(uri, extn, *outPath);
|
||||
#else
|
||||
const char* file;
|
||||
const char* uri;
|
||||
{
|
||||
const nfdresult_t res = ReadResponseUrisSingle(msg, file);
|
||||
const nfdresult_t res = ReadResponseUrisSingle(msg, uri);
|
||||
if (res != NFD_OKAY) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return AllocAndCopyFilePath(file, *outPath);
|
||||
return AllocAndCopyFilePath(uri, *outPath);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1331,15 +1402,15 @@ nfdresult_t NFD_PickFolderN(nfdnchar_t** outPath, const nfdnchar_t* defaultPath)
|
||||
}
|
||||
DBusMessage_Guard msg_guard(msg);
|
||||
|
||||
const char* file;
|
||||
const char* uri;
|
||||
{
|
||||
const nfdresult_t res = ReadResponseUrisSingle(msg, file);
|
||||
const nfdresult_t res = ReadResponseUrisSingle(msg, uri);
|
||||
if (res != NFD_OKAY) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return AllocAndCopyFilePath(file, *outPath);
|
||||
return AllocAndCopyFilePath(uri, *outPath);
|
||||
}
|
||||
|
||||
nfdresult_t NFD_PathSet_GetCount(const nfdpathset_t* pathSet, nfdpathsetsize_t* count) {
|
||||
@ -1364,12 +1435,12 @@ nfdresult_t NFD_PathSet_GetPathN(const nfdpathset_t* pathSet,
|
||||
}
|
||||
}
|
||||
if (dbus_message_iter_get_arg_type(&uri_iter) != DBUS_TYPE_STRING) {
|
||||
NFDi_SetError("D-Bus response signal URI sub iter is not an string.");
|
||||
NFDi_SetError("D-Bus response signal URI sub iter is not a string.");
|
||||
return NFD_ERROR;
|
||||
}
|
||||
const char* file;
|
||||
dbus_message_iter_get_basic(&uri_iter, &file);
|
||||
return AllocAndCopyFilePath(file, *outPath);
|
||||
const char* uri;
|
||||
dbus_message_iter_get_basic(&uri_iter, &uri);
|
||||
return AllocAndCopyFilePath(uri, *outPath);
|
||||
}
|
||||
|
||||
void NFD_PathSet_FreePathN(const nfdnchar_t* filePath) {
|
||||
@ -1402,12 +1473,12 @@ nfdresult_t NFD_PathSet_EnumNextN(nfdpathsetenum_t* enumerator, nfdnchar_t** out
|
||||
return NFD_OKAY;
|
||||
}
|
||||
if (arg_type != DBUS_TYPE_STRING) {
|
||||
NFDi_SetError("D-Bus response signal URI sub iter is not an string.");
|
||||
NFDi_SetError("D-Bus response signal URI sub iter is not a string.");
|
||||
return NFD_ERROR;
|
||||
}
|
||||
const char* file;
|
||||
dbus_message_iter_get_basic(&uri_iter, &file);
|
||||
const nfdresult_t res = AllocAndCopyFilePath(file, *outPath);
|
||||
const char* uri;
|
||||
dbus_message_iter_get_basic(&uri_iter, &uri);
|
||||
const nfdresult_t res = AllocAndCopyFilePath(uri, *outPath);
|
||||
if (res != NFD_OKAY) return res;
|
||||
dbus_message_iter_next(&uri_iter);
|
||||
return NFD_OKAY;
|
||||
|
Loading…
Reference in New Issue
Block a user