nautilus/eel/eel-string.c
Ernestas Kulik 3e3bd830b5 eel: string: fix middle truncation test cases
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, as 536505728e added an
assertion.
2018-03-21 10:05:45 +02:00

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 */