mirror of
https://gitlab.gnome.org/GNOME/nautilus
synced 2024-08-24 18:25:50 +00:00
![Ernestas Kulik](/assets/img/avatar_default.png)
f42979f0b0
changed the ellipsis to a unicode one, which is one character in length, in turn changing the expected output. For cases where the truncation length is zero, the expected output is changed to NULL, as536505728e
added an assertion.
536 lines
16 KiB
C
536 lines
16 KiB
C
/*
|
|
* eel-string.c: String routines to augment <string.h>.
|
|
*
|
|
* Copyright (C) 2000 Eazel, Inc.
|
|
*
|
|
* The Gnome Library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* The Gnome Library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with the Gnome Library; see the file COPYING.LIB. If not,
|
|
* see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Authors: Darin Adler <darin@eazel.com>
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include "eel-string.h"
|
|
|
|
#include <errno.h>
|
|
#include <locale.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <eel-glib-extensions.h>
|
|
|
|
#if !defined (EEL_OMIT_SELF_CHECK)
|
|
#include "eel-lib-self-check-functions.h"
|
|
#endif
|
|
|
|
char *
|
|
eel_str_double_underscores (const char *string)
|
|
{
|
|
int underscores;
|
|
const char *p;
|
|
char *q;
|
|
char *escaped;
|
|
|
|
if (string == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
underscores = 0;
|
|
for (p = string; *p != '\0'; p++)
|
|
{
|
|
underscores += (*p == '_');
|
|
}
|
|
|
|
if (underscores == 0)
|
|
{
|
|
return g_strdup (string);
|
|
}
|
|
|
|
escaped = g_new (char, strlen (string) + underscores + 1);
|
|
for (p = string, q = escaped; *p != '\0'; p++, q++)
|
|
{
|
|
/* Add an extra underscore. */
|
|
if (*p == '_')
|
|
{
|
|
*q++ = '_';
|
|
}
|
|
*q = *p;
|
|
}
|
|
*q = '\0';
|
|
|
|
return escaped;
|
|
}
|
|
|
|
char *
|
|
eel_str_capitalize (const char *string)
|
|
{
|
|
char *capitalized;
|
|
|
|
if (string == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
capitalized = g_strdup (string);
|
|
|
|
capitalized[0] = g_ascii_toupper (capitalized[0]);
|
|
|
|
return capitalized;
|
|
}
|
|
|
|
gchar *
|
|
eel_str_middle_truncate (const gchar *string,
|
|
guint truncate_length)
|
|
{
|
|
const gchar ellipsis[] = "…";
|
|
glong ellipsis_length;
|
|
glong length;
|
|
glong num_left_chars;
|
|
glong num_right_chars;
|
|
g_autofree gchar *left_substring = NULL;
|
|
g_autofree gchar *right_substring = NULL;
|
|
|
|
g_return_val_if_fail (string != NULL, NULL);
|
|
g_return_val_if_fail (truncate_length > 0, NULL);
|
|
|
|
ellipsis_length = g_utf8_strlen (ellipsis, -1);
|
|
|
|
/* Our ellipsis string + one character on each side. */
|
|
if (truncate_length < ellipsis_length + 2)
|
|
{
|
|
return g_strdup (string);
|
|
}
|
|
|
|
length = g_utf8_strlen (string, -1);
|
|
|
|
if (length <= truncate_length)
|
|
{
|
|
return g_strdup (string);
|
|
}
|
|
|
|
num_left_chars = (truncate_length - ellipsis_length) / 2;
|
|
num_right_chars = truncate_length - num_left_chars - ellipsis_length;
|
|
|
|
g_assert (num_left_chars > 0);
|
|
g_assert (num_right_chars > 0);
|
|
|
|
left_substring = g_utf8_substring (string, 0, num_left_chars);
|
|
right_substring = g_utf8_substring (string, length - num_right_chars, length);
|
|
|
|
return g_strconcat (left_substring, ellipsis, right_substring, NULL);
|
|
}
|
|
|
|
char *
|
|
eel_str_strip_substring_and_after (const char *string,
|
|
const char *substring)
|
|
{
|
|
const char *substring_position;
|
|
|
|
g_return_val_if_fail (substring != NULL, g_strdup (string));
|
|
g_return_val_if_fail (substring[0] != '\0', g_strdup (string));
|
|
|
|
if (string == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
substring_position = strstr (string, substring);
|
|
if (substring_position == NULL)
|
|
{
|
|
return g_strdup (string);
|
|
}
|
|
|
|
return g_strndup (string,
|
|
substring_position - string);
|
|
}
|
|
|
|
char *
|
|
eel_str_replace_substring (const char *string,
|
|
const char *substring,
|
|
const char *replacement)
|
|
{
|
|
int substring_length, replacement_length, result_length, remaining_length;
|
|
const char *p, *substring_position;
|
|
char *result, *result_position;
|
|
|
|
g_return_val_if_fail (substring != NULL, g_strdup (string));
|
|
g_return_val_if_fail (substring[0] != '\0', g_strdup (string));
|
|
|
|
if (string == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
substring_length = substring ? strlen (substring) : 0;
|
|
replacement_length = replacement ? strlen (replacement) : 0;
|
|
|
|
result_length = strlen (string);
|
|
for (p = string;; p = substring_position + substring_length)
|
|
{
|
|
substring_position = strstr (p, substring);
|
|
if (substring_position == NULL)
|
|
{
|
|
break;
|
|
}
|
|
result_length += replacement_length - substring_length;
|
|
}
|
|
|
|
result = g_malloc (result_length + 1);
|
|
|
|
result_position = result;
|
|
for (p = string;; p = substring_position + substring_length)
|
|
{
|
|
substring_position = strstr (p, substring);
|
|
if (substring_position == NULL)
|
|
{
|
|
remaining_length = strlen (p);
|
|
memcpy (result_position, p, remaining_length);
|
|
result_position += remaining_length;
|
|
break;
|
|
}
|
|
memcpy (result_position, p, substring_position - p);
|
|
result_position += substring_position - p;
|
|
memcpy (result_position, replacement, replacement_length);
|
|
result_position += replacement_length;
|
|
}
|
|
g_assert (result_position - result == result_length);
|
|
result_position[0] = '\0';
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* get_common_prefix_length:
|
|
* @str_a: first string
|
|
* @str_b: second string
|
|
* @min_required_len: the minimum number of characters required in the prefix
|
|
*
|
|
* Returns: the size of the common prefix of two strings, in characters.
|
|
* If there's no common prefix, or the common prefix is smaller than
|
|
* min_required_len, this will return -1
|
|
*/
|
|
static int
|
|
get_common_prefix_length (char *str_a,
|
|
char *str_b,
|
|
int min_required_len)
|
|
{
|
|
int a_len;
|
|
int b_len;
|
|
int intersection_len;
|
|
int matching_chars;
|
|
char *a;
|
|
char *b;
|
|
|
|
a_len = g_utf8_strlen (str_a, -1);
|
|
b_len = g_utf8_strlen (str_b, -1);
|
|
|
|
intersection_len = MIN (a_len, b_len);
|
|
if (intersection_len < min_required_len)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
matching_chars = 0;
|
|
a = str_a;
|
|
b = str_b;
|
|
while (matching_chars < intersection_len)
|
|
{
|
|
if (g_utf8_get_char (a) != g_utf8_get_char (b))
|
|
{
|
|
break;
|
|
}
|
|
|
|
++matching_chars;
|
|
|
|
a = g_utf8_next_char (a);
|
|
b = g_utf8_next_char (b);
|
|
}
|
|
|
|
if (matching_chars < min_required_len)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return matching_chars;
|
|
}
|
|
|
|
char *
|
|
eel_str_get_common_prefix (GList *strs,
|
|
int min_required_len)
|
|
{
|
|
GList *l;
|
|
char *common_part;
|
|
char *name;
|
|
char *truncated;
|
|
int matching_chars;
|
|
|
|
if (strs == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
common_part = NULL;
|
|
for (l = strs; l != NULL; l = l->next)
|
|
{
|
|
name = l->data;
|
|
if (name == NULL)
|
|
{
|
|
g_free (common_part);
|
|
return NULL;
|
|
}
|
|
|
|
if (l->prev == NULL)
|
|
{
|
|
common_part = g_strdup (name);
|
|
continue;
|
|
}
|
|
|
|
matching_chars = get_common_prefix_length (common_part, name, min_required_len);
|
|
|
|
if (matching_chars == -1)
|
|
{
|
|
g_free (common_part);
|
|
return NULL;
|
|
}
|
|
|
|
truncated = g_utf8_substring (common_part, 0, matching_chars);
|
|
g_free (common_part);
|
|
common_part = truncated;
|
|
}
|
|
|
|
matching_chars = g_utf8_strlen (common_part, -1);
|
|
if (matching_chars < min_required_len)
|
|
{
|
|
g_free (common_part);
|
|
return NULL;
|
|
}
|
|
|
|
return common_part;
|
|
}
|
|
|
|
/**************** Custom printf ***********/
|
|
|
|
typedef struct
|
|
{
|
|
const char *start;
|
|
const char *end;
|
|
GString *format;
|
|
int arg_pos;
|
|
int width_pos;
|
|
int width_format_index;
|
|
int precision_pos;
|
|
int precision_format_index;
|
|
} ConversionInfo;
|
|
|
|
enum
|
|
{
|
|
ARG_TYPE_INVALID,
|
|
ARG_TYPE_INT,
|
|
ARG_TYPE_LONG,
|
|
ARG_TYPE_LONG_LONG,
|
|
ARG_TYPE_SIZE,
|
|
ARG_TYPE_LONG_DOUBLE,
|
|
ARG_TYPE_DOUBLE,
|
|
ARG_TYPE_POINTER
|
|
};
|
|
|
|
/*********** refcounted strings ****************/
|
|
|
|
G_LOCK_DEFINE_STATIC (unique_ref_strs);
|
|
static GHashTable *unique_ref_strs = NULL;
|
|
|
|
static eel_ref_str
|
|
eel_ref_str_new_internal (const char *string,
|
|
int start_count)
|
|
{
|
|
char *res;
|
|
volatile gint *count;
|
|
gsize len;
|
|
|
|
len = strlen (string);
|
|
res = g_malloc (sizeof (gint) + len + 1);
|
|
count = (volatile gint *) res;
|
|
*count = start_count;
|
|
res += sizeof (gint);
|
|
memcpy (res, string, len + 1);
|
|
return res;
|
|
}
|
|
|
|
eel_ref_str
|
|
eel_ref_str_new (const char *string)
|
|
{
|
|
if (string == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return eel_ref_str_new_internal (string, 1);
|
|
}
|
|
|
|
eel_ref_str
|
|
eel_ref_str_get_unique (const char *string)
|
|
{
|
|
eel_ref_str res;
|
|
|
|
if (string == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
G_LOCK (unique_ref_strs);
|
|
if (unique_ref_strs == NULL)
|
|
{
|
|
unique_ref_strs =
|
|
g_hash_table_new (g_str_hash, g_str_equal);
|
|
}
|
|
|
|
res = g_hash_table_lookup (unique_ref_strs, string);
|
|
if (res != NULL)
|
|
{
|
|
eel_ref_str_ref (res);
|
|
}
|
|
else
|
|
{
|
|
res = eel_ref_str_new_internal (string, 0x80000001);
|
|
g_hash_table_insert (unique_ref_strs, res, res);
|
|
}
|
|
|
|
G_UNLOCK (unique_ref_strs);
|
|
|
|
return res;
|
|
}
|
|
|
|
eel_ref_str
|
|
eel_ref_str_ref (eel_ref_str str)
|
|
{
|
|
volatile gint *count;
|
|
|
|
count = (volatile gint *) ((char *) str - sizeof (gint));
|
|
g_atomic_int_add (count, 1);
|
|
|
|
return str;
|
|
}
|
|
|
|
void
|
|
eel_ref_str_unref (eel_ref_str str)
|
|
{
|
|
volatile gint *count;
|
|
gint old_ref;
|
|
|
|
if (str == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
count = (volatile gint *) ((char *) str - sizeof (gint));
|
|
|
|
retry_atomic_decrement:
|
|
old_ref = g_atomic_int_get (count);
|
|
if (old_ref == 1)
|
|
{
|
|
g_free ((char *) count);
|
|
}
|
|
else if (old_ref == 0x80000001)
|
|
{
|
|
G_LOCK (unique_ref_strs);
|
|
/* Need to recheck after taking lock to avoid races with _get_unique() */
|
|
if (g_atomic_int_add (count, -1) == 0x80000001)
|
|
{
|
|
g_hash_table_remove (unique_ref_strs, (char *) str);
|
|
g_free ((char *) count);
|
|
}
|
|
G_UNLOCK (unique_ref_strs);
|
|
}
|
|
else if (!g_atomic_int_compare_and_exchange (count,
|
|
old_ref, old_ref - 1))
|
|
{
|
|
goto retry_atomic_decrement;
|
|
}
|
|
}
|
|
|
|
|
|
#if !defined (EEL_OMIT_SELF_CHECK)
|
|
|
|
void
|
|
eel_self_check_string (void)
|
|
{
|
|
EEL_CHECK_STRING_RESULT (eel_str_double_underscores (NULL), NULL);
|
|
EEL_CHECK_STRING_RESULT (eel_str_double_underscores (""), "");
|
|
EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("_"), "__");
|
|
EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("foo"), "foo");
|
|
EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("foo_bar"), "foo__bar");
|
|
EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("foo_bar_2"), "foo__bar__2");
|
|
EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("_foo"), "__foo");
|
|
EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("foo_"), "foo__");
|
|
|
|
EEL_CHECK_STRING_RESULT (eel_str_capitalize (NULL), NULL);
|
|
EEL_CHECK_STRING_RESULT (eel_str_capitalize (""), "");
|
|
EEL_CHECK_STRING_RESULT (eel_str_capitalize ("foo"), "Foo");
|
|
EEL_CHECK_STRING_RESULT (eel_str_capitalize ("Foo"), "Foo");
|
|
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 0), NULL);
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 1), "foo");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 3), "foo");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 4), "foo");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 5), "foo");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 6), "foo");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 7), "foo");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 0), NULL);
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 1), "a_much_longer_foo");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 2), "a_much_longer_foo");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 3), "a…o");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 4), "a…oo");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 5), "a_…oo");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 6), "a_…foo");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 7), "a_m…foo");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 8), "a_m…_foo");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 9), "a_mu…_foo");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 8), "som…even");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 8), "som…_odd");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 9), "some…even");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 9), "some…_odd");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 10), "some…_even");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 10), "some…g_odd");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 11), "somet…_even");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 11), "somet…g_odd");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 12), "somet…g_even");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 12), "somet…ng_odd");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 13), "someth…g_even");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 13), "something_odd");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 14), "something_even");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 13), "something_odd");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("ääääääääää", 5), "ää…ää");
|
|
EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("あぃいぅうぇえぉ", 7), "あぃい…ぇえぉ");
|
|
|
|
EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after (NULL, "bar"), NULL);
|
|
EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after ("", "bar"), "");
|
|
EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after ("foo", "bar"), "foo");
|
|
EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after ("foo bar", "bar"), "foo ");
|
|
EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after ("foo bar xxx", "bar"), "foo ");
|
|
EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after ("bar", "bar"), "");
|
|
|
|
EEL_CHECK_STRING_RESULT (eel_str_replace_substring (NULL, "foo", NULL), NULL);
|
|
EEL_CHECK_STRING_RESULT (eel_str_replace_substring (NULL, "foo", "bar"), NULL);
|
|
EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("bar", "foo", NULL), "bar");
|
|
EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("", "foo", ""), "");
|
|
EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("", "foo", "bar"), "");
|
|
EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("bar", "foo", ""), "bar");
|
|
EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("xxx", "x", "foo"), "foofoofoo");
|
|
EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("fff", "f", "foo"), "foofoofoo");
|
|
EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("foofoofoo", "foo", "f"), "fff");
|
|
EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("foofoofoo", "f", ""), "oooooo");
|
|
}
|
|
|
|
#endif /* !EEL_OMIT_SELF_CHECK */
|