refactor: add capacity-aware template string matrices
This commit is contained in:
parent
d93fead0ae
commit
f20d8f7115
4 changed files with 210 additions and 112 deletions
|
|
@ -41,10 +41,6 @@ static char *images_href_builder(int page, void *data) {
|
||||||
return build_images_href((const char *)data, page);
|
return build_images_href((const char *)data, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free_pagination(char ***matrix, int *inner_counts, int count) {
|
|
||||||
free_string_matrix(matrix, inner_counts, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
int images_handler(UrlParams *params) {
|
int images_handler(UrlParams *params) {
|
||||||
extern Config global_config;
|
extern Config global_config;
|
||||||
TemplateContext ctx = new_context();
|
TemplateContext ctx = new_context();
|
||||||
|
|
@ -80,14 +76,12 @@ int images_handler(UrlParams *params) {
|
||||||
if (!error_images_msg)
|
if (!error_images_msg)
|
||||||
error_images_msg = "Error fetching images";
|
error_images_msg = "Error fetching images";
|
||||||
|
|
||||||
char ***pager_matrix = NULL;
|
StringMatrix pagination;
|
||||||
int *pager_inner_counts = NULL;
|
string_matrix_build_pagination(page, images_href_builder, (void *)raw_query,
|
||||||
int pager_count =
|
&pagination);
|
||||||
build_pagination(page, images_href_builder, (void *)raw_query,
|
if (pagination.count > 0) {
|
||||||
&pager_matrix, &pager_inner_counts);
|
context_set_array_of_arrays(&ctx, "pagination_links", pagination.rows,
|
||||||
if (pager_count > 0) {
|
pagination.count, pagination.field_counts);
|
||||||
context_set_array_of_arrays(&ctx, "pagination_links", pager_matrix,
|
|
||||||
pager_count, pager_inner_counts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char *display_query = url_decode_query(raw_query);
|
char *display_query = url_decode_query(raw_query);
|
||||||
|
|
@ -95,7 +89,7 @@ int images_handler(UrlParams *params) {
|
||||||
|
|
||||||
if (!raw_query || strlen(raw_query) == 0) {
|
if (!raw_query || strlen(raw_query) == 0) {
|
||||||
send_redirect("/");
|
send_redirect("/");
|
||||||
free_pagination(pager_matrix, pager_inner_counts, pager_count);
|
string_matrix_free(&pagination);
|
||||||
free(display_query);
|
free(display_query);
|
||||||
free(locale);
|
free(locale);
|
||||||
free_context(&ctx);
|
free_context(&ctx);
|
||||||
|
|
@ -133,7 +127,7 @@ int images_handler(UrlParams *params) {
|
||||||
snprintf(response, sizeof(response), "<h1>%s</h1>", rate_limit_msg);
|
snprintf(response, sizeof(response), "<h1>%s</h1>", rate_limit_msg);
|
||||||
send_response(response);
|
send_response(response);
|
||||||
free(request_cache_key);
|
free(request_cache_key);
|
||||||
free_pagination(pager_matrix, pager_inner_counts, pager_count);
|
string_matrix_free(&pagination);
|
||||||
free(display_query);
|
free(display_query);
|
||||||
free(locale);
|
free(locale);
|
||||||
free_context(&ctx);
|
free_context(&ctx);
|
||||||
|
|
@ -154,16 +148,15 @@ int images_handler(UrlParams *params) {
|
||||||
snprintf(error_html, sizeof(error_html), "<h1>%s</h1>", error_images_msg);
|
snprintf(error_html, sizeof(error_html), "<h1>%s</h1>", error_images_msg);
|
||||||
send_response(error_html);
|
send_response(error_html);
|
||||||
free(request_cache_key);
|
free(request_cache_key);
|
||||||
free_pagination(pager_matrix, pager_inner_counts, pager_count);
|
string_matrix_free(&pagination);
|
||||||
free(display_query);
|
free(display_query);
|
||||||
free(locale);
|
free(locale);
|
||||||
free_context(&ctx);
|
free_context(&ctx);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
char ***image_matrix = NULL;
|
StringMatrix images;
|
||||||
int *inner_counts = NULL;
|
string_matrix_init(&images);
|
||||||
int image_count = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < result_count; i++) {
|
for (int i = 0; i < result_count; i++) {
|
||||||
const char *values[IMAGE_RESULT_FIELDS] = {
|
const char *values[IMAGE_RESULT_FIELDS] = {
|
||||||
|
|
@ -172,12 +165,9 @@ int images_handler(UrlParams *params) {
|
||||||
results[i].page_url,
|
results[i].page_url,
|
||||||
results[i].full_url,
|
results[i].full_url,
|
||||||
};
|
};
|
||||||
int new_count = append_string_row(&image_matrix, &inner_counts,
|
if (string_matrix_append(&images, values, IMAGE_RESULT_FIELDS) != 0) {
|
||||||
image_count, values,
|
string_matrix_free(&images);
|
||||||
IMAGE_RESULT_FIELDS);
|
string_matrix_free(&pagination);
|
||||||
if (new_count == image_count) {
|
|
||||||
free_string_matrix(image_matrix, inner_counts, image_count);
|
|
||||||
free_pagination(pager_matrix, pager_inner_counts, pager_count);
|
|
||||||
free_image_results(results, result_count);
|
free_image_results(results, result_count);
|
||||||
free(request_cache_key);
|
free(request_cache_key);
|
||||||
free(display_query);
|
free(display_query);
|
||||||
|
|
@ -185,11 +175,10 @@ int images_handler(UrlParams *params) {
|
||||||
free_context(&ctx);
|
free_context(&ctx);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
image_count = new_count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context_set_array_of_arrays(&ctx, "images", image_matrix, image_count,
|
context_set_array_of_arrays(&ctx, "images", images.rows, images.count,
|
||||||
inner_counts);
|
images.field_counts);
|
||||||
|
|
||||||
char *rendered = render_template("images.html", &ctx);
|
char *rendered = render_template("images.html", &ctx);
|
||||||
if (rendered) {
|
if (rendered) {
|
||||||
|
|
@ -199,9 +188,8 @@ int images_handler(UrlParams *params) {
|
||||||
send_response("<h1>Error rendering image results</h1>");
|
send_response("<h1>Error rendering image results</h1>");
|
||||||
}
|
}
|
||||||
|
|
||||||
free_string_matrix(image_matrix, inner_counts, image_count);
|
string_matrix_free(&images);
|
||||||
|
string_matrix_free(&pagination);
|
||||||
free_pagination(pager_matrix, pager_inner_counts, pager_count);
|
|
||||||
|
|
||||||
free_image_results(results, result_count);
|
free_image_results(results, result_count);
|
||||||
free(request_cache_key);
|
free(request_cache_key);
|
||||||
|
|
|
||||||
|
|
@ -140,29 +140,31 @@ static InfoBox fetch_currency_wrapper(char *query) {
|
||||||
return fetch_currency_data(query);
|
return fetch_currency_data(query);
|
||||||
}
|
}
|
||||||
char *get_base_url(const char *input) {
|
char *get_base_url(const char *input) {
|
||||||
if (!input) return NULL;
|
if (!input)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
const char *start = input;
|
const char *start = input;
|
||||||
|
|
||||||
const char *protocol_pos = strstr(input, "://");
|
const char *protocol_pos = strstr(input, "://");
|
||||||
if (protocol_pos) {
|
if (protocol_pos) {
|
||||||
start = protocol_pos + 3;
|
start = protocol_pos + 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *end = start;
|
const char *end = start;
|
||||||
while (*end && *end != '/' && *end != '?' && *end != '#') {
|
while (*end && *end != '/' && *end != '?' && *end != '#') {
|
||||||
end++;
|
end++;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t len = end - start;
|
size_t len = end - start;
|
||||||
|
|
||||||
char *domain = (char *)malloc(len + 1);
|
char *domain = (char *)malloc(len + 1);
|
||||||
if (!domain) return NULL;
|
if (!domain)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
strncpy(domain, start, len);
|
strncpy(domain, start, len);
|
||||||
domain[len] = '\0';
|
domain[len] = '\0';
|
||||||
|
|
||||||
return domain;
|
return domain;
|
||||||
}
|
}
|
||||||
static int is_calculator_query(const char *query) {
|
static int is_calculator_query(const char *query) {
|
||||||
if (!query)
|
if (!query)
|
||||||
|
|
@ -252,11 +254,8 @@ static void *infobox_thread_func(void *arg) {
|
||||||
static int add_infobox_to_collection(InfoBox *infobox, char ****collection,
|
static int add_infobox_to_collection(InfoBox *infobox, char ****collection,
|
||||||
int **inner_counts, int current_count) {
|
int **inner_counts, int current_count) {
|
||||||
const char *values[INFOBOX_FIELD_COUNT] = {
|
const char *values[INFOBOX_FIELD_COUNT] = {
|
||||||
infobox->title,
|
infobox->title, infobox->thumbnail_url, infobox->extract,
|
||||||
infobox->thumbnail_url,
|
infobox->url, infobox->url,
|
||||||
infobox->extract,
|
|
||||||
infobox->url,
|
|
||||||
infobox->url,
|
|
||||||
};
|
};
|
||||||
return append_string_row(collection, inner_counts, current_count, values,
|
return append_string_row(collection, inner_counts, current_count, values,
|
||||||
INFOBOX_FIELD_COUNT);
|
INFOBOX_FIELD_COUNT);
|
||||||
|
|
@ -267,8 +266,7 @@ static int add_warning_to_collection(const char *engine_name,
|
||||||
char ****collection, int **inner_counts,
|
char ****collection, int **inner_counts,
|
||||||
int current_count) {
|
int current_count) {
|
||||||
const char *values[] = {engine_name, warning_message};
|
const char *values[] = {engine_name, warning_message};
|
||||||
return append_string_row(collection, inner_counts, current_count, values,
|
return append_string_row(collection, inner_counts, current_count, values, 2);
|
||||||
2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free_user_engine_list(char **user_engines, int user_engine_count) {
|
static void free_user_engine_list(char **user_engines, int user_engine_count) {
|
||||||
|
|
@ -296,19 +294,25 @@ static void free_infobox_results(InfoBoxThreadData *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *warning_message_for_job(const ScrapeJob *job, const char *locale) {
|
static const char *warning_message_for_job(const ScrapeJob *job,
|
||||||
|
const char *locale) {
|
||||||
switch (job->status) {
|
switch (job->status) {
|
||||||
case SCRAPE_STATUS_FETCH_ERROR: {
|
case SCRAPE_STATUS_FETCH_ERROR: {
|
||||||
const char *msg = beaker_get_locale_value(locale, "warning_fetch_error");
|
const char *msg = beaker_get_locale_value(locale, "warning_fetch_error");
|
||||||
return msg ? msg : "request failed before OmniSearch could read search results.";
|
return msg ? msg
|
||||||
|
: "request failed before OmniSearch could read search results.";
|
||||||
}
|
}
|
||||||
case SCRAPE_STATUS_PARSE_MISMATCH: {
|
case SCRAPE_STATUS_PARSE_MISMATCH: {
|
||||||
const char *msg = beaker_get_locale_value(locale, "warning_parse_mismatch");
|
const char *msg = beaker_get_locale_value(locale, "warning_parse_mismatch");
|
||||||
return msg ? msg : "returned search results in a format OmniSearch could not parse.";
|
return msg ? msg
|
||||||
|
: "returned search results in a format OmniSearch could not "
|
||||||
|
"parse.";
|
||||||
}
|
}
|
||||||
case SCRAPE_STATUS_BLOCKED: {
|
case SCRAPE_STATUS_BLOCKED: {
|
||||||
const char *msg = beaker_get_locale_value(locale, "warning_blocked");
|
const char *msg = beaker_get_locale_value(locale, "warning_blocked");
|
||||||
return msg ? msg : "returned a captcha or another blocking page instead of search results.";
|
return msg ? msg
|
||||||
|
: "returned a captcha or another blocking page instead of "
|
||||||
|
"search results.";
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -362,8 +366,8 @@ static int engine_allowed_for_user(const SearchEngine *eng, char **user_ids,
|
||||||
static char *build_search_href(const char *query, const char *engine_id,
|
static char *build_search_href(const char *query, const char *engine_id,
|
||||||
int page) {
|
int page) {
|
||||||
const char *safe_query = query ? query : "";
|
const char *safe_query = query ? query : "";
|
||||||
int use_engine = engine_id && engine_id[0] != '\0' &&
|
int use_engine =
|
||||||
!engine_id_matches(engine_id, "all");
|
engine_id && engine_id[0] != '\0' && !engine_id_matches(engine_id, "all");
|
||||||
size_t needed = strlen("/search?q=") + strlen(safe_query) + 1;
|
size_t needed = strlen("/search?q=") + strlen(safe_query) + 1;
|
||||||
|
|
||||||
if (use_engine)
|
if (use_engine)
|
||||||
|
|
@ -421,7 +425,8 @@ int results_handler(UrlParams *params) {
|
||||||
|
|
||||||
char **user_engines = NULL;
|
char **user_engines = NULL;
|
||||||
int user_engine_count = 0;
|
int user_engine_count = 0;
|
||||||
int has_user_pref = (get_user_engines(&user_engines, &user_engine_count) == 0);
|
int has_user_pref =
|
||||||
|
(get_user_engines(&user_engines, &user_engine_count) == 0);
|
||||||
|
|
||||||
if (params) {
|
if (params) {
|
||||||
for (int i = 0; i < params->count; i++) {
|
for (int i = 0; i < params->count; i++) {
|
||||||
|
|
@ -449,9 +454,11 @@ int results_handler(UrlParams *params) {
|
||||||
beaker_set_locale(&ctx, locale);
|
beaker_set_locale(&ctx, locale);
|
||||||
|
|
||||||
const char *rate_limit_msg = beaker_get_locale_value(locale, "rate_limit");
|
const char *rate_limit_msg = beaker_get_locale_value(locale, "rate_limit");
|
||||||
if (!rate_limit_msg) rate_limit_msg = "Slow down! Too many searches from you!";
|
if (!rate_limit_msg)
|
||||||
|
rate_limit_msg = "Slow down! Too many searches from you!";
|
||||||
const char *no_results_msg = beaker_get_locale_value(locale, "no_results");
|
const char *no_results_msg = beaker_get_locale_value(locale, "no_results");
|
||||||
if (!no_results_msg) no_results_msg = "No results found";
|
if (!no_results_msg)
|
||||||
|
no_results_msg = "No results found";
|
||||||
|
|
||||||
char page_str[16];
|
char page_str[16];
|
||||||
snprintf(page_str, sizeof(page_str), "%d", page);
|
snprintf(page_str, sizeof(page_str), "%d", page);
|
||||||
|
|
@ -604,9 +611,9 @@ int results_handler(UrlParams *params) {
|
||||||
? "engine-filter active"
|
? "engine-filter active"
|
||||||
: "engine-filter";
|
: "engine-filter";
|
||||||
|
|
||||||
filter_count = add_link_to_collection(filter_href, ENGINE_REGISTRY[i].name,
|
filter_count = add_link_to_collection(
|
||||||
filter_class, &filter_matrix,
|
filter_href, ENGINE_REGISTRY[i].name, filter_class, &filter_matrix,
|
||||||
&filter_inner_counts, filter_count);
|
&filter_inner_counts, filter_count);
|
||||||
free(filter_href);
|
free(filter_href);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -660,11 +667,12 @@ int results_handler(UrlParams *params) {
|
||||||
free_user_engine_list(user_engines, user_engine_count);
|
free_user_engine_list(user_engines, user_engine_count);
|
||||||
free_context(&ctx);
|
free_context(&ctx);
|
||||||
char no_results_html[128];
|
char no_results_html[128];
|
||||||
snprintf(no_results_html, sizeof(no_results_html), "<h1>%s</h1>", no_results_msg);
|
snprintf(no_results_html, sizeof(no_results_html), "<h1>%s</h1>",
|
||||||
|
no_results_msg);
|
||||||
send_response(no_results_html);
|
send_response(no_results_html);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char ***infobox_matrix = NULL;
|
char ***infobox_matrix = NULL;
|
||||||
int *infobox_inner_counts = NULL;
|
int *infobox_inner_counts = NULL;
|
||||||
int infobox_count = 0;
|
int infobox_count = 0;
|
||||||
|
|
@ -720,9 +728,8 @@ int results_handler(UrlParams *params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (total_results > 0) {
|
if (total_results > 0) {
|
||||||
char ***results_matrix = NULL;
|
StringMatrix results_matrix;
|
||||||
int *results_inner_counts = NULL;
|
string_matrix_init(&results_matrix);
|
||||||
int unique_count = 0;
|
|
||||||
UrlHashTable url_table;
|
UrlHashTable url_table;
|
||||||
url_hash_init(&url_table);
|
url_hash_init(&url_table);
|
||||||
|
|
||||||
|
|
@ -749,34 +756,35 @@ int results_handler(UrlParams *params) {
|
||||||
base_url,
|
base_url,
|
||||||
"",
|
"",
|
||||||
};
|
};
|
||||||
int new_count = append_string_row(&results_matrix, &results_inner_counts,
|
int append_result =
|
||||||
unique_count, values,
|
string_matrix_append(&results_matrix, values, RESULT_FIELD_COUNT);
|
||||||
RESULT_FIELD_COUNT);
|
|
||||||
|
|
||||||
free(pretty_url);
|
free(pretty_url);
|
||||||
free(base_url);
|
free(base_url);
|
||||||
free(all_results[i][j].url);
|
free(all_results[i][j].url);
|
||||||
free(all_results[i][j].title);
|
free(all_results[i][j].title);
|
||||||
free(all_results[i][j].snippet);
|
free(all_results[i][j].snippet);
|
||||||
unique_count = new_count;
|
if (append_result != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
free(all_results[i]);
|
free(all_results[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
context_set_array_of_arrays(&ctx, "results", results_matrix, unique_count,
|
context_set_array_of_arrays(&ctx, "results", results_matrix.rows,
|
||||||
results_inner_counts);
|
results_matrix.count,
|
||||||
|
results_matrix.field_counts);
|
||||||
|
|
||||||
char ***pager_matrix = NULL;
|
StringMatrix pager_matrix;
|
||||||
int *pager_inner_counts = NULL;
|
SearchHrefData href_data = {.query = raw_query,
|
||||||
SearchHrefData href_data = { .query = raw_query, .engine_id = selected_engine_id };
|
.engine_id = selected_engine_id};
|
||||||
int pager_count = build_pagination(page, search_href_builder,
|
int pagination_result = string_matrix_build_pagination(
|
||||||
&href_data, &pager_matrix,
|
page, search_href_builder, &href_data, &pager_matrix);
|
||||||
&pager_inner_counts);
|
|
||||||
|
|
||||||
if (pager_count > 0) {
|
if (pagination_result == 0 && pager_matrix.count > 0) {
|
||||||
context_set_array_of_arrays(&ctx, "pagination_links", pager_matrix,
|
context_set_array_of_arrays(&ctx, "pagination_links", pager_matrix.rows,
|
||||||
pager_count, pager_inner_counts);
|
pager_matrix.count,
|
||||||
free_string_matrix(pager_matrix, pager_inner_counts, pager_count);
|
pager_matrix.field_counts);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *html = render_template("results.html", &ctx);
|
char *html = render_template("results.html", &ctx);
|
||||||
|
|
@ -785,7 +793,8 @@ int results_handler(UrlParams *params) {
|
||||||
free(html);
|
free(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
free_string_matrix(results_matrix, results_inner_counts, unique_count);
|
string_matrix_free(&pager_matrix);
|
||||||
|
string_matrix_free(&results_matrix);
|
||||||
url_hash_free(&url_table);
|
url_hash_free(&url_table);
|
||||||
} else {
|
} else {
|
||||||
char *html = render_template("results.html", &ctx);
|
char *html = render_template("results.html", &ctx);
|
||||||
|
|
|
||||||
|
|
@ -263,6 +263,67 @@ void free_string_matrix(char ***matrix, int *inner_counts, int row_count) {
|
||||||
free(inner_counts);
|
free(inner_counts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void string_matrix_init(StringMatrix *matrix) {
|
||||||
|
if (matrix)
|
||||||
|
*matrix = (StringMatrix){0};
|
||||||
|
}
|
||||||
|
|
||||||
|
int string_matrix_append(StringMatrix *matrix, const char *const *values,
|
||||||
|
int field_count) {
|
||||||
|
if (!matrix || !values || field_count <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
char **row = calloc((size_t)field_count, sizeof(*row));
|
||||||
|
if (!row)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < field_count; i++) {
|
||||||
|
row[i] = strdup(values[i] ? values[i] : "");
|
||||||
|
if (!row[i]) {
|
||||||
|
for (int j = 0; j < i; j++)
|
||||||
|
free(row[j]);
|
||||||
|
free(row);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matrix->count == matrix->capacity) {
|
||||||
|
int new_capacity = matrix->capacity ? matrix->capacity * 2 : 8;
|
||||||
|
char ***new_rows = malloc(sizeof(*new_rows) * (size_t)new_capacity);
|
||||||
|
int *new_counts = malloc(sizeof(*new_counts) * (size_t)new_capacity);
|
||||||
|
if (!new_rows || !new_counts) {
|
||||||
|
free(new_rows);
|
||||||
|
free(new_counts);
|
||||||
|
for (int i = 0; i < field_count; i++)
|
||||||
|
free(row[i]);
|
||||||
|
free(row);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (matrix->count > 0) {
|
||||||
|
memcpy(new_rows, matrix->rows, sizeof(*new_rows) * matrix->count);
|
||||||
|
memcpy(new_counts, matrix->field_counts,
|
||||||
|
sizeof(*new_counts) * matrix->count);
|
||||||
|
}
|
||||||
|
free(matrix->rows);
|
||||||
|
free(matrix->field_counts);
|
||||||
|
matrix->rows = new_rows;
|
||||||
|
matrix->field_counts = new_counts;
|
||||||
|
matrix->capacity = new_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
matrix->rows[matrix->count] = row;
|
||||||
|
matrix->field_counts[matrix->count] = field_count;
|
||||||
|
matrix->count++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void string_matrix_free(StringMatrix *matrix) {
|
||||||
|
if (!matrix)
|
||||||
|
return;
|
||||||
|
free_string_matrix(matrix->rows, matrix->field_counts, matrix->count);
|
||||||
|
*matrix = (StringMatrix){0};
|
||||||
|
}
|
||||||
|
|
||||||
int add_link_to_collection(const char *href, const char *label,
|
int add_link_to_collection(const char *href, const char *label,
|
||||||
const char *class_name, char ****collection,
|
const char *class_name, char ****collection,
|
||||||
int **inner_counts, int current_count) {
|
int **inner_counts, int current_count) {
|
||||||
|
|
@ -273,37 +334,62 @@ int add_link_to_collection(const char *href, const char *label,
|
||||||
|
|
||||||
int build_pagination(int page, char *(*href_builder)(int page, void *data),
|
int build_pagination(int page, char *(*href_builder)(int page, void *data),
|
||||||
void *data, char ****out_matrix, int **out_inner_counts) {
|
void *data, char ****out_matrix, int **out_inner_counts) {
|
||||||
|
if (!out_matrix || !out_inner_counts)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
StringMatrix matrix;
|
||||||
|
if (string_matrix_build_pagination(page, href_builder, data, &matrix) != 0) {
|
||||||
|
*out_matrix = NULL;
|
||||||
|
*out_inner_counts = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_matrix = matrix.rows;
|
||||||
|
*out_inner_counts = matrix.field_counts;
|
||||||
|
return matrix.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int string_matrix_build_pagination(int page,
|
||||||
|
char *(*href_builder)(int page, void *data),
|
||||||
|
void *data, StringMatrix *out_matrix) {
|
||||||
enum { PAGER_WINDOW_SIZE = 5 };
|
enum { PAGER_WINDOW_SIZE = 5 };
|
||||||
|
if (!out_matrix || !href_builder)
|
||||||
|
return -1;
|
||||||
|
|
||||||
*out_matrix = NULL;
|
string_matrix_init(out_matrix);
|
||||||
*out_inner_counts = NULL;
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
int pager_start = page <= 3 ? 1 : page - 2;
|
int pager_start = page <= 3 ? 1 : page - 2;
|
||||||
int pager_end = pager_start + PAGER_WINDOW_SIZE - 1;
|
int pager_end = pager_start + PAGER_WINDOW_SIZE - 1;
|
||||||
|
|
||||||
if (page > 1) {
|
for (int i = page > 1 ? pager_start - 1 : pager_start; i <= pager_end + 1;
|
||||||
char *href = href_builder(page - 1, data);
|
i++) {
|
||||||
count = add_link_to_collection(href, "←", "pagination-btn prev", out_matrix,
|
const char *label = NULL;
|
||||||
out_inner_counts, count);
|
const char *class_name = "pagination-btn";
|
||||||
|
char page_label[16];
|
||||||
|
int target_page = i;
|
||||||
|
|
||||||
|
if (page > 1 && i == pager_start - 1) {
|
||||||
|
label = "←";
|
||||||
|
class_name = "pagination-btn prev";
|
||||||
|
target_page = page - 1;
|
||||||
|
} else if (i == pager_end + 1) {
|
||||||
|
label = "→";
|
||||||
|
class_name = "pagination-btn next";
|
||||||
|
target_page = page + 1;
|
||||||
|
} else {
|
||||||
|
snprintf(page_label, sizeof(page_label), "%d", i);
|
||||||
|
label = page_label;
|
||||||
|
if (i == page)
|
||||||
|
class_name = "pagination-btn pagination-current";
|
||||||
|
}
|
||||||
|
|
||||||
|
char *href = href_builder(target_page, data);
|
||||||
|
const char *values[LINK_FIELD_COUNT] = {href, label, class_name};
|
||||||
|
int result = string_matrix_append(out_matrix, values, LINK_FIELD_COUNT);
|
||||||
free(href);
|
free(href);
|
||||||
|
if (result != 0) {
|
||||||
|
string_matrix_free(out_matrix);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
for (int i = pager_start; i <= pager_end; i++) {
|
|
||||||
char label[16];
|
|
||||||
snprintf(label, sizeof(label), "%d", i);
|
|
||||||
char *href = href_builder(i, data);
|
|
||||||
count = add_link_to_collection(
|
|
||||||
href, label,
|
|
||||||
i == page ? "pagination-btn pagination-current" : "pagination-btn",
|
|
||||||
out_matrix, out_inner_counts, count);
|
|
||||||
free(href);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *href = href_builder(page + 1, data);
|
|
||||||
count = add_link_to_collection(href, "→", "pagination-btn next", out_matrix,
|
|
||||||
out_inner_counts, count);
|
|
||||||
free(href);
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,13 @@
|
||||||
|
|
||||||
#define LINK_FIELD_COUNT 3
|
#define LINK_FIELD_COUNT 3
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char ***rows;
|
||||||
|
int *field_counts;
|
||||||
|
int count;
|
||||||
|
int capacity;
|
||||||
|
} StringMatrix;
|
||||||
|
|
||||||
int hex_to_int(char c);
|
int hex_to_int(char c);
|
||||||
char *get_theme(const char *default_theme);
|
char *get_theme(const char *default_theme);
|
||||||
void init_themes(const char *static_path);
|
void init_themes(const char *static_path);
|
||||||
|
|
@ -28,11 +35,19 @@ int append_string_row(char ****matrix, int **inner_counts, int row_count,
|
||||||
const char *const *values, int field_count);
|
const char *const *values, int field_count);
|
||||||
void free_string_matrix(char ***matrix, int *inner_counts, int row_count);
|
void free_string_matrix(char ***matrix, int *inner_counts, int row_count);
|
||||||
|
|
||||||
|
void string_matrix_init(StringMatrix *matrix);
|
||||||
|
int string_matrix_append(StringMatrix *matrix, const char *const *values,
|
||||||
|
int field_count);
|
||||||
|
void string_matrix_free(StringMatrix *matrix);
|
||||||
|
|
||||||
int add_link_to_collection(const char *href, const char *label,
|
int add_link_to_collection(const char *href, const char *label,
|
||||||
const char *class_name, char ****collection,
|
const char *class_name, char ****collection,
|
||||||
int **inner_counts, int current_count);
|
int **inner_counts, int current_count);
|
||||||
|
|
||||||
int build_pagination(int page, char *(*href_builder)(int page, void *data),
|
int build_pagination(int page, char *(*href_builder)(int page, void *data),
|
||||||
void *data, char ****out_matrix, int **out_inner_counts);
|
void *data, char ****out_matrix, int **out_inner_counts);
|
||||||
|
int string_matrix_build_pagination(int page,
|
||||||
|
char *(*href_builder)(int page, void *data),
|
||||||
|
void *data, StringMatrix *out_matrix);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue