From d93fead0aeaed1b7860fe132476c81c1845ce756 Mon Sep 17 00:00:00 2001 From: frosty Date: Sat, 8 Aug 2026 12:56:26 -0400 Subject: refactor: centralise matrix ownership and request cleanup --- src/Routes/Images.c | 77 +++++++------- src/Routes/Search.c | 284 ++++++++++++++-------------------------------------- 2 files changed, 114 insertions(+), 247 deletions(-) (limited to 'src/Routes') diff --git a/src/Routes/Images.c b/src/Routes/Images.c index 40ab88f..d89b732 100644 --- a/src/Routes/Images.c +++ b/src/Routes/Images.c @@ -41,6 +41,10 @@ static char *images_href_builder(int page, void *data) { 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) { extern Config global_config; TemplateContext ctx = new_context(); @@ -91,8 +95,9 @@ int images_handler(UrlParams *params) { if (!raw_query || strlen(raw_query) == 0) { send_redirect("/"); - if (display_query) - free(display_query); + free_pagination(pager_matrix, pager_inner_counts, pager_count); + free(display_query); + free(locale); free_context(&ctx); return -1; } @@ -128,7 +133,9 @@ int images_handler(UrlParams *params) { snprintf(response, sizeof(response), "

%s

", rate_limit_msg); send_response(response); free(request_cache_key); + free_pagination(pager_matrix, pager_inner_counts, pager_count); free(display_query); + free(locale); free_context(&ctx); return -1; } @@ -147,36 +154,41 @@ int images_handler(UrlParams *params) { snprintf(error_html, sizeof(error_html), "

%s

", error_images_msg); send_response(error_html); free(request_cache_key); + free_pagination(pager_matrix, pager_inner_counts, pager_count); free(display_query); + free(locale); free_context(&ctx); return -1; } - char ***image_matrix = malloc(sizeof(char **) * result_count); - int *inner_counts = malloc(sizeof(int) * result_count); - - if (!image_matrix || !inner_counts) { - if (image_matrix) - free(image_matrix); - if (inner_counts) - free(inner_counts); - free_image_results(results, result_count); - free(request_cache_key); - free(display_query); - free_context(&ctx); - return -1; - } + char ***image_matrix = NULL; + int *inner_counts = NULL; + int image_count = 0; for (int i = 0; i < result_count; i++) { - image_matrix[i] = malloc(sizeof(char *) * IMAGE_RESULT_FIELDS); - image_matrix[i][0] = strdup(results[i].thumbnail_url); - image_matrix[i][1] = strdup(results[i].title); - image_matrix[i][2] = strdup(results[i].page_url); - image_matrix[i][3] = strdup(results[i].full_url); - inner_counts[i] = IMAGE_RESULT_FIELDS; + const char *values[IMAGE_RESULT_FIELDS] = { + results[i].thumbnail_url, + results[i].title, + results[i].page_url, + results[i].full_url, + }; + int new_count = append_string_row(&image_matrix, &inner_counts, + image_count, values, + IMAGE_RESULT_FIELDS); + 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(request_cache_key); + free(display_query); + free(locale); + free_context(&ctx); + return -1; + } + image_count = new_count; } - context_set_array_of_arrays(&ctx, "images", image_matrix, result_count, + context_set_array_of_arrays(&ctx, "images", image_matrix, image_count, inner_counts); char *rendered = render_template("images.html", &ctx); @@ -187,27 +199,14 @@ int images_handler(UrlParams *params) { send_response("

Error rendering image results

"); } - for (int i = 0; i < result_count; i++) { - for (int j = 0; j < IMAGE_RESULT_FIELDS; j++) - free(image_matrix[i][j]); - free(image_matrix[i]); - } - free(image_matrix); - free(inner_counts); + free_string_matrix(image_matrix, inner_counts, image_count); - if (pager_count > 0) { - for (int i = 0; i < pager_count; i++) { - for (int j = 0; j < LINK_FIELD_COUNT; j++) - free(pager_matrix[i][j]); - free(pager_matrix[i]); - } - free(pager_matrix); - free(pager_inner_counts); - } + free_pagination(pager_matrix, pager_inner_counts, pager_count); free_image_results(results, result_count); free(request_cache_key); free(display_query); + free(locale); free_context(&ctx); return 0; diff --git a/src/Routes/Search.c b/src/Routes/Search.c index b580c10..53718b3 100644 --- a/src/Routes/Search.c +++ b/src/Routes/Search.c @@ -251,71 +251,49 @@ static void *infobox_thread_func(void *arg) { static int add_infobox_to_collection(InfoBox *infobox, char ****collection, int **inner_counts, int current_count) { - *collection = - (char ***)realloc(*collection, sizeof(char **) * (current_count + 1)); - *inner_counts = - (int *)realloc(*inner_counts, sizeof(int) * (current_count + 1)); - - (*collection)[current_count] = - (char **)malloc(sizeof(char *) * INFOBOX_FIELD_COUNT); - (*collection)[current_count][0] = - infobox->title ? strdup(infobox->title) : NULL; - (*collection)[current_count][1] = - infobox->thumbnail_url ? strdup(infobox->thumbnail_url) : NULL; - (*collection)[current_count][2] = - infobox->extract ? strdup(infobox->extract) : NULL; - (*collection)[current_count][3] = infobox->url ? strdup(infobox->url) : NULL; - (*collection)[current_count][4] = infobox->url ? strdup(infobox->url) : NULL; - (*inner_counts)[current_count] = INFOBOX_FIELD_COUNT; - - return current_count + 1; + const char *values[INFOBOX_FIELD_COUNT] = { + infobox->title, + infobox->thumbnail_url, + infobox->extract, + infobox->url, + infobox->url, + }; + return append_string_row(collection, inner_counts, current_count, values, + INFOBOX_FIELD_COUNT); } static int add_warning_to_collection(const char *engine_name, const char *warning_message, char ****collection, int **inner_counts, int current_count) { - char ***new_collection = - (char ***)malloc(sizeof(char **) * (current_count + 1)); - int *new_inner_counts = - (int *)malloc(sizeof(int) * (current_count + 1)); + const char *values[] = {engine_name, warning_message}; + return append_string_row(collection, inner_counts, current_count, values, + 2); +} - if (!new_collection || !new_inner_counts) { - free(new_collection); - free(new_inner_counts); - return current_count; - } +static void free_user_engine_list(char **user_engines, int user_engine_count) { + for (int i = 0; i < user_engine_count; i++) + free(user_engines[i]); + free(user_engines); +} - if (*collection && current_count > 0) { - memcpy(new_collection, *collection, sizeof(char **) * current_count); - } - if (*inner_counts && current_count > 0) { - memcpy(new_inner_counts, *inner_counts, sizeof(int) * current_count); +static void free_search_results(SearchResult **all_results, ScrapeJob *jobs, + int job_count) { + for (int i = 0; i < job_count; i++) { + for (int j = 0; all_results[i] && j < jobs[i].results_count; j++) { + free(all_results[i][j].url); + free(all_results[i][j].title); + free(all_results[i][j].snippet); + } + free(all_results[i]); } +} - free(*collection); - free(*inner_counts); - - *collection = new_collection; - *inner_counts = new_inner_counts; - - (*collection)[current_count] = (char **)malloc(sizeof(char *) * 2); - if (!(*collection)[current_count]) - return current_count; - - (*collection)[current_count][0] = strdup(engine_name ? engine_name : ""); - (*collection)[current_count][1] = - strdup(warning_message ? warning_message : ""); - - if (!(*collection)[current_count][0] || !(*collection)[current_count][1]) { - free((*collection)[current_count][0]); - free((*collection)[current_count][1]); - free((*collection)[current_count]); - return current_count; +static void free_infobox_results(InfoBoxThreadData *data) { + for (int i = 0; i < HANDLER_COUNT; i++) { + if (data[i].success) + free_infobox(&data[i].result); } - - (*inner_counts)[current_count] = 2; - return current_count + 1; } static const char *warning_message_for_job(const ScrapeJob *job, const char *locale) { @@ -491,11 +469,8 @@ int results_handler(UrlParams *params) { if (!raw_query || strlen(raw_query) == 0) { send_redirect("/"); - if (has_user_pref) { - for (int i = 0; i < user_engine_count; i++) - free(user_engines[i]); - free(user_engines); - } + free(locale); + free_user_engine_list(user_engines, user_engine_count); free_context(&ctx); return -1; } @@ -528,13 +503,6 @@ int results_handler(UrlParams *params) { infobox_data[i].result = (InfoBox){NULL}; } - if (page == 1) { - for (int i = 0; i < HANDLER_COUNT; i++) { - pthread_create(&infobox_threads[i], NULL, infobox_thread_func, - &infobox_data[i]); - } - } - ScrapeJob jobs[ENGINE_COUNT]; SearchResult *all_results[ENGINE_COUNT]; @@ -592,11 +560,8 @@ int results_handler(UrlParams *params) { snprintf(response, sizeof(response), "

%s

", rate_limit_msg); send_response(response); free(request_cache_key); - if (has_user_pref) { - for (int i = 0; i < user_engine_count; i++) - free(user_engines[i]); - free(user_engines); - } + free(locale); + free_user_engine_list(user_engines, user_engine_count); free_context(&ctx); return -1; } @@ -648,13 +613,14 @@ int results_handler(UrlParams *params) { if (filter_count > 0) { context_set_array_of_arrays(&ctx, "engine_filters", filter_matrix, filter_count, filter_inner_counts); - for (int i = 0; i < filter_count; i++) { - for (int j = 0; j < LINK_FIELD_COUNT; j++) - free(filter_matrix[i][j]); - free(filter_matrix[i]); - } - free(filter_matrix); - free(filter_inner_counts); + free_string_matrix(filter_matrix, filter_inner_counts, filter_count); + } + } + + if (page == 1) { + for (int i = 0; i < HANDLER_COUNT; i++) { + pthread_create(&infobox_threads[i], NULL, infobox_thread_func, + &infobox_data[i]); } } @@ -672,27 +638,12 @@ int results_handler(UrlParams *params) { for (int i = 0; i < engine_idx; i++) { if (jobs[i].results_count > 0 && all_results[i][0].url) { char *redirect_url = strdup(all_results[i][0].url); - for (int j = 0; j < enabled_engine_count; j++) { - for (int k = 0; k < jobs[j].results_count; k++) { - free(all_results[j][k].url); - free(all_results[j][k].title); - free(all_results[j][k].snippet); - } - free(all_results[j]); - } - if (page == 1) { - for (int j = 0; j < HANDLER_COUNT; j++) { - if (infobox_data[j].success) { - free_infobox(&infobox_data[j].result); - } - } - } + free_search_results(all_results, jobs, engine_idx); + if (page == 1) + free_infobox_results(infobox_data); free(request_cache_key); - if (has_user_pref) { - for (int i = 0; i < user_engine_count; i++) - free(user_engines[i]); - free(user_engines); - } + free(locale); + free_user_engine_list(user_engines, user_engine_count); free_context(&ctx); if (redirect_url) { send_redirect(redirect_url); @@ -701,22 +652,12 @@ int results_handler(UrlParams *params) { return 0; } } - for (int i = 0; i < enabled_engine_count; i++) { - free(all_results[i]); - } - if (page == 1) { - for (int i = 0; i < HANDLER_COUNT; i++) { - if (infobox_data[i].success) { - free_infobox(&infobox_data[i].result); - } - } - } + free_search_results(all_results, jobs, engine_idx); + if (page == 1) + free_infobox_results(infobox_data); free(request_cache_key); - if (has_user_pref) { - for (int i = 0; i < user_engine_count; i++) - free(user_engines[i]); - free(user_engines); - } + free(locale); + free_user_engine_list(user_engines, user_engine_count); free_context(&ctx); char no_results_html[128]; snprintf(no_results_html, sizeof(no_results_html), "

%s

", no_results_msg); @@ -741,13 +682,7 @@ int results_handler(UrlParams *params) { if (infobox_count > 0) { context_set_array_of_arrays(&ctx, "infoboxes", infobox_matrix, infobox_count, infobox_inner_counts); - for (int i = 0; i < infobox_count; i++) { - for (int j = 0; j < INFOBOX_FIELD_COUNT; j++) - free(infobox_matrix[i][j]); - free(infobox_matrix[i]); - } - free(infobox_matrix); - free(infobox_inner_counts); + free_string_matrix(infobox_matrix, infobox_inner_counts, infobox_count); } int warning_count = 0; @@ -776,16 +711,7 @@ int results_handler(UrlParams *params) { warning_index, warning_inner_counts); } - if (warning_matrix) { - for (int i = 0; i < warning_index; i++) { - free(warning_matrix[i][0]); - free(warning_matrix[i][1]); - free(warning_matrix[i]); - } - free(warning_matrix); - } - if (warning_inner_counts) - free(warning_inner_counts); + free_string_matrix(warning_matrix, warning_inner_counts, warning_index); } int total_results = 0; @@ -794,32 +720,8 @@ int results_handler(UrlParams *params) { } if (total_results > 0) { - char ***results_matrix = (char ***)malloc(sizeof(char **) * total_results); - int *results_inner_counts = (int *)malloc(sizeof(int) * total_results); - if (!results_matrix || !results_inner_counts) { - char *html = render_template("results.html", &ctx); - if (html) { - send_response(html); - free(html); - } - for (int i = 0; i < enabled_engine_count; i++) - free(all_results[i]); - if (page == 1) { - for (int i = 0; i < HANDLER_COUNT; i++) { - if (infobox_data[i].success) { - free_infobox(&infobox_data[i].result); - } - } - } - free(request_cache_key); - if (has_user_pref) { - for (int i = 0; i < user_engine_count; i++) - free(user_engines[i]); - free(user_engines); - } - free_context(&ctx); - return 0; - } + char ***results_matrix = NULL; + int *results_inner_counts = NULL; int unique_count = 0; UrlHashTable url_table; url_hash_init(&url_table); @@ -837,37 +739,26 @@ int results_handler(UrlParams *params) { url_hash_insert(&url_table, display_url); - results_matrix[unique_count] = - (char **)malloc(sizeof(char *) * RESULT_FIELD_COUNT); - if (!results_matrix[unique_count]) { - free(all_results[i][j].url); - free(all_results[i][j].title); - free(all_results[i][j].snippet); - continue; - } char *pretty_url = pretty_display_url(display_url); char *base_url = get_base_url(display_url); - - results_matrix[unique_count][0] = strdup(display_url); - results_matrix[unique_count][1] = strdup(pretty_url); - results_matrix[unique_count][2] = all_results[i][j].title - ? strdup(all_results[i][j].title) - : strdup("Untitled"); - results_matrix[unique_count][3] = - all_results[i][j].snippet ? strdup(all_results[i][j].snippet) - : strdup(""); - results_matrix[unique_count][4] = strdup(base_url ? base_url : ""); - results_matrix[unique_count][5] = strdup(""); - - results_inner_counts[unique_count] = RESULT_FIELD_COUNT; + const char *values[RESULT_FIELD_COUNT] = { + display_url, + pretty_url, + all_results[i][j].title ? all_results[i][j].title : "Untitled", + all_results[i][j].snippet ? all_results[i][j].snippet : "", + base_url, + "", + }; + int new_count = append_string_row(&results_matrix, &results_inner_counts, + unique_count, values, + RESULT_FIELD_COUNT); free(pretty_url); free(base_url); free(all_results[i][j].url); free(all_results[i][j].title); free(all_results[i][j].snippet); - - unique_count++; + unique_count = new_count; } free(all_results[i]); } @@ -885,13 +776,7 @@ int results_handler(UrlParams *params) { if (pager_count > 0) { context_set_array_of_arrays(&ctx, "pagination_links", pager_matrix, pager_count, pager_inner_counts); - for (int i = 0; i < pager_count; i++) { - for (int j = 0; j < LINK_FIELD_COUNT; j++) - free(pager_matrix[i][j]); - free(pager_matrix[i]); - } - free(pager_matrix); - free(pager_inner_counts); + free_string_matrix(pager_matrix, pager_inner_counts, pager_count); } char *html = render_template("results.html", &ctx); @@ -900,13 +785,7 @@ int results_handler(UrlParams *params) { free(html); } - for (int i = 0; i < unique_count; i++) { - for (int j = 0; j < RESULT_FIELD_COUNT; j++) - free(results_matrix[i][j]); - free(results_matrix[i]); - } - free(results_matrix); - free(results_inner_counts); + free_string_matrix(results_matrix, results_inner_counts, unique_count); url_hash_free(&url_table); } else { char *html = render_template("results.html", &ctx); @@ -915,26 +794,15 @@ int results_handler(UrlParams *params) { free(html); } - for (int i = 0; i < enabled_engine_count; i++) { - free(all_results[i]); - } + free_search_results(all_results, jobs, engine_idx); } free(request_cache_key); - if (page == 1) { - for (int i = 0; i < HANDLER_COUNT; i++) { - if (infobox_data[i].success) { - free_infobox(&infobox_data[i].result); - } - } - } + if (page == 1) + free_infobox_results(infobox_data); free(locale); - if (has_user_pref) { - for (int i = 0; i < user_engine_count; i++) - free(user_engines[i]); - free(user_engines); - } + free_user_engine_list(user_engines, user_engine_count); free_context(&ctx); return 0; -- cgit v1.3 From f20d8f711588a1918a6d75b99b0e11d745ca8395 Mon Sep 17 00:00:00 2001 From: frosty Date: Sat, 8 Aug 2026 13:25:16 -0400 Subject: refactor: add capacity-aware template string matrices --- src/Routes/Images.c | 48 +++++++----------- src/Routes/Search.c | 123 ++++++++++++++++++++++++--------------------- src/Utility/Utility.c | 136 ++++++++++++++++++++++++++++++++++++++++---------- src/Utility/Utility.h | 15 ++++++ 4 files changed, 210 insertions(+), 112 deletions(-) (limited to 'src/Routes') diff --git a/src/Routes/Images.c b/src/Routes/Images.c index d89b732..1ab875e 100644 --- a/src/Routes/Images.c +++ b/src/Routes/Images.c @@ -41,10 +41,6 @@ static char *images_href_builder(int page, void *data) { 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) { extern Config global_config; TemplateContext ctx = new_context(); @@ -80,14 +76,12 @@ int images_handler(UrlParams *params) { if (!error_images_msg) error_images_msg = "Error fetching images"; - char ***pager_matrix = NULL; - int *pager_inner_counts = NULL; - int pager_count = - build_pagination(page, images_href_builder, (void *)raw_query, - &pager_matrix, &pager_inner_counts); - if (pager_count > 0) { - context_set_array_of_arrays(&ctx, "pagination_links", pager_matrix, - pager_count, pager_inner_counts); + StringMatrix pagination; + string_matrix_build_pagination(page, images_href_builder, (void *)raw_query, + &pagination); + if (pagination.count > 0) { + context_set_array_of_arrays(&ctx, "pagination_links", pagination.rows, + pagination.count, pagination.field_counts); } char *display_query = url_decode_query(raw_query); @@ -95,7 +89,7 @@ int images_handler(UrlParams *params) { if (!raw_query || strlen(raw_query) == 0) { send_redirect("/"); - free_pagination(pager_matrix, pager_inner_counts, pager_count); + string_matrix_free(&pagination); free(display_query); free(locale); free_context(&ctx); @@ -133,7 +127,7 @@ int images_handler(UrlParams *params) { snprintf(response, sizeof(response), "

%s

", rate_limit_msg); send_response(response); free(request_cache_key); - free_pagination(pager_matrix, pager_inner_counts, pager_count); + string_matrix_free(&pagination); free(display_query); free(locale); free_context(&ctx); @@ -154,16 +148,15 @@ int images_handler(UrlParams *params) { snprintf(error_html, sizeof(error_html), "

%s

", error_images_msg); send_response(error_html); free(request_cache_key); - free_pagination(pager_matrix, pager_inner_counts, pager_count); + string_matrix_free(&pagination); free(display_query); free(locale); free_context(&ctx); return -1; } - char ***image_matrix = NULL; - int *inner_counts = NULL; - int image_count = 0; + StringMatrix images; + string_matrix_init(&images); for (int i = 0; i < result_count; i++) { const char *values[IMAGE_RESULT_FIELDS] = { @@ -172,12 +165,9 @@ int images_handler(UrlParams *params) { results[i].page_url, results[i].full_url, }; - int new_count = append_string_row(&image_matrix, &inner_counts, - image_count, values, - IMAGE_RESULT_FIELDS); - if (new_count == image_count) { - free_string_matrix(image_matrix, inner_counts, image_count); - free_pagination(pager_matrix, pager_inner_counts, pager_count); + if (string_matrix_append(&images, values, IMAGE_RESULT_FIELDS) != 0) { + string_matrix_free(&images); + string_matrix_free(&pagination); free_image_results(results, result_count); free(request_cache_key); free(display_query); @@ -185,11 +175,10 @@ int images_handler(UrlParams *params) { free_context(&ctx); return -1; } - image_count = new_count; } - context_set_array_of_arrays(&ctx, "images", image_matrix, image_count, - inner_counts); + context_set_array_of_arrays(&ctx, "images", images.rows, images.count, + images.field_counts); char *rendered = render_template("images.html", &ctx); if (rendered) { @@ -199,9 +188,8 @@ int images_handler(UrlParams *params) { send_response("

Error rendering image results

"); } - free_string_matrix(image_matrix, inner_counts, image_count); - - free_pagination(pager_matrix, pager_inner_counts, pager_count); + string_matrix_free(&images); + string_matrix_free(&pagination); free_image_results(results, result_count); free(request_cache_key); diff --git a/src/Routes/Search.c b/src/Routes/Search.c index 53718b3..abc2e73 100644 --- a/src/Routes/Search.c +++ b/src/Routes/Search.c @@ -140,29 +140,31 @@ static InfoBox fetch_currency_wrapper(char *query) { return fetch_currency_data(query); } 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, "://"); - if (protocol_pos) { - start = protocol_pos + 3; - } + const char *protocol_pos = strstr(input, "://"); + if (protocol_pos) { + start = protocol_pos + 3; + } - const char *end = start; - while (*end && *end != '/' && *end != '?' && *end != '#') { - end++; - } + const char *end = start; + while (*end && *end != '/' && *end != '?' && *end != '#') { + end++; + } - size_t len = end - start; + size_t len = end - start; - char *domain = (char *)malloc(len + 1); - if (!domain) return NULL; + char *domain = (char *)malloc(len + 1); + if (!domain) + return NULL; - strncpy(domain, start, len); - domain[len] = '\0'; + strncpy(domain, start, len); + domain[len] = '\0'; - return domain; + return domain; } static int is_calculator_query(const char *query) { if (!query) @@ -252,11 +254,8 @@ static void *infobox_thread_func(void *arg) { static int add_infobox_to_collection(InfoBox *infobox, char ****collection, int **inner_counts, int current_count) { const char *values[INFOBOX_FIELD_COUNT] = { - infobox->title, - infobox->thumbnail_url, - infobox->extract, - infobox->url, - infobox->url, + infobox->title, infobox->thumbnail_url, infobox->extract, + infobox->url, infobox->url, }; return append_string_row(collection, inner_counts, current_count, values, INFOBOX_FIELD_COUNT); @@ -267,8 +266,7 @@ static int add_warning_to_collection(const char *engine_name, char ****collection, int **inner_counts, int current_count) { const char *values[] = {engine_name, warning_message}; - return append_string_row(collection, inner_counts, current_count, values, - 2); + return append_string_row(collection, inner_counts, current_count, values, 2); } 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) { case SCRAPE_STATUS_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: { 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: { 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: 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, int page) { const char *safe_query = query ? query : ""; - int use_engine = engine_id && engine_id[0] != '\0' && - !engine_id_matches(engine_id, "all"); + int use_engine = + engine_id && engine_id[0] != '\0' && !engine_id_matches(engine_id, "all"); size_t needed = strlen("/search?q=") + strlen(safe_query) + 1; if (use_engine) @@ -421,7 +425,8 @@ int results_handler(UrlParams *params) { char **user_engines = NULL; 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) { for (int i = 0; i < params->count; i++) { @@ -449,9 +454,11 @@ int results_handler(UrlParams *params) { beaker_set_locale(&ctx, locale); 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"); - if (!no_results_msg) no_results_msg = "No results found"; + if (!no_results_msg) + no_results_msg = "No results found"; char page_str[16]; snprintf(page_str, sizeof(page_str), "%d", page); @@ -604,9 +611,9 @@ int results_handler(UrlParams *params) { ? "engine-filter active" : "engine-filter"; - filter_count = add_link_to_collection(filter_href, ENGINE_REGISTRY[i].name, - filter_class, &filter_matrix, - &filter_inner_counts, filter_count); + filter_count = add_link_to_collection( + filter_href, ENGINE_REGISTRY[i].name, filter_class, &filter_matrix, + &filter_inner_counts, filter_count); free(filter_href); } @@ -660,11 +667,12 @@ int results_handler(UrlParams *params) { free_user_engine_list(user_engines, user_engine_count); free_context(&ctx); char no_results_html[128]; - snprintf(no_results_html, sizeof(no_results_html), "

%s

", no_results_msg); + snprintf(no_results_html, sizeof(no_results_html), "

%s

", + no_results_msg); send_response(no_results_html); return 0; } - + char ***infobox_matrix = NULL; int *infobox_inner_counts = NULL; int infobox_count = 0; @@ -720,9 +728,8 @@ int results_handler(UrlParams *params) { } if (total_results > 0) { - char ***results_matrix = NULL; - int *results_inner_counts = NULL; - int unique_count = 0; + StringMatrix results_matrix; + string_matrix_init(&results_matrix); UrlHashTable url_table; url_hash_init(&url_table); @@ -749,34 +756,35 @@ int results_handler(UrlParams *params) { base_url, "", }; - int new_count = append_string_row(&results_matrix, &results_inner_counts, - unique_count, values, - RESULT_FIELD_COUNT); + int append_result = + string_matrix_append(&results_matrix, values, RESULT_FIELD_COUNT); free(pretty_url); free(base_url); free(all_results[i][j].url); free(all_results[i][j].title); free(all_results[i][j].snippet); - unique_count = new_count; + if (append_result != 0) { + continue; + } } free(all_results[i]); } - context_set_array_of_arrays(&ctx, "results", results_matrix, unique_count, - results_inner_counts); + context_set_array_of_arrays(&ctx, "results", results_matrix.rows, + results_matrix.count, + results_matrix.field_counts); - char ***pager_matrix = NULL; - int *pager_inner_counts = NULL; - SearchHrefData href_data = { .query = raw_query, .engine_id = selected_engine_id }; - int pager_count = build_pagination(page, search_href_builder, - &href_data, &pager_matrix, - &pager_inner_counts); + StringMatrix pager_matrix; + SearchHrefData href_data = {.query = raw_query, + .engine_id = selected_engine_id}; + int pagination_result = string_matrix_build_pagination( + page, search_href_builder, &href_data, &pager_matrix); - if (pager_count > 0) { - context_set_array_of_arrays(&ctx, "pagination_links", pager_matrix, - pager_count, pager_inner_counts); - free_string_matrix(pager_matrix, pager_inner_counts, pager_count); + if (pagination_result == 0 && pager_matrix.count > 0) { + context_set_array_of_arrays(&ctx, "pagination_links", pager_matrix.rows, + pager_matrix.count, + pager_matrix.field_counts); } char *html = render_template("results.html", &ctx); @@ -785,7 +793,8 @@ int results_handler(UrlParams *params) { 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); } else { char *html = render_template("results.html", &ctx); diff --git a/src/Utility/Utility.c b/src/Utility/Utility.c index 9c05434..97a8d15 100644 --- a/src/Utility/Utility.c +++ b/src/Utility/Utility.c @@ -263,6 +263,67 @@ void free_string_matrix(char ***matrix, int *inner_counts, int row_count) { 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, const char *class_name, char ****collection, 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), void *data, char ****out_matrix, int **out_inner_counts) { - enum { PAGER_WINDOW_SIZE = 5 }; + if (!out_matrix || !out_inner_counts) + return 0; - *out_matrix = NULL; - *out_inner_counts = NULL; - int count = 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 }; + if (!out_matrix || !href_builder) + return -1; + string_matrix_init(out_matrix); int pager_start = page <= 3 ? 1 : page - 2; int pager_end = pager_start + PAGER_WINDOW_SIZE - 1; - if (page > 1) { - char *href = href_builder(page - 1, data); - count = add_link_to_collection(href, "←", "pagination-btn prev", out_matrix, - out_inner_counts, count); - free(href); - } + for (int i = page > 1 ? pager_start - 1 : pager_start; i <= pager_end + 1; + i++) { + const char *label = NULL; + 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"; + } - 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); + 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); + if (result != 0) { + string_matrix_free(out_matrix); + return -1; + } } - - 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; + return 0; } diff --git a/src/Utility/Utility.h b/src/Utility/Utility.h index 9fca8b6..663f2c4 100644 --- a/src/Utility/Utility.h +++ b/src/Utility/Utility.h @@ -13,6 +13,13 @@ #define LINK_FIELD_COUNT 3 +typedef struct { + char ***rows; + int *field_counts; + int count; + int capacity; +} StringMatrix; + int hex_to_int(char c); char *get_theme(const char *default_theme); 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); 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, const char *class_name, char ****collection, int **inner_counts, int current_count); int build_pagination(int page, char *(*href_builder)(int page, void *data), 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 -- cgit v1.3 From 10d44e7ff547a806e4bf109e7aadd48488ddc9f0 Mon Sep 17 00:00:00 2001 From: frosty Date: Wed, 12 Aug 2026 15:40:41 -0400 Subject: fix: improvements allocation safety and string processing --- src/Config.c | 66 +++++++++++++++---------------------- src/Routes/Search.c | 77 ++++++++++++++++++++----------------------- src/Scraping/Scraping.c | 14 +++++--- src/Utility/Utility.c | 88 +++++++++---------------------------------------- src/Utility/Utility.h | 8 ----- 5 files changed, 87 insertions(+), 166 deletions(-) (limited to 'src/Routes') diff --git a/src/Config.c b/src/Config.c index bde76fe..3d2790b 100644 --- a/src/Config.c +++ b/src/Config.c @@ -3,6 +3,17 @@ #include #include +static char *trim(char *value) { + while (*value == ' ' || *value == '\t') + value++; + + char *end = value + strlen(value); + while (end > value && (end[-1] == ' ' || end[-1] == '\t')) + end--; + *end = '\0'; + return value; +} + int load_config(const char *filename, Config *config) { FILE *file = fopen(filename, "r"); if (!file) { @@ -33,51 +44,31 @@ int load_config(const char *filename, Config *config) { char *delimiter = strchr(line, '='); if (delimiter) { *delimiter = '\0'; - char *key = line; - char *value = delimiter + 1; - - while (*key == ' ' || *key == '\t') - key++; - while (*value == ' ' || *value == '\t') - value++; + char *key = trim(line); + char *value = trim(delimiter + 1); - char *key_end = key + strlen(key) - 1; - while (key_end > key && (*key_end == ' ' || *key_end == '\t')) { - *key_end = '\0'; - key_end--; - } - - char *value_end = value + strlen(value) - 1; - while (value_end > value && (*value_end == ' ' || *value_end == '\t' || - *value_end == '"' || *value_end == '\'')) { - *value_end = '\0'; - value_end--; - } - - while (*value == ' ' || *value == '\t') - value++; + char *value_end = value + strlen(value); + while (value_end > value && + (value_end[-1] == '"' || value_end[-1] == '\'')) + *--value_end = '\0'; while (*value == '"' || *value == '\'') value++; if (strcmp(section, "server") == 0) { if (strcmp(key, "host") == 0) { - strncpy(config->host, value, sizeof(config->host) - 1); - config->host[sizeof(config->host) - 1] = '\0'; + snprintf(config->host, sizeof(config->host), "%s", value); } else if (strcmp(key, "port") == 0) { config->port = atoi(value); } else if (strcmp(key, "locale") == 0) { - strncpy(config->default_locale, value, - sizeof(config->default_locale) - 1); - config->default_locale[sizeof(config->default_locale) - 1] = '\0'; + snprintf(config->default_locale, sizeof(config->default_locale), "%s", + value); } } else if (strcmp(section, "proxy") == 0) { if (strcmp(key, "proxy") == 0) { - strncpy(config->proxy, value, sizeof(config->proxy) - 1); - config->proxy[sizeof(config->proxy) - 1] = '\0'; + snprintf(config->proxy, sizeof(config->proxy), "%s", value); } else if (strcmp(key, "list_file") == 0) { - strncpy(config->proxy_list_file, value, - sizeof(config->proxy_list_file) - 1); - config->proxy_list_file[sizeof(config->proxy_list_file) - 1] = '\0'; + snprintf(config->proxy_list_file, sizeof(config->proxy_list_file), + "%s", value); } else if (strcmp(key, "max_retries") == 0) { config->max_proxy_retries = atoi(value); } else if (strcmp(key, "randomize_username") == 0) { @@ -87,8 +78,7 @@ int load_config(const char *filename, Config *config) { } } else if (strcmp(section, "cache") == 0) { if (strcmp(key, "dir") == 0) { - strncpy(config->cache_dir, value, sizeof(config->cache_dir) - 1); - config->cache_dir[sizeof(config->cache_dir) - 1] = '\0'; + snprintf(config->cache_dir, sizeof(config->cache_dir), "%s", value); } else if (strcmp(key, "ttl_search") == 0) { config->cache_ttl_search = atoi(value); } else if (strcmp(key, "ttl_infobox") == 0) { @@ -98,12 +88,10 @@ int load_config(const char *filename, Config *config) { } } else if (strcmp(section, "engines") == 0) { if (strcmp(key, "engines") == 0) { - strncpy(config->engines, value, sizeof(config->engines) - 1); - config->engines[sizeof(config->engines) - 1] = '\0'; + snprintf(config->engines, sizeof(config->engines), "%s", value); } else if (strcmp(key, "yacy_instance") == 0) { - strncpy(config->yacy_instance, value, - sizeof(config->yacy_instance) - 1); - config->yacy_instance[sizeof(config->yacy_instance) - 1] = '\0'; + snprintf(config->yacy_instance, sizeof(config->yacy_instance), "%s", + value); } } else if (strcmp(section, "rate_limit") == 0) { if (strcmp(key, "search_requests") == 0) { diff --git a/src/Routes/Search.c b/src/Routes/Search.c index abc2e73..a245f6b 100644 --- a/src/Routes/Search.c +++ b/src/Routes/Search.c @@ -251,22 +251,20 @@ static void *infobox_thread_func(void *arg) { return NULL; } -static int add_infobox_to_collection(InfoBox *infobox, char ****collection, - int **inner_counts, int current_count) { +static int add_infobox_to_collection(InfoBox *infobox, + StringMatrix *collection) { const char *values[INFOBOX_FIELD_COUNT] = { infobox->title, infobox->thumbnail_url, infobox->extract, infobox->url, infobox->url, }; - return append_string_row(collection, inner_counts, current_count, values, - INFOBOX_FIELD_COUNT); + return string_matrix_append(collection, values, INFOBOX_FIELD_COUNT); } static int add_warning_to_collection(const char *engine_name, const char *warning_message, - char ****collection, int **inner_counts, - int current_count) { + StringMatrix *collection) { const char *values[] = {engine_name, warning_message}; - return append_string_row(collection, inner_counts, current_count, values, 2); + return string_matrix_append(collection, values, 2); } static void free_user_engine_list(char **user_engines, int user_engine_count) { @@ -587,15 +585,14 @@ int results_handler(UrlParams *params) { } if (filter_engine_count > 1) { - char ***filter_matrix = NULL; - int *filter_inner_counts = NULL; - int filter_count = 0; + StringMatrix filter_matrix; + string_matrix_init(&filter_matrix); char *all_href = build_search_href(raw_query, "all", 1); - - filter_count = add_link_to_collection( + const char *all_values[LINK_FIELD_COUNT] = { all_href, "All", - selected_engine ? "engine-filter" : "engine-filter active", - &filter_matrix, &filter_inner_counts, filter_count); + selected_engine ? "engine-filter" : "engine-filter active"}; + + string_matrix_append(&filter_matrix, all_values, LINK_FIELD_COUNT); free(all_href); for (int i = 0; i < ENGINE_COUNT; i++) { @@ -611,17 +608,18 @@ int results_handler(UrlParams *params) { ? "engine-filter active" : "engine-filter"; - filter_count = add_link_to_collection( - filter_href, ENGINE_REGISTRY[i].name, filter_class, &filter_matrix, - &filter_inner_counts, filter_count); + const char *filter_values[LINK_FIELD_COUNT] = { + filter_href, ENGINE_REGISTRY[i].name, filter_class}; + string_matrix_append(&filter_matrix, filter_values, LINK_FIELD_COUNT); free(filter_href); } - if (filter_count > 0) { - context_set_array_of_arrays(&ctx, "engine_filters", filter_matrix, - filter_count, filter_inner_counts); - free_string_matrix(filter_matrix, filter_inner_counts, filter_count); + if (filter_matrix.count > 0) { + context_set_array_of_arrays(&ctx, "engine_filters", filter_matrix.rows, + filter_matrix.count, + filter_matrix.field_counts); } + string_matrix_free(&filter_matrix); } if (page == 1) { @@ -673,25 +671,23 @@ int results_handler(UrlParams *params) { return 0; } - char ***infobox_matrix = NULL; - int *infobox_inner_counts = NULL; - int infobox_count = 0; + StringMatrix infobox_matrix; + string_matrix_init(&infobox_matrix); if (page == 1) { for (int i = 0; i < HANDLER_COUNT; i++) { if (infobox_data[i].success) { - infobox_count = - add_infobox_to_collection(&infobox_data[i].result, &infobox_matrix, - &infobox_inner_counts, infobox_count); + add_infobox_to_collection(&infobox_data[i].result, &infobox_matrix); } } } - if (infobox_count > 0) { - context_set_array_of_arrays(&ctx, "infoboxes", infobox_matrix, - infobox_count, infobox_inner_counts); - free_string_matrix(infobox_matrix, infobox_inner_counts, infobox_count); + if (infobox_matrix.count > 0) { + context_set_array_of_arrays(&ctx, "infoboxes", infobox_matrix.rows, + infobox_matrix.count, + infobox_matrix.field_counts); } + string_matrix_free(&infobox_matrix); int warning_count = 0; for (int i = 0; i < enabled_engine_count; i++) { @@ -700,26 +696,25 @@ int results_handler(UrlParams *params) { } if (warning_count > 0) { - char ***warning_matrix = NULL; - int *warning_inner_counts = NULL; - int warning_index = 0; + StringMatrix warning_matrix; + string_matrix_init(&warning_matrix); for (int i = 0; i < enabled_engine_count; i++) { const char *warning_message = warning_message_for_job(&jobs[i], locale); if (!warning_message) continue; - warning_index = add_warning_to_collection( - jobs[i].engine->name, warning_message, &warning_matrix, - &warning_inner_counts, warning_index); + add_warning_to_collection(jobs[i].engine->name, warning_message, + &warning_matrix); } - if (warning_index > 0) { - context_set_array_of_arrays(&ctx, "engine_warnings", warning_matrix, - warning_index, warning_inner_counts); + if (warning_matrix.count > 0) { + context_set_array_of_arrays(&ctx, "engine_warnings", warning_matrix.rows, + warning_matrix.count, + warning_matrix.field_counts); } - free_string_matrix(warning_matrix, warning_inner_counts, warning_index); + string_matrix_free(&warning_matrix); } int total_results = 0; diff --git a/src/Scraping/Scraping.c b/src/Scraping/Scraping.c index 5c51ca5..9c4ff57 100644 --- a/src/Scraping/Scraping.c +++ b/src/Scraping/Scraping.c @@ -214,13 +214,17 @@ int setup_job(ScrapeJob *job, CURLM *multi_handle) { return -1; } - for (char *p = encoded_query + strlen(encoded_query) - 3; p >= encoded_query; - p--) { - if (p[0] == '%' && p[1] == '2' && p[2] == '0') { - *p = '+'; - memmove(p + 1, p + 3, strlen(p + 3) + 1); + char *read = encoded_query; + char *write = encoded_query; + while (*read) { + if (read[0] == '%' && read[1] == '2' && read[2] == '0') { + *write++ = '+'; + read += 3; + } else { + *write++ = *read++; } } + *write = '\0'; char *full_url = build_search_url(job->engine->base_url, job->engine->page_param, diff --git a/src/Utility/Utility.c b/src/Utility/Utility.c index 97a8d15..5183efe 100644 --- a/src/Utility/Utility.c +++ b/src/Utility/Utility.c @@ -14,7 +14,6 @@ static int themes_initialized = 0; void init_themes(const char *static_path) { if (themes_initialized) return; - themes_initialized = 1; char themes_dir[512]; snprintf(themes_dir, sizeof(themes_dir), "%s/themes", static_path); @@ -26,20 +25,32 @@ void init_themes(const char *static_path) { struct dirent *entry; int capacity = 4; themes_list = malloc(sizeof(char *) * capacity); + if (!themes_list) { + closedir(dir); + return; + } themes_count = 0; while ((entry = readdir(dir)) != NULL) { size_t len = strlen(entry->d_name); if (len > 4 && strcmp(entry->d_name + len - 4, ".css") == 0) { if (themes_count >= capacity) { - capacity *= 2; - themes_list = realloc(themes_list, sizeof(char *) * capacity); + int new_capacity = capacity * 2; + char **new_list = realloc(themes_list, sizeof(char *) * new_capacity); + if (!new_list) + break; + themes_list = new_list; + capacity = new_capacity; } - themes_list[themes_count] = strndup(entry->d_name, len - 4); + char *theme = strndup(entry->d_name, len - 4); + if (!theme) + break; + themes_list[themes_count] = theme; themes_count++; } } closedir(dir); + themes_initialized = 1; for (int i = 0; i < themes_count; i++) { for (int j = i + 1; j < themes_count; j++) { @@ -206,50 +217,6 @@ int user_engines_contains(const char *engine_id, char **ids, int count) { return 0; } -int append_string_row(char ****matrix, int **inner_counts, int row_count, - const char *const *values, int field_count) { - if (!matrix || !inner_counts || !values || field_count <= 0) - return row_count; - - char **row = calloc((size_t)field_count, sizeof(*row)); - if (!row) - return row_count; - - 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 row_count; - } - } - - char ***new_matrix = malloc(sizeof(*new_matrix) * (row_count + 1)); - int *new_counts = malloc(sizeof(*new_counts) * (row_count + 1)); - if (!new_matrix || !new_counts) { - free(new_matrix); - free(new_counts); - for (int i = 0; i < field_count; i++) - free(row[i]); - free(row); - return row_count; - } - - if (row_count > 0) { - memcpy(new_matrix, *matrix, sizeof(*new_matrix) * row_count); - memcpy(new_counts, *inner_counts, sizeof(*new_counts) * row_count); - } - - new_matrix[row_count] = row; - new_counts[row_count] = field_count; - free(*matrix); - free(*inner_counts); - *matrix = new_matrix; - *inner_counts = new_counts; - return row_count + 1; -} - void free_string_matrix(char ***matrix, int *inner_counts, int row_count) { if (matrix) { for (int i = 0; i < row_count; i++) { @@ -324,31 +291,6 @@ void string_matrix_free(StringMatrix *matrix) { *matrix = (StringMatrix){0}; } -int add_link_to_collection(const char *href, const char *label, - const char *class_name, char ****collection, - int **inner_counts, int current_count) { - const char *values[LINK_FIELD_COUNT] = {href, label, class_name}; - return append_string_row(collection, inner_counts, current_count, values, - LINK_FIELD_COUNT); -} - -int build_pagination(int page, char *(*href_builder)(int page, void *data), - 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) { diff --git a/src/Utility/Utility.h b/src/Utility/Utility.h index 663f2c4..a78fe65 100644 --- a/src/Utility/Utility.h +++ b/src/Utility/Utility.h @@ -31,8 +31,6 @@ int is_engine_id_enabled(const char *engine_id); int get_user_engines(char ***out_ids, int *out_count); int user_engines_contains(const char *engine_id, char **ids, int count); -int append_string_row(char ****matrix, int **inner_counts, int row_count, - const char *const *values, int field_count); void free_string_matrix(char ***matrix, int *inner_counts, int row_count); void string_matrix_init(StringMatrix *matrix); @@ -40,12 +38,6 @@ 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, - const char *class_name, char ****collection, - int **inner_counts, int current_count); - -int build_pagination(int page, char *(*href_builder)(int page, void *data), - 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); -- cgit v1.3 From a20a9e8878ab6708bd3e3ecb4b1cee8728436dd0 Mon Sep 17 00:00:00 2001 From: frosty Date: Wed, 12 Aug 2026 20:21:12 -0400 Subject: feat: add default search engine option --- locales/en_gb.ini | 4 ++++ locales/en_us.ini | 4 ++++ src/Routes/Search.c | 22 ++++++++++++++++++---- src/Routes/Settings.c | 16 ++++++++++++++-- src/Routes/SettingsSave.c | 30 ++++++++++++++++++++++++++++++ src/Utility/Utility.c | 9 +++++++++ src/Utility/Utility.h | 1 + templates/settings.html | 17 ++++++++++++++++- 8 files changed, 96 insertions(+), 7 deletions(-) (limited to 'src/Routes') diff --git a/locales/en_gb.ini b/locales/en_gb.ini index db919a3..cdb5f57 100644 --- a/locales/en_gb.ini +++ b/locales/en_gb.ini @@ -21,6 +21,10 @@ display_language_label = "Display Language" language_desc = "Choose your preferred language." engines_label = "Search Engines" engines_desc = "Choose which search engines to use. Only engines enabled on the server are shown." +other_label = "Other Settings" +other_desc = "Choose additional settings." +default_engine_label = "Default Search Engine" +all_engines_option = "All enabled engines" save_settings_button = "Save Settings" no_results = "No results found" error_images = "Error fetching images" diff --git a/locales/en_us.ini b/locales/en_us.ini index 422f220..e83aa80 100644 --- a/locales/en_us.ini +++ b/locales/en_us.ini @@ -21,6 +21,10 @@ display_language_label = "Display Language" language_desc = "Choose your preferred language." engines_label = "Search Engines" engines_desc = "Choose which search engines to use. Only engines enabled on the server are shown." +other_label = "Other Settings" +other_desc = "Choose additional settings." +default_engine_label = "Default Search Engine" +all_engines_option = "All enabled engines" save_settings_button = "Save Settings" no_results = "No results found" error_images = "Error fetching images" diff --git a/src/Routes/Search.c b/src/Routes/Search.c index a245f6b..e7703be 100644 --- a/src/Routes/Search.c +++ b/src/Routes/Search.c @@ -417,7 +417,11 @@ int results_handler(UrlParams *params) { extern Config global_config; TemplateContext ctx = new_context(); char *raw_query = ""; - const char *selected_engine_id = "all"; + char selected_engine_id[32]; + char *default_engine = get_default_search_engine(); + snprintf(selected_engine_id, sizeof(selected_engine_id), "%s", + default_engine); + free(default_engine); int page = 1; int btnI = 0; @@ -435,7 +439,8 @@ int results_handler(UrlParams *params) { if (parsed > 1) page = parsed; } else if (strcmp(params->params[i].key, "engine") == 0) { - selected_engine_id = params->params[i].value; + snprintf(selected_engine_id, sizeof(selected_engine_id), "%s", + params->params[i].value); } else if (strcmp(params->params[i].key, "btnI") == 0) { btnI = atoi(params->params[i].value); } @@ -481,8 +486,17 @@ int results_handler(UrlParams *params) { } const SearchEngine *selected_engine = find_enabled_engine(selected_engine_id); - if (!selected_engine) - selected_engine_id = "all"; + if (selected_engine && + !engine_allowed_for_user(selected_engine, user_engines, + user_engine_count, has_user_pref)) { + selected_engine = NULL; + } + if (selected_engine) { + snprintf(selected_engine_id, sizeof(selected_engine_id), "%s", + selected_engine->id); + } else { + snprintf(selected_engine_id, sizeof(selected_engine_id), "all"); + } context_set(&ctx, "selected_engine", selected_engine_id); char *search_href = build_search_href(raw_query, selected_engine_id, 1); diff --git a/src/Routes/Settings.c b/src/Routes/Settings.c index eb3072b..05325e8 100644 --- a/src/Routes/Settings.c +++ b/src/Routes/Settings.c @@ -34,6 +34,14 @@ int settings_handler(UrlParams *params) { int user_engine_count = 0; int has_user_pref = (get_user_engines(&user_engines, &user_engine_count) == 0); + char *default_engine = get_default_search_engine(); + + if (strcmp(default_engine, "all") != 0 && has_user_pref && + !user_engines_contains(default_engine, user_engines, + user_engine_count)) { + free(default_engine); + default_engine = strdup("all"); + } int enabled_count = 0; for (int i = 0; i < ENGINE_COUNT; i++) { @@ -63,11 +71,13 @@ int settings_handler(UrlParams *params) { } } - engine_data[idx] = malloc(sizeof(char *) * 3); + engine_data[idx] = malloc(sizeof(char *) * 4); engine_data[idx][0] = (char *)ENGINE_REGISTRY[i].id; engine_data[idx][1] = (char *)ENGINE_REGISTRY[i].name; engine_data[idx][2] = is_selected ? "checked" : ""; - engine_inner[idx] = 3; + engine_data[idx][3] = + strcmp(default_engine, ENGINE_REGISTRY[i].id) == 0 ? "selected" : ""; + engine_inner[idx] = 4; idx++; } @@ -78,6 +88,7 @@ int settings_handler(UrlParams *params) { context_set(&ctx, "query", query); context_set(&ctx, "theme", theme); context_set(&ctx, "locale", locale); + context_set(&ctx, "default_engine", default_engine); context_set_array_of_arrays(&ctx, "locales", locale_data, locale_count, inner_counts); @@ -132,6 +143,7 @@ int settings_handler(UrlParams *params) { send_response(rendered_html); free(rendered_html); + free(default_engine); free(theme); free(locale); free_context(&ctx); diff --git a/src/Routes/SettingsSave.c b/src/Routes/SettingsSave.c index cacff50..3cbb1d6 100644 --- a/src/Routes/SettingsSave.c +++ b/src/Routes/SettingsSave.c @@ -10,6 +10,8 @@ int settings_save_handler(UrlParams *params) { const char *theme = ""; const char *locale = ""; const char *query = ""; + const char *default_engine = "all"; + int default_engine_present = 0; int engines_present = 0; char selected_ids[ENGINE_COUNT][32]; int selected_count = 0; @@ -22,6 +24,9 @@ int settings_save_handler(UrlParams *params) { locale = params->params[i].value; } else if (strcmp(params->params[i].key, "q") == 0) { query = params->params[i].value; + } else if (strcmp(params->params[i].key, "default_engine") == 0) { + default_engine = params->params[i].value; + default_engine_present = 1; } else if (strcmp(params->params[i].key, "engines_present") == 0) { engines_present = 1; } else if (strncmp(params->params[i].key, "engine_", 7) == 0 && @@ -58,6 +63,31 @@ int settings_save_handler(UrlParams *params) { false, false); } + if (default_engine_present) { + const char *validated_default = "all"; + + for (int i = 0; i < ENGINE_COUNT; i++) { + if (!ENGINE_REGISTRY[i].enabled || + strcmp(default_engine, ENGINE_REGISTRY[i].id) != 0) + continue; + + int selected = !engines_present; + for (int j = 0; j < selected_count; j++) { + if (strcmp(selected_ids[j], ENGINE_REGISTRY[i].id) == 0) { + selected = 1; + break; + } + } + + if (selected) + validated_default = ENGINE_REGISTRY[i].id; + break; + } + + set_cookie("default_engine", validated_default, + "Fri, 31 Dec 2038 23:59:59 GMT", "/", false, false); + } + char redirect_url[512]; snprintf(redirect_url, sizeof(redirect_url), "/settings?q=%s", query); send_redirect(redirect_url); diff --git a/src/Utility/Utility.c b/src/Utility/Utility.c index 5183efe..35fc491 100644 --- a/src/Utility/Utility.c +++ b/src/Utility/Utility.c @@ -217,6 +217,15 @@ int user_engines_contains(const char *engine_id, char **ids, int count) { return 0; } +char *get_default_search_engine(void) { + char *cookie = get_cookie("default_engine"); + if (cookie && (strcmp(cookie, "all") == 0 || is_engine_id_enabled(cookie))) + return cookie; + + free(cookie); + return strdup("all"); +} + void free_string_matrix(char ***matrix, int *inner_counts, int row_count) { if (matrix) { for (int i = 0; i < row_count; i++) { diff --git a/src/Utility/Utility.h b/src/Utility/Utility.h index a78fe65..a194f21 100644 --- a/src/Utility/Utility.h +++ b/src/Utility/Utility.h @@ -30,6 +30,7 @@ char *get_locale(const char *default_locale); int is_engine_id_enabled(const char *engine_id); int get_user_engines(char ***out_ids, int *out_count); int user_engines_contains(const char *engine_id, char **ids, int count); +char *get_default_search_engine(void); void free_string_matrix(char ***matrix, int *inner_counts, int row_count); diff --git a/templates/settings.html b/templates/settings.html index 2f4ecb6..ecc2936 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -87,6 +87,21 @@ {{endfor}} +
+

{{l("other_label")}}

+

{{l("other_desc")}}

+
+ + +
+
{{endif}}
@@ -96,4 +111,4 @@
- \ No newline at end of file + -- cgit v1.3 From 68d9d46e5ee5eeefca8630ee4c26f7794a8842a4 Mon Sep 17 00:00:00 2001 From: frosty Date: Sat, 15 Aug 2026 02:57:29 -0400 Subject: fix: improve memory handling in search cleanup paths --- src/Routes/Search.c | 3 +- src/Scraping/Scraping.c | 85 ++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 67 insertions(+), 21 deletions(-) (limited to 'src/Routes') diff --git a/src/Routes/Search.c b/src/Routes/Search.c index e7703be..7fcdac8 100644 --- a/src/Routes/Search.c +++ b/src/Routes/Search.c @@ -287,8 +287,7 @@ static void free_search_results(SearchResult **all_results, ScrapeJob *jobs, static void free_infobox_results(InfoBoxThreadData *data) { for (int i = 0; i < HANDLER_COUNT; i++) { - if (data[i].success) - free_infobox(&data[i].result); + free_infobox(&data[i].result); } } diff --git a/src/Scraping/Scraping.c b/src/Scraping/Scraping.c index 9c4ff57..6d94450 100644 --- a/src/Scraping/Scraping.c +++ b/src/Scraping/Scraping.c @@ -1,6 +1,7 @@ #include "Scraping.h" #include "../Cache/Cache.h" #include "../Proxy/Proxy.h" +#include "../Utility/XmlHelper.h" #include "Config.h" #include #include @@ -170,14 +171,26 @@ void parse_and_cache_response(ScrapeJob *job) { } } -void cleanup_job_handle(ScrapeJob *job, CURL *handle) { +static void cleanup_job_results(ScrapeJob *job) { + if (!job || !job->out_results) + return; + + xml_result_free(*job->out_results, job->results_count); + *job->out_results = NULL; + job->results_count = 0; +} + +static void cleanup_job_handle(ScrapeJob *job, CURL *handle) { struct curl_slist *headers = NULL; - curl_easy_getinfo(handle, CURLINFO_PRIVATE, &headers); + if (handle) + curl_easy_getinfo(handle, CURLINFO_PRIVATE, &headers); if (headers) curl_slist_free_all(headers); free(job->response.memory); job->response.memory = NULL; + job->response.size = 0; + job->response.capacity = 0; } void process_response(ScrapeJob *job, CURL *handle, CURLMsg *msg) { @@ -194,19 +207,25 @@ void process_response(ScrapeJob *job, CURL *handle, CURLMsg *msg) { } int setup_job(ScrapeJob *job, CURLM *multi_handle) { - if (job->handle) + if (job->handle) { + cleanup_job_handle(job, job->handle); curl_easy_cleanup(job->handle); - if (job->response.memory) + job->handle = NULL; + } else if (job->response.memory) { free(job->response.memory); + job->response.memory = NULL; + job->response.size = 0; + job->response.capacity = 0; + } - job->results_count = 0; + cleanup_job_results(job); job->http_status = 0; job->status = SCRAPE_STATUS_PENDING; - if (check_cache_for_job(job)) { - job->results_count = job->results_count > 0 ? job->results_count : 0; + if (check_cache_for_job(job)) return 0; - } + + cleanup_job_results(job); char *encoded_query = curl_easy_escape(NULL, job->query, 0); if (!encoded_query) { @@ -247,6 +266,14 @@ int setup_job(ScrapeJob *job, CURLM *multi_handle) { job->response.memory = (char *)malloc(INITIAL_BUFFER_SIZE); job->response.size = 0; job->response.capacity = INITIAL_BUFFER_SIZE; + if (!job->response.memory) { + curl_easy_cleanup(job->handle); + job->handle = NULL; + job->response.capacity = 0; + free(full_url); + job->status = SCRAPE_STATUS_FETCH_ERROR; + return -1; + } struct curl_slist *headers = build_request_headers(job->engine->host_header, job->engine->referer); @@ -255,10 +282,33 @@ int setup_job(ScrapeJob *job, CURLM *multi_handle) { curl_easy_setopt(job->handle, CURLOPT_PRIVATE, headers); free(full_url); - curl_multi_add_handle(multi_handle, job->handle); + CURLMcode add_result = curl_multi_add_handle(multi_handle, job->handle); + if (add_result != CURLM_OK) { + cleanup_job_handle(job, job->handle); + curl_easy_cleanup(job->handle); + job->handle = NULL; + job->status = SCRAPE_STATUS_FETCH_ERROR; + return -1; + } return 0; } +static void cleanup_unfinished_jobs(CURLM *multi_handle, ScrapeJob *jobs, + int num_jobs) { + for (int i = 0; i < num_jobs; i++) { + if (!jobs[i].handle) + continue; + + curl_multi_remove_handle(multi_handle, jobs[i].handle); + cleanup_job_handle(&jobs[i], jobs[i].handle); + curl_easy_cleanup(jobs[i].handle); + jobs[i].handle = NULL; + + if (jobs[i].status == SCRAPE_STATUS_PENDING) + jobs[i].status = SCRAPE_STATUS_FETCH_ERROR; + } +} + int handle_responses(CURLM *multi_handle, ScrapeJob *jobs, int num_jobs) { CURLMsg *msg; int msgs_left; @@ -304,27 +354,24 @@ retry:; return -1; for (int i = 0; i < num_jobs; i++) { - if (setup_job(&jobs[i], multi_handle) != 0 && jobs[i].handle) { - curl_multi_remove_handle(multi_handle, jobs[i].handle); - curl_easy_cleanup(jobs[i].handle); - jobs[i].handle = NULL; - } + setup_job(&jobs[i], multi_handle); } http_delay(); int still_running = 0; - curl_multi_perform(multi_handle, &still_running); + CURLMcode mc = curl_multi_perform(multi_handle, &still_running); - do { + while (mc == CURLM_OK && still_running) { int numfds = 0; - CURLMcode mc = curl_multi_wait(multi_handle, NULL, 0, 1000, &numfds); + mc = curl_multi_wait(multi_handle, NULL, 0, 1000, &numfds); if (mc != CURLM_OK) break; - curl_multi_perform(multi_handle, &still_running); - } while (still_running); + mc = curl_multi_perform(multi_handle, &still_running); + } handle_responses(multi_handle, jobs, num_jobs); + cleanup_unfinished_jobs(multi_handle, jobs, num_jobs); curl_multi_cleanup(multi_handle); if (retries < max_proxy_retries && should_retry(jobs, num_jobs)) { -- cgit v1.3