Skip to content

Commit

Permalink
Linux: Allow case-insensitive file filters and make that the default
Browse files Browse the repository at this point in the history
  • Loading branch information
btzy committed Mar 3, 2025
1 parent 77b392a commit 51a82ce
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 18 deletions.
65 changes: 61 additions & 4 deletions src/nfd_gtk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@

#include "nfd.h"

// Linux file extensions are case-sensitive, but perhaps in the vast majority of cases we actually
// want the filter list to be case-insensitive. Set NFD_LINUX_CASE_SENSITIVE_FILTER to 0 for
// case-insensitive filtering, or 1 for case-sensitive filtering. If
// NFD_LINUX_CASE_SENSITIVE_FILTER is undefined, then it will be set to 0.
#if !defined(NFD_LINUX_CASE_SENSITIVE_FILTER)
#define NFD_LINUX_CASE_SENSITIVE_FILTER 0
#endif

namespace {

template <typename T>
Expand Down Expand Up @@ -66,6 +74,27 @@ T* copy(const T* begin, const T* end, T* out) {
return out;
}

#if NFD_LINUX_CASE_SENSITIVE_FILTER == 0
nfdnchar_t* emit_case_insensitive_glob(const nfdnchar_t* begin,
const nfdnchar_t* end,
nfdnchar_t* out) {
// this code will only make regular Latin characters case-insensitive; other
// characters remain case sensitive
for (; begin != end; ++begin) {
if ((*begin >= 'A' && *begin <= 'Z') || (*begin >= 'a' && *begin <= 'z')) {
*out++ = '[';
*out++ = *begin;
// invert the case of the original character
*out++ = *begin ^ static_cast<nfdnchar_t>(0x20);
*out++ = ']';
} else {
*out++ = *begin;
}
}
return out;
}
#endif

// Does not own the filter and extension.
struct Pair_GtkFileFilter_FileExtension {
GtkFileFilter* filter;
Expand Down Expand Up @@ -122,6 +151,7 @@ void AddFiltersToDialog(GtkFileChooser* chooser,
*p_nameBuf++ = ' ';
}

#if NFD_LINUX_CASE_SENSITIVE_FILTER != 0
// +1 for the trailing '\0'
nfdnchar_t* extnBuf = NFDi_Malloc<nfdnchar_t>(sizeof(nfdnchar_t) *
(p_spec - p_extensionStart + 3));
Expand All @@ -130,10 +160,23 @@ void AddFiltersToDialog(GtkFileChooser* chooser,
*p_extnBufEnd++ = '.';
p_extnBufEnd = copy(p_extensionStart, p_spec, p_extnBufEnd);
*p_extnBufEnd++ = '\0';
assert((size_t)(p_extnBufEnd - extnBuf) ==
sizeof(nfdnchar_t) * (p_spec - p_extensionStart + 3));
gtk_file_filter_add_pattern(filter, extnBuf);
NFDi_Free(extnBuf);
#else
// Each character in the Latin alphabet is converted into 4 characters. E.g.
// 'a' is converted into "[Aa]". Other characters are preserved. Then we +1
// for the trailing '\0'.
nfdnchar_t* extnBuf = NFDi_Malloc<nfdnchar_t>(
sizeof(nfdnchar_t) * ((p_spec - p_extensionStart) * 4 + 3));
nfdnchar_t* p_extnBufEnd = extnBuf;
*p_extnBufEnd++ = '*';
*p_extnBufEnd++ = '.';
p_extnBufEnd =
emit_case_insensitive_glob(p_extensionStart, p_spec, p_extnBufEnd);
*p_extnBufEnd++ = '\0';
gtk_file_filter_add_pattern(filter, extnBuf);
NFDi_Free(extnBuf);
#endif

if (*p_spec) {
// update the extension start point
Expand Down Expand Up @@ -222,6 +265,7 @@ Pair_GtkFileFilter_FileExtension* AddFiltersToDialogWithMap(GtkFileChooser* choo
*p_nameBuf++ = ' ';
}

#if NFD_LINUX_CASE_SENSITIVE_FILTER != 0
// +1 for the trailing '\0'
nfdnchar_t* extnBuf = NFDi_Malloc<nfdnchar_t>(sizeof(nfdnchar_t) *
(p_spec - p_extensionStart + 3));
Expand All @@ -230,10 +274,23 @@ Pair_GtkFileFilter_FileExtension* AddFiltersToDialogWithMap(GtkFileChooser* choo
*p_extnBufEnd++ = '.';
p_extnBufEnd = copy(p_extensionStart, p_spec, p_extnBufEnd);
*p_extnBufEnd++ = '\0';
assert((size_t)(p_extnBufEnd - extnBuf) ==
sizeof(nfdnchar_t) * (p_spec - p_extensionStart + 3));
gtk_file_filter_add_pattern(filter, extnBuf);
NFDi_Free(extnBuf);
#else
// Each character in the Latin alphabet is converted into 4 characters. E.g.
// 'a' is converted into "[Aa]". Other characters are preserved. Then we +1
// for the trailing '\0'.
nfdnchar_t* extnBuf = NFDi_Malloc<nfdnchar_t>(
sizeof(nfdnchar_t) * ((p_spec - p_extensionStart) * 4 + 3));
nfdnchar_t* p_extnBufEnd = extnBuf;
*p_extnBufEnd++ = '*';
*p_extnBufEnd++ = '.';
p_extnBufEnd =
emit_case_insensitive_glob(p_extensionStart, p_spec, p_extnBufEnd);
*p_extnBufEnd++ = '\0';
gtk_file_filter_add_pattern(filter, extnBuf);
NFDi_Free(extnBuf);
#endif

// store current pointer in map (if it's
// the first one)
Expand Down
80 changes: 66 additions & 14 deletions src/nfd_portal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ know that we appended an extension, so they will not check or whitelist the corr
NFD_APPEND_EXTENSION is not recommended for portals.
*/

// Linux file extensions are case-sensitive, but perhaps in the vast majority of cases we actually
// want the filter list to be case-insensitive. Set NFD_LINUX_CASE_SENSITIVE_FILTER to 0 for
// case-insensitive filtering, or 1 for case-sensitive filtering. If
// NFD_LINUX_CASE_SENSITIVE_FILTER is undefined, then it will be set to 0.
#if !defined(NFD_LINUX_CASE_SENSITIVE_FILTER)
#define NFD_LINUX_CASE_SENSITIVE_FILTER 0
#endif
namespace {

template <typename T = void>
Expand Down Expand Up @@ -124,6 +131,27 @@ T* reverse_copy(const T* begin, const T* end, T* out) {
return out;
}

#if NFD_LINUX_CASE_SENSITIVE_FILTER == 0
nfdnchar_t* emit_case_insensitive_glob(const nfdnchar_t* begin,
const nfdnchar_t* end,
nfdnchar_t* out) {
// this code will only make regular Latin characters case-insensitive; other
// characters remain case sensitive
for (; begin != end; ++begin) {
if ((*begin >= 'A' && *begin <= 'Z') || (*begin >= 'a' && *begin <= 'z')) {
*out++ = '[';
*out++ = *begin;
// invert the case of the original character
*out++ = *begin ^ static_cast<nfdnchar_t>(0x20);
*out++ = ']';
} else {
*out++ = *begin;
}
}
return out;
}
#endif

// 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');
Expand Down Expand Up @@ -316,13 +344,25 @@ void AppendSingleFilter(DBusMessageIter& base_iter, const nfdnfilteritem_t& filt
do {
++extn_end;
} while (*extn_end != ',' && *extn_end != '\0');
char* buf = static_cast<char*>(alloca(2 + (extn_end - extn_begin) + 1));
char* buf_end = buf;
*buf_end++ = '*';
*buf_end++ = '.';
buf_end = copy(extn_begin, extn_end, buf_end);
*buf_end = '\0';
dbus_message_iter_append_basic(&filter_sublist_struct_iter, DBUS_TYPE_STRING, &buf);
{
#if NFD_LINUX_CASE_SENSITIVE_FILTER != 0
char* buf = static_cast<char*>(alloca(2 + (extn_end - extn_begin) + 1));
char* buf_end = buf;
*buf_end++ = '*';
*buf_end++ = '.';
buf_end = copy(extn_begin, extn_end, buf_end);
*buf_end = '\0';
dbus_message_iter_append_basic(&filter_sublist_struct_iter, DBUS_TYPE_STRING, &buf);
#else
char* buf = static_cast<char*>(alloca(2 + (extn_end - extn_begin) * 4 + 1));
char* buf_end = buf;
*buf_end++ = '*';
*buf_end++ = '.';
buf_end = emit_case_insensitive_glob(extn_begin, extn_end, buf_end);
*buf_end = '\0';
dbus_message_iter_append_basic(&filter_sublist_struct_iter, DBUS_TYPE_STRING, &buf);
#endif
}
dbus_message_iter_close_container(&filter_sublist_iter, &filter_sublist_struct_iter);
if (*extn_end == '\0') {
break;
Expand Down Expand Up @@ -385,13 +425,25 @@ bool AppendSingleFilterCheckExtn(DBusMessageIter& base_iter,
do {
++extn_end;
} while (*extn_end != ',' && *extn_end != '\0');
char* buf = static_cast<char*>(alloca(2 + (extn_end - extn_begin) + 1));
char* buf_end = buf;
*buf_end++ = '*';
*buf_end++ = '.';
buf_end = copy(extn_begin, extn_end, buf_end);
*buf_end = '\0';
dbus_message_iter_append_basic(&filter_sublist_struct_iter, DBUS_TYPE_STRING, &buf);
{
#if NFD_LINUX_CASE_SENSITIVE_FILTER != 0
char* buf = static_cast<char*>(alloca(2 + (extn_end - extn_begin) + 1));
char* buf_end = buf;
*buf_end++ = '*';
*buf_end++ = '.';
buf_end = copy(extn_begin, extn_end, buf_end);
*buf_end = '\0';
dbus_message_iter_append_basic(&filter_sublist_struct_iter, DBUS_TYPE_STRING, &buf);
#else
char* buf = static_cast<char*>(alloca(2 + (extn_end - extn_begin) * 4 + 1));
char* buf_end = buf;
*buf_end++ = '*';
*buf_end++ = '.';
buf_end = emit_case_insensitive_glob(extn_begin, extn_end, buf_end);
*buf_end = '\0';
dbus_message_iter_append_basic(&filter_sublist_struct_iter, DBUS_TYPE_STRING, &buf);
#endif
}
dbus_message_iter_close_container(&filter_sublist_iter, &filter_sublist_struct_iter);
if (!extn_matched) {
const char* match_extn_p;
Expand Down

0 comments on commit 51a82ce

Please sign in to comment.