diff --git a/include/helper.h b/include/helper.h index 5f530dbf..a74a162e 100644 --- a/include/helper.h +++ b/include/helper.h @@ -221,4 +221,17 @@ PangoAttrList *token_match_get_pango_attr ( ThemeHighlight th, GRegex **tokens, */ int rofi_scorer_fuzzy_evaluate ( const char *pattern, glong plen, const char *str, glong slen ); /*@}*/ + +/** + * @param a UTF-8 string to compare + * @param b UTF-8 string to compare + * @param n Maximum number of characters to compare + * + * Compares the `G_NORMALIZE_ALL_COMPOSE` forms of the two strings. + * + * @returns less than, equal to, or greater than zero if the first `n` characters (not bytes) of `a` + * are found, respectively, to be less than, to match, or be greater than the first `n` + * characters (not bytes) of `b`. + */ +int utf8_strncmp ( const char *a, const char* b, size_t n ); #endif // ROFI_HELPER_H diff --git a/source/dialogs/combi.c b/source/dialogs/combi.c index a3f09284..f63c49bf 100644 --- a/source/dialogs/combi.c +++ b/source/dialogs/combi.c @@ -29,6 +29,7 @@ #include #include #include "settings.h" +#include "helper.h" #include @@ -151,18 +152,23 @@ static void combi_mode_destroy ( Mode *sw ) static ModeMode combi_mode_result ( Mode *sw, int mretv, char **input, unsigned int selected_line ) { CombiModePrivateData *pd = mode_get_private_data ( sw ); - if ( *input[0] == '!' ) { + + if ( input[0][0] == '!' ) { int switcher = -1; - for ( unsigned i = 0; switcher == -1 && i < pd->num_switchers; i++ ) { - if ( ( *input )[1] == mode_get_name ( pd->switchers[i] )[0] ) { - switcher = i; + char *eob = strchrnul ( input[0], ' ' ); + ssize_t bang_len = g_utf8_pointer_to_offset ( input[0], eob ) - 1; + if ( bang_len > 0 ) { + for ( unsigned i = 0; switcher == -1 && i < pd->num_switchers; i++ ) { + const char *mode_name = mode_get_name ( pd->switchers[i] ); + size_t mode_name_len = g_utf8_strlen ( mode_name, -1 ); + if ( (size_t) bang_len <= mode_name_len && utf8_strncmp ( &input[0][1], mode_name, bang_len ) == 0 ) { + switcher = i; + } } } - if ( switcher >= 0 ) { - char *n = strchr ( *input, ' ' ); - // skip whitespace - if ( n != NULL ) { - n++; + if ( switcher >= 0 ) { + if ( eob[0] == ' ' ) { + char *n = eob+1; return mode_result ( pd->switchers[switcher], mretv, &n, selected_line - pd->starts[switcher] ); } @@ -223,7 +229,7 @@ static char * combi_get_completion ( const Mode *sw, unsigned int index ) for ( unsigned i = 0; i < pd->num_switchers; i++ ) { if ( index >= pd->starts[i] && index < ( pd->starts[i] + pd->lengths[i] ) ) { char *comp = mode_get_completion ( pd->switchers[i], index - pd->starts[i] ); - char *mcomp = g_strdup_printf ( "!%c %s", mode_get_name ( pd->switchers[i] )[0], comp ); + char *mcomp = g_strdup_printf ( "!%s %s", mode_get_name ( pd->switchers[i] ), comp ); g_free ( comp ); return mcomp; } @@ -237,14 +243,20 @@ static char * combi_preprocess_input ( Mode *sw, const char *input ) { CombiModePrivateData *pd = mode_get_private_data ( sw ); pd->current = NULL; - if ( input != NULL && input[0] == '!' && strlen ( input ) > 1 ) { - for ( unsigned i = 0; i < pd->num_switchers; i++ ) { - if ( input[1] == mode_get_name ( pd->switchers[i] )[0] ) { - pd->current = pd->switchers[i]; - if ( input[2] == '\0' ) { - return NULL; + if ( input != NULL && input[0] == '!' ) { + char *eob = strchrnul ( input, ' ' ); + ssize_t bang_len = g_utf8_pointer_to_offset ( input, eob ) - 1; + if ( bang_len > 0 ) { + for ( unsigned i = 0; i < pd->num_switchers; i++ ) { + const char *mode_name = mode_get_name ( pd->switchers[i] ); + size_t mode_name_len = g_utf8_strlen ( mode_name, -1 ); + if ( (size_t) bang_len <= mode_name_len && utf8_strncmp ( &input[1], mode_name, bang_len ) == 0 ) { + pd->current = pd->switchers[i]; + if ( eob[0] == '\0' || eob[1] == '\0' ) { + return NULL; + } + return g_strdup ( eob+1 ); } - return g_strdup ( &input[2] ); } } } diff --git a/source/helper.c b/source/helper.c index ef4a8a49..6e3fc67a 100644 --- a/source/helper.c +++ b/source/helper.c @@ -889,3 +889,26 @@ int rofi_scorer_fuzzy_evaluate ( const char *pattern, glong plen, const char *st g_free ( dp ); return -lefts; } + +/** + * @param a UTF-8 string to compare + * @param b UTF-8 string to compare + * @param n Maximum number of characters to compare + * + * Compares the `G_NORMALIZE_ALL_COMPOSE` forms of the two strings. + * + * @returns less than, equal to, or greater than zero if the first `n` characters (not bytes) of `a` + * are found, respectively, to be less than, to match, or be greater than the first `n` + * characters (not bytes) of `b`. + */ +int utf8_strncmp ( const char* a, const char* b, size_t n ) +{ + char *na = g_utf8_normalize ( a, -1, G_NORMALIZE_ALL_COMPOSE ); + char *nb = g_utf8_normalize ( b, -1, G_NORMALIZE_ALL_COMPOSE ); + *g_utf8_offset_to_pointer ( na, n ) = '\0'; + *g_utf8_offset_to_pointer ( nb, n ) = '\0'; + int r = g_utf8_collate ( na, nb ); + g_free ( na ); + g_free ( nb ); + return r; +}