mirror of
https://github.com/git-for-windows/git.git
synced 2026-06-27 00:58:30 -05:00
The Git project is not exactly the easiest project to get started in: it's written in C and POSIX shell, with bits of Perl, Rust and other languages sprinkled into it. On top of that, the project has grown somewhat organically over time, making the codebase hard to navigate. These are problems that we're aware of, and there have been and still are efforts to clean up some of the technical debt that is natural to exist an a project that is more than 20 years old. Furthermore, we provide resources to newcomers that help them out like our coding guidelines, code of conduct or "MyFirstContribution.adoc". But there is a rather practical problem: finding your way around in our project's tree is not easy. Doing a directory listing in the top-level directory will present you with more than 550 files, which makes it extremely hard for a newcomer to figure out what files they are even supposed to look at. This makes the onboarding experience somewhat harder than it really needs to be. This isn't only a problem for newcomers though, as I myself struggle to find the files I am looking for because of the sheer number of files. Besides the problem of discoverability it also creates a problem of structure. It is not obvious at all which files are part of "libgit.a" and which files are only linked into our final executables. So while we have this split in our build systems, that split is not evident at all in our tree. Introduce a new "lib/" directory and move all of our sources for "libgit.a" into it to fix these issues. It makes the split we have evident and reduces the number of files in our top-level tree from 550 files to ~80 files. This is still a lot of files, but it's significantly easier to navigate already. Furthermore, we can further iterate after this step and think about introducing a better structure for remaining files, as well. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
389 lines
9.6 KiB
C
389 lines
9.6 KiB
C
#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,
|
|
bool *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 {
|
|
if (exact_match)
|
|
*exact_match = true;
|
|
return middle;
|
|
}
|
|
}
|
|
|
|
if (exact_match)
|
|
*exact_match = false;
|
|
return right;
|
|
}
|
|
|
|
static size_t add_entry(struct string_list *list, const char *string)
|
|
{
|
|
bool exact_match;
|
|
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)
|
|
{
|
|
bool 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);
|
|
}
|
|
}
|
|
|
|
bool string_list_has_string(const struct string_list *list, const char *string)
|
|
{
|
|
bool exact_match;
|
|
get_entry_index(list, string, &exact_match);
|
|
return exact_match;
|
|
}
|
|
|
|
size_t string_list_find_insert_index(const struct string_list *list, const char *string,
|
|
bool *exact_match)
|
|
{
|
|
return get_entry_index(list, string, exact_match);
|
|
}
|
|
|
|
struct string_list_item *string_list_lookup(struct string_list *list, const char *string)
|
|
{
|
|
bool exact_match;
|
|
size_t 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);
|
|
}
|
|
|
|
void string_list_sort_u(struct string_list *list, int free_util)
|
|
{
|
|
string_list_sort(list);
|
|
string_list_remove_duplicates(list, free_util);
|
|
}
|
|
|
|
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--;
|
|
}
|
|
|
|
void unsorted_string_list_remove(struct string_list *list, const char *str,
|
|
int free_util)
|
|
{
|
|
struct string_list_item *item = unsorted_string_list_lookup(list, str);
|
|
if (item)
|
|
unsorted_string_list_delete_item(list, item - list->items,
|
|
free_util);
|
|
}
|
|
|
|
/*
|
|
* 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 (;;) {
|
|
const 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);
|
|
}
|