From e8edc42f9d078f7cde5b71e7780c0d6a1c99e96c Mon Sep 17 00:00:00 2001 From: Dave Davenport Date: Tue, 16 Oct 2018 21:01:45 +0200 Subject: [PATCH] [Helper] Quick and dirty string replacer with optional keys block. First (quick and dirty) version of a more advanced key replacer where sets of text are only outputted if the contained key matches. --- include/helper.h | 16 ++++++++ source/helper.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++ test/helper-test.c | 26 +++++++++++++ 3 files changed, 134 insertions(+) diff --git a/include/helper.h b/include/helper.h index 76657173..77718cfe 100644 --- a/include/helper.h +++ b/include/helper.h @@ -336,5 +336,21 @@ void parse_ranges ( char *input, rofi_range_pair **list, unsigned int *length ); * @param filter */ void rofi_output_formatted_line ( const char *format, const char *string, int selected_line, const char *filter ); + + +/** + * @param string The string with elements to be replaced + * @param ... Set of {key}, value that will be replaced, terminated by a NULL + * + * Items {key} are replaced by the value if '{key}' is passed as key/value pair, otherwise removed from string. + * If the {key} is in between [] all the text between [] are removed if {key} + * is not found. Otherwise key is replaced and [ & ] removed. + * + * This allows for optional replacement, f.e. '{ssh-client} [-t {title}] -e + * "{cmd}"' the '-t {title}' is only there if {title} is set. + * + * @returns a new string with the keys replaced. + */ +char *helper_string_replace_if_exists ( char * string, ... ); G_END_DECLS #endif // ROFI_HELPER_H diff --git a/source/helper.c b/source/helper.c index 0ca94e47..6722cf50 100644 --- a/source/helper.c +++ b/source/helper.c @@ -1233,3 +1233,95 @@ void rofi_output_formatted_line ( const char *format, const char *string, int se fputc ( '\n', stdout ); fflush ( stdout ); } + + +static gboolean helper_eval_cb2 ( const GMatchInfo *info, GString *res, gpointer data ) +{ + GHashTable *h = (GHashTable*)h; + gchar *match; + // Get the match + int num_match = g_match_info_get_match_count(info); + // Just {text} This is inside () 5. + if ( num_match == 5 ) { + match = g_match_info_fetch ( info, 4); + if ( match != NULL ) { + // Lookup the match, so we can replace it. + gchar *r = g_hash_table_lookup ( (GHashTable *) data, match ); + if ( r != NULL ) { + // Append the replacement to the string. + g_string_append ( res, r ); + } + // Free match. + g_free ( match ); + } + } + // {} with [] guard around it. + else if ( num_match == 4 ) { + match = g_match_info_fetch ( info, 2); + if ( match != NULL ) { + // Lookup the match, so we can replace it. + gchar *r = g_hash_table_lookup ( (GHashTable *) data, match ); + if ( r != NULL ) { + // Add (optional) prefix + gchar *prefix = g_match_info_fetch (info, 1); + g_string_append ( res, prefix ); + g_free (prefix ); + // Append the replacement to the string. + g_string_append ( res, r ); + // Add (optional) postfix + gchar *post = g_match_info_fetch (info, 3); + g_string_append ( res, post ); + g_free (post ); + } + // Free match. + g_free ( match ); + } + } + // Else we have an invalid match. + // Continue replacement. + return FALSE; +} + +char *helper_string_replace_if_exists ( char * string, ... ) +{ + GError *error = NULL; + GHashTable *h; + h = g_hash_table_new ( g_str_hash, g_str_equal ); + // By default, we insert terminal and ssh-client + g_hash_table_insert ( h, "{terminal}", config.terminal_emulator ); + g_hash_table_insert ( h, "{ssh-client}", config.ssh_client ); + // Add list from variable arguments. + va_list ap; + va_start ( ap, string ); + while ( 1 ) { + char * key = va_arg ( ap, char * ); + if ( key == (char *) 0 ) { + break; + } + char *value = va_arg ( ap, char * ); + if ( value == (char *) 0 ) { + break; + } + g_hash_table_insert ( h, key, value ); + } + va_end ( ap ); + + // Replace hits within {-\w+}. + GRegex *reg = g_regex_new ( "\\[(.*)({[-\\w]+})(.*)\\]|({[\\w-]+})", 0, 0, NULL ); + char *res = g_regex_replace_eval ( reg, string, -1, 0, 0, helper_eval_cb2, h, NULL ); + // Free regex. + g_regex_unref ( reg ); + // Destroy key-value storage. + g_hash_table_destroy ( h ); + // Throw error if shell parsing fails. + if ( error ) { + char *msg = g_strdup_printf ( "Failed to parse: '%s'\nError: '%s'", string, error->message ); + rofi_view_error_dialog ( msg, FALSE ); + g_free ( msg ); + // print error. + g_error_free ( error ); + g_free ( res ); + return NULL; + } + return res; +} diff --git a/test/helper-test.c b/test/helper-test.c index ddafde63..052af651 100644 --- a/test/helper-test.c +++ b/test/helper-test.c @@ -156,4 +156,30 @@ int main ( int argc, char ** argv ) TASSERTL ( rofi_scorer_fuzzy_evaluate ("aap noot mies", 12,"Anm", 3 ), 1073741824); } + + + char *a; + a = helper_string_replace_if_exists ( "{terminal} [-t {title} blub ]-e {cmd}", "{cmd}", "aap", "{title}", "some title", NULL); + printf("%s\n",a); + TASSERT ( g_utf8_collate ( a, "rofi-sensible-terminal -t some title blub -e aap") == 0); + g_free(a); + a = helper_string_replace_if_exists ( "{terminal} [-t {title} blub ]-e {cmd}", "{cmd}", "aap", NULL); + printf("%s\n",a); + TASSERT ( g_utf8_collate ( a, "rofi-sensible-terminal -e aap") == 0); + g_free(a); + a = helper_string_replace_if_exists ( "{name} [({category})]", "{name}", "Librecad", "{category}", "Desktop app", NULL ); + printf("%s\n",a); + TASSERT ( g_utf8_collate ( a, "Librecad (Desktop app)") == 0); + g_free(a); + a = helper_string_replace_if_exists ( "{name}[ ({category})]", "{name}", "Librecad", NULL ); + TASSERT ( g_utf8_collate ( a, "Librecad") == 0); + g_free(a); + a = helper_string_replace_if_exists ( "{terminal} [{title} blub ]-e {cmd}", "{cmd}", "aap", "{title}", "some title", NULL); + printf("%s\n",a); + TASSERT ( g_utf8_collate ( a, "rofi-sensible-terminal some title blub -e aap") == 0); + g_free(a); + a = helper_string_replace_if_exists ( "{terminal} [{title} blub ]-e {cmd}", "{cmd}", "aap", "{title}", NULL, NULL); + printf("%s\n",a); + TASSERT ( g_utf8_collate ( a, "rofi-sensible-terminal -e aap") == 0); + g_free(a); }