blob: 343cf1ca90d2ace20b315973c6c7549ef2e93a28 [file] [log] [blame] [edit]
#include "git-compat-util.h"
#include "string-list.h"
void string_list_init_nodup(struct string_list *list)
{
struct string_list blank = STRING_LIST_INIT_NODUP;
memcpy(list, &blank, sizeof(*list));
}
void string_list_init_dup(struct string_list *list)
{
struct string_list blank = STRING_LIST_INIT_DUP;
memcpy(list, &blank, sizeof(*list));
}
/* if there is no exact match, point to the index where the entry could be
* inserted */
static size_t get_entry_index(const struct string_list *list, const char *string,
int *exact_match)
{
size_t left = 0, right = list->nr;
compare_strings_fn cmp = list->cmp ? list->cmp : strcmp;
while (left < right) {
size_t middle = left + (right - left) / 2;
int compare = cmp(string, list->items[middle].string);
if (compare < 0)
right = middle;
else if (compare > 0)
left = middle + 1;
else {
*exact_match = 1;
return middle;
}
}
*exact_match = 0;
return right;
}
static size_t add_entry(struct string_list *list, const char *string)
{
int exact_match = 0;
size_t index = get_entry_index(list, string, &exact_match);
if (exact_match)
return index;
ALLOC_GROW(list->items, list->nr+1, list->alloc);
if (index < list->nr)
MOVE_ARRAY(list->items + index + 1, list->items + index,
list->nr - index);
list->items[index].string = list->strdup_strings ?
xstrdup(string) : (char *)string;
list->items[index].util = NULL;
list->nr++;
return index;
}
struct string_list_item *string_list_insert(struct string_list *list, const char *string)
{
size_t index = add_entry(list, string);
return list->items + index;
}
void string_list_remove(struct string_list *list, const char *string,
int free_util)
{
int exact_match;
int i = get_entry_index(list, string, &exact_match);
if (exact_match) {
if (list->strdup_strings)
free(list->items[i].string);
if (free_util)
free(list->items[i].util);
list->nr--;
MOVE_ARRAY(list->items + i, list->items + i + 1, list->nr - i);
}
}
int string_list_has_string(const struct string_list *list, const char *string)
{
int exact_match;
get_entry_index(list, string, &exact_match);
return exact_match;
}
int string_list_find_insert_index(const struct string_list *list, const char *string,
int negative_existing_index)
{
int exact_match;
int index = get_entry_index(list, string, &exact_match);
if (exact_match)
index = -1 - (negative_existing_index ? index : 0);
return index;
}
struct string_list_item *string_list_lookup(struct string_list *list, const char *string)
{
int exact_match, i = get_entry_index(list, string, &exact_match);
if (!exact_match)
return NULL;
return list->items + i;
}
void string_list_remove_duplicates(struct string_list *list, int free_util)
{
if (list->nr > 1) {
size_t dst = 1;
compare_strings_fn cmp = list->cmp ? list->cmp : strcmp;
for (size_t src = 1; src < list->nr; src++) {
if (!cmp(list->items[dst - 1].string, list->items[src].string)) {
if (list->strdup_strings)
free(list->items[src].string);
if (free_util)
free(list->items[src].util);
} else
list->items[dst++] = list->items[src];
}
list->nr = dst;
}
}
int for_each_string_list(struct string_list *list,
string_list_each_func_t fn, void *cb_data)
{
int ret = 0;
for (size_t i = 0; i < list->nr; i++)
if ((ret = fn(&list->items[i], cb_data)))
break;
return ret;
}
void filter_string_list(struct string_list *list, int free_util,
string_list_each_func_t want, void *cb_data)
{
size_t dst = 0;
for (size_t src = 0; src < list->nr; src++) {
if (want(&list->items[src], cb_data)) {
list->items[dst++] = list->items[src];
} else {
if (list->strdup_strings)
free(list->items[src].string);
if (free_util)
free(list->items[src].util);
}
}
list->nr = dst;
}
static int item_is_not_empty(struct string_list_item *item, void *data UNUSED)
{
return *item->string != '\0';
}
void string_list_remove_empty_items(struct string_list *list, int free_util)
{
filter_string_list(list, free_util, item_is_not_empty, NULL);
}
void string_list_clear(struct string_list *list, int free_util)
{
if (list->items) {
if (list->strdup_strings) {
for (size_t i = 0; i < list->nr; i++)
free(list->items[i].string);
}
if (free_util) {
for (size_t i = 0; i < list->nr; i++)
free(list->items[i].util);
}
free(list->items);
}
list->items = NULL;
list->nr = list->alloc = 0;
}
void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc)
{
if (list->items) {
if (clearfunc) {
for (size_t i = 0; i < list->nr; i++)
clearfunc(list->items[i].util, list->items[i].string);
}
if (list->strdup_strings) {
for (size_t i = 0; i < list->nr; i++)
free(list->items[i].string);
}
free(list->items);
}
list->items = NULL;
list->nr = list->alloc = 0;
}
void string_list_setlen(struct string_list *list, size_t nr)
{
if (list->strdup_strings)
BUG("cannot setlen a string_list which owns its entries");
if (nr > list->nr)
BUG("cannot grow a string_list with setlen");
list->nr = nr;
}
struct string_list_item *string_list_append_nodup(struct string_list *list,
char *string)
{
struct string_list_item *retval;
ALLOC_GROW(list->items, list->nr + 1, list->alloc);
retval = &list->items[list->nr++];
retval->string = string;
retval->util = NULL;
return retval;
}
struct string_list_item *string_list_append(struct string_list *list,
const char *string)
{
return string_list_append_nodup(
list,
list->strdup_strings ? xstrdup(string) : (char *)string);
}
/*
* Encapsulate the compare function pointer because ISO C99 forbids
* casting from void * to a function pointer and vice versa.
*/
struct string_list_sort_ctx
{
compare_strings_fn cmp;
};
static int cmp_items(const void *a, const void *b, void *ctx)
{
struct string_list_sort_ctx *sort_ctx = ctx;
const struct string_list_item *one = a;
const struct string_list_item *two = b;
return sort_ctx->cmp(one->string, two->string);
}
void string_list_sort(struct string_list *list)
{
struct string_list_sort_ctx sort_ctx = {list->cmp ? list->cmp : strcmp};
QSORT_S(list->items, list->nr, cmp_items, &sort_ctx);
}
struct string_list_item *unsorted_string_list_lookup(struct string_list *list,
const char *string)
{
struct string_list_item *item;
compare_strings_fn cmp = list->cmp ? list->cmp : strcmp;
for_each_string_list_item(item, list)
if (!cmp(string, item->string))
return item;
return NULL;
}
int unsorted_string_list_has_string(struct string_list *list,
const char *string)
{
return unsorted_string_list_lookup(list, string) != NULL;
}
void unsorted_string_list_delete_item(struct string_list *list, int i, int free_util)
{
if (list->strdup_strings)
free(list->items[i].string);
if (free_util)
free(list->items[i].util);
list->items[i] = list->items[list->nr-1];
list->nr--;
}
/*
* append a substring [p..end] to list; return number of things it
* appended to the list.
*/
static int append_one(struct string_list *list,
const char *p, const char *end,
int in_place, unsigned flags)
{
if (!end)
end = p + strlen(p);
if ((flags & STRING_LIST_SPLIT_TRIM)) {
/* rtrim */
for (; p < end; end--)
if (!isspace(end[-1]))
break;
}
if ((flags & STRING_LIST_SPLIT_NONEMPTY) && (end <= p))
return 0;
if (in_place) {
*((char *)end) = '\0';
string_list_append(list, p);
} else {
string_list_append_nodup(list, xmemdupz(p, end - p));
}
return 1;
}
/*
* Unfortunately this cannot become a public interface, as _in_place()
* wants to have "const char *string" while the other variant wants to
* have "char *string" for type safety.
*
* This accepts "const char *string" to allow both wrappers to use it;
* it internally casts away the constness when in_place is true by
* taking advantage of strpbrk() that takes a "const char *" arg and
* returns "char *" pointer into that const string. Yucky but works ;-).
*/
static int split_string(struct string_list *list, const char *string, const char *delim,
int maxsplit, int in_place, unsigned flags)
{
int count = 0;
const char *p = string;
if (in_place && list->strdup_strings)
BUG("string_list_split_in_place() called with strdup_strings");
else if (!in_place && !list->strdup_strings)
BUG("string_list_split() called without strdup_strings");
for (;;) {
char *end;
if (flags & STRING_LIST_SPLIT_TRIM) {
/* ltrim */
while (*p && isspace(*p))
p++;
}
if (0 <= maxsplit && maxsplit <= count)
end = NULL;
else
end = strpbrk(p, delim);
count += append_one(list, p, end, in_place, flags);
if (!end)
return count;
p = end + 1;
}
}
int string_list_split(struct string_list *list, const char *string,
const char *delim, int maxsplit)
{
return split_string(list, string, delim, maxsplit, 0, 0);
}
int string_list_split_in_place(struct string_list *list, char *string,
const char *delim, int maxsplit)
{
return split_string(list, string, delim, maxsplit, 1, 0);
}
int string_list_split_f(struct string_list *list, const char *string,
const char *delim, int maxsplit, unsigned flags)
{
return split_string(list, string, delim, maxsplit, 0, flags);
}
int string_list_split_in_place_f(struct string_list *list, char *string,
const char *delim, int maxsplit, unsigned flags)
{
return split_string(list, string, delim, maxsplit, 1, flags);
}