diff options
| author | frosty <gabriel@bwaaa.monster> | 2026-06-05 09:47:24 -0400 |
|---|---|---|
| committer | frosty <gabriel@bwaaa.monster> | 2026-06-05 09:49:27 -0400 |
| commit | ad949a452fb9ec6290adb8ec14a04171233a8426 (patch) | |
| tree | 02f89ccd8db37b975c0eff93343f487e506accb3 /src | |
| parent | 24f9909badd4e7aa1f3aeef717b93e9b71c20a4e (diff) | |
| parent | b8fd03344d1550cce1d13c753c400d00c2c11b97 (diff) | |
| download | omnisearch-ad949a452fb9ec6290adb8ec14a04171233a8426.tar.gz | |
chore: merge indev into master
Diffstat (limited to 'src')
| -rw-r--r-- | src/Config.c | 6 | ||||
| -rw-r--r-- | src/Main.c | 34 | ||||
| -rw-r--r-- | src/Routes/Home.c | 5 | ||||
| -rw-r--r-- | src/Routes/Images.c | 17 | ||||
| -rw-r--r-- | src/Routes/Settings.c | 32 | ||||
| -rw-r--r-- | src/Scraping/ImageScraping.c | 178 | ||||
| -rw-r--r-- | src/Utility/HttpClient.c | 21 | ||||
| -rw-r--r-- | src/Utility/Utility.c | 116 | ||||
| -rw-r--r-- | src/Utility/Utility.h | 7 |
9 files changed, 271 insertions, 145 deletions
diff --git a/src/Config.c b/src/Config.c index 967a4b4..9883d45 100644 --- a/src/Config.c +++ b/src/Config.c @@ -65,11 +65,9 @@ int load_config(const char *filename, Config *config) { config->host[sizeof(config->host) - 1] = '\0'; } else if (strcmp(key, "port") == 0) { config->port = atoi(value); - } else if (strcmp(key, "domain") == 0) { - strncpy(config->domain, value, sizeof(config->domain) - 1); - config->domain[sizeof(config->domain) - 1] = '\0'; } else if (strcmp(key, "locale") == 0) { - strncpy(config->default_locale, value, sizeof(config->default_locale) - 1); + strncpy(config->default_locale, value, + sizeof(config->default_locale) - 1); config->default_locale[sizeof(config->default_locale) - 1] = '\0'; } } else if (strcmp(section, "proxy") == 0) { @@ -19,17 +19,38 @@ #include "Utility/Utility.h" Config global_config; - + int handle_opensearch(UrlParams *params) { (void)params; - extern Config global_config; TemplateContext ctx = new_context(); - context_set(&ctx, "domain", global_config.domain); + + const char *http_host = beaker_get_header("Host"); + if (http_host == NULL) { + http_host = "localhost"; + } + + const char *req_scheme = + "https"; // not sure if it's a good idea to just assume https, but you + // should probably be using https for anything other than testing + // or local network anyways. + + if (strncmp(http_host, "localhost", 9) == 0 || + strncmp(http_host, "127.", 4) == 0 || + strncmp(http_host, "192.168.", 8) == 0 || + strncmp(http_host, "10.", 3) == 0) { + req_scheme = "http"; + } + + context_set(&ctx, "domain", http_host); + context_set(&ctx, "scheme", req_scheme); + char *rendered = render_template("opensearch.xml", &ctx); - serve_data(rendered, strlen(rendered), "application/opensearchdescription+xml"); + serve_data(rendered, strlen(rendered), + "application/opensearchdescription+xml"); free(rendered); free_context(&ctx); + return 0; } @@ -46,7 +67,6 @@ int main() { Config cfg = {.host = DEFAULT_HOST, .port = DEFAULT_PORT, - .domain = "", .default_locale = "en_gb", .proxy = "", .proxy_list_file = "", @@ -68,6 +88,7 @@ int main() { } set_default_locale(cfg.default_locale); + init_themes("static"); global_config = cfg; @@ -75,7 +96,8 @@ int main() { if (loaded > 0) { fprintf(stderr, "[INFO] Loaded %d locales\n", loaded); } else { - fprintf(stderr, "[WARN] No locales loaded (make sure to run from omnisearch directory)\n"); + fprintf(stderr, "[WARN] No locales loaded (make sure to run from " + "omnisearch directory)\n"); } apply_engines_config(cfg.engines); diff --git a/src/Routes/Home.c b/src/Routes/Home.c index 0534517..bf85fe1 100644 --- a/src/Routes/Home.c +++ b/src/Routes/Home.c @@ -2,12 +2,17 @@ #include "../Utility/Utility.h" #include <beaker.h> #include <stdlib.h> +#include <string.h> int home_handler(UrlParams *params) { (void)params; char *theme = get_theme(""); char *locale = get_locale(NULL); + char **themes = NULL; + int themes_count = 0; + get_available_themes(&themes, &themes_count); + TemplateContext ctx = new_context(); context_set(&ctx, "theme", theme); context_set(&ctx, "version", VERSION); diff --git a/src/Routes/Images.c b/src/Routes/Images.c index dda329c..40ab88f 100644 --- a/src/Routes/Images.c +++ b/src/Routes/Images.c @@ -6,6 +6,8 @@ #include "../Utility/Utility.h" #include "Config.h" #include <beaker.h> +#include <stdlib.h> +#include <string.h> static char *build_images_request_cache_key(const char *query, int page, const char *client_key) { @@ -67,15 +69,18 @@ int images_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 image searches from you!"; - const char *error_images_msg = beaker_get_locale_value(locale, "error_images"); - if (!error_images_msg) error_images_msg = "Error fetching images"; + if (!rate_limit_msg) + rate_limit_msg = "Slow down! Too many image searches from you!"; + const char *error_images_msg = + beaker_get_locale_value(locale, "error_images"); + 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); + 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); diff --git a/src/Routes/Settings.c b/src/Routes/Settings.c index b21dd6f..eb3072b 100644 --- a/src/Routes/Settings.c +++ b/src/Routes/Settings.c @@ -32,7 +32,8 @@ int settings_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); int enabled_count = 0; for (int i = 0; i < ENGINE_COUNT; i++) { @@ -77,7 +78,34 @@ int settings_handler(UrlParams *params) { context_set(&ctx, "query", query); context_set(&ctx, "theme", theme); context_set(&ctx, "locale", locale); - context_set_array_of_arrays(&ctx, "locales", locale_data, locale_count, inner_counts); + context_set_array_of_arrays(&ctx, "locales", locale_data, locale_count, + inner_counts); + + char **themes = NULL; + int themes_count = 0; + get_available_themes(&themes, &themes_count); + + if (themes_count > 0) { + char ***theme_ptrs = malloc(sizeof(char **) * themes_count); + int *theme_inner = malloc(sizeof(int) * themes_count); + for (int i = 0; i < themes_count; i++) { + theme_ptrs[i] = malloc(sizeof(char *) * 2); + theme_ptrs[i][0] = themes[i]; + theme_ptrs[i][1] = strdup(themes[i]); + if (theme_ptrs[i][1][0] >= 'a' && theme_ptrs[i][1][0] <= 'z') { + theme_ptrs[i][1][0] = theme_ptrs[i][1][0] - 'a' + 'A'; + } + theme_inner[i] = 2; + } + context_set_array_of_arrays(&ctx, "themes", theme_ptrs, themes_count, + theme_inner); + for (int i = 0; i < themes_count; i++) { + free(theme_ptrs[i][1]); + free(theme_ptrs[i]); + } + free(theme_ptrs); + free(theme_inner); + } if (enabled_count > 0) { context_set_array_of_arrays(&ctx, "enabled_engines", engine_data, diff --git a/src/Scraping/ImageScraping.c b/src/Scraping/ImageScraping.c index 33f710a..2341244 100644 --- a/src/Scraping/ImageScraping.c +++ b/src/Scraping/ImageScraping.c @@ -28,113 +28,82 @@ static char *build_proxy_url(const char *image_url) { return proxy_url; } -static int parse_image_node(xmlNodePtr node, ImageResult *result) { - xmlNodePtr img_node = NULL; - xmlNodePtr tit_node = NULL; - xmlNodePtr des_node = NULL; - xmlNodePtr thumb_link = NULL; +static char *extract_json_string(const char *json, const char *key) { + if (!json || !key) + return NULL; - for (xmlNodePtr child = node->children; child; child = child->next) { - if (child->type != XML_ELEMENT_NODE) - continue; + char search_key[64]; + snprintf(search_key, sizeof(search_key), "\"%s\"", key); - if (xmlStrcmp(child->name, (const xmlChar *)"a") == 0) { - xmlChar *class = xmlGetProp(child, (const xmlChar *)"class"); - if (class) { - if (xmlStrstr(class, (const xmlChar *)"thumb") != NULL) { - thumb_link = child; - for (xmlNodePtr thumb_child = child->children; thumb_child; - thumb_child = thumb_child->next) { - if (xmlStrcmp(thumb_child->name, (const xmlChar *)"div") == 0) { - xmlChar *div_class = - xmlGetProp(thumb_child, (const xmlChar *)"class"); - if (div_class && - xmlStrcmp(div_class, (const xmlChar *)"cico") == 0) { - for (xmlNodePtr cico_child = thumb_child->children; cico_child; - cico_child = cico_child->next) { - if (xmlStrcmp(cico_child->name, (const xmlChar *)"img") == - 0) { - img_node = cico_child; - break; - } - } - } - if (div_class) - xmlFree(div_class); - } - } - } else if (xmlStrstr(class, (const xmlChar *)"tit") != NULL) { - tit_node = child; - } - xmlFree(class); - } - } else if (xmlStrcmp(child->name, (const xmlChar *)"div") == 0) { - xmlChar *class = xmlGetProp(child, (const xmlChar *)"class"); - if (class && xmlStrcmp(class, (const xmlChar *)"meta") == 0) { - for (xmlNodePtr meta_child = child->children; meta_child; - meta_child = meta_child->next) { - if (xmlStrcmp(meta_child->name, (const xmlChar *)"div") == 0) { - xmlChar *div_class = - xmlGetProp(meta_child, (const xmlChar *)"class"); - if (div_class) { - if (xmlStrcmp(div_class, (const xmlChar *)"des") == 0) { - des_node = meta_child; - } - xmlFree(div_class); - } - } else if (xmlStrcmp(meta_child->name, (const xmlChar *)"a") == 0) { - xmlChar *a_class = xmlGetProp(meta_child, (const xmlChar *)"class"); - if (a_class && xmlStrstr(a_class, (const xmlChar *)"tit") != NULL) { - tit_node = meta_child; - } - if (a_class) - xmlFree(a_class); - } - } - } - if (class) - xmlFree(class); - } + const char *key_pos = strstr(json, search_key); + if (!key_pos) + return NULL; + + const char *colon = strchr(key_pos + strlen(search_key), ':'); + if (!colon) + return NULL; + + colon++; + while (*colon == ' ' || *colon == '\t' || *colon == '\n' || *colon == '\r') + colon++; + + if (*colon != '"') + return NULL; + colon++; + + size_t len = 0; + const char *start = colon; + while (*colon && *colon != '"') { + if (*colon == '\\' && *(colon + 1)) + colon++; + colon++; + len++; } - xmlChar *iurl = - img_node ? xmlGetProp(img_node, (const xmlChar *)"src") : NULL; - xmlChar *full_url = - thumb_link ? xmlGetProp(thumb_link, (const xmlChar *)"href") : NULL; - xmlChar *title = des_node ? xmlNodeGetContent(des_node) - : (tit_node ? xmlNodeGetContent(tit_node) : NULL); - xmlChar *rurl = - tit_node ? xmlGetProp(tit_node, (const xmlChar *)"href") : NULL; + char *result = malloc(len + 1); + if (!result) + return NULL; - if (!iurl || strlen((char *)iurl) == 0) { - if (iurl) - xmlFree(iurl); - if (title) - xmlFree(title); - if (rurl) - xmlFree(rurl); - if (full_url) - xmlFree(full_url); - return 0; + colon = start; + size_t i = 0; + while (*colon && *colon != '"') { + if (*colon == '\\' && *(colon + 1)) + colon++; + result[i++] = *colon++; } + result[i] = '\0'; + + return result; +} - char *proxy_url = build_proxy_url((char *)iurl); - result->thumbnail_url = proxy_url ? strdup(proxy_url) : strdup((char *)iurl); - free(proxy_url); - result->title = strdup(title ? (char *)title : "Image"); - result->page_url = strdup(rurl ? (char *)rurl : "#"); - result->full_url = strdup(full_url ? (char *)full_url : "#"); +static int parse_iusc_node(xmlNodePtr node, ImageResult *result) { + xmlChar *m_attr = xmlGetProp(node, (const xmlChar *)"m"); + if (!m_attr) + return 0; + + char *turl = extract_json_string((const char *)m_attr, "turl"); + char *murl = extract_json_string((const char *)m_attr, "murl"); + char *purl = extract_json_string((const char *)m_attr, "purl"); + char *title = extract_json_string((const char *)m_attr, "t"); + + int ok = (turl != NULL && strlen(turl) > 0); + if (ok) { + char *proxy_url = build_proxy_url(turl); + result->thumbnail_url = proxy_url ? strdup(proxy_url) : strdup(turl); + free(proxy_url); + result->title = + title && strlen(title) > 0 ? strdup(title) : strdup("Image"); + result->page_url = purl && strlen(purl) > 0 ? strdup(purl) : strdup("#"); + result->full_url = murl && strlen(murl) > 0 ? strdup(murl) : strdup("#"); + } - if (iurl) - xmlFree(iurl); - if (title) - xmlFree(title); - if (rurl) - xmlFree(rurl); - if (full_url) - xmlFree(full_url); + free(turl); + free(murl); + free(purl); + free(title); - return 1; + xmlFree(m_attr); + return ok; } int scrape_images(const char *query, int page, ImageResult **out_results, @@ -157,13 +126,16 @@ int scrape_images(const char *query, int page, ImageResult **out_results, char url[BUFFER_SIZE_LARGE]; int first = (page - 1) * IMAGE_RESULTS_PER_PAGE + 1; - snprintf(url, sizeof(url), "%s?q=%s&first=%d", BING_IMAGE_URL, encoded_query, - first); + snprintf( + url, sizeof(url), + "https://www.bing.com/images/async?q=%s&async=content&first=%d&count=%d", + encoded_query, first, 35); free(encoded_query); HttpResponse resp = http_get( url, - "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"); + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, " + "like Gecko) Chrome/120.0.0.0 Safari/537.36"); if (!resp.memory) { return -1; } @@ -183,7 +155,7 @@ int scrape_images(const char *query, int page, ImageResult **out_results, } xmlXPathObjectPtr xpathObj = - xmlXPathEvalExpression((const xmlChar *)"//div[@class='item']", xpathCtx); + xmlXPathEvalExpression((const xmlChar *)"//a[@class='iusc']", xpathCtx); if (!xpathObj || !xpathObj->nodesetval) { if (xpathObj) @@ -210,7 +182,7 @@ int scrape_images(const char *query, int page, ImageResult **out_results, int count = 0; for (int i = 0; i < nodes && count < IMAGE_RESULTS_PER_PAGE; i++) { xmlNodePtr node = xpathObj->nodesetval->nodeTab[i]; - if (parse_image_node(node, &results[count])) { + if (parse_iusc_node(node, &results[count])) { count++; } } diff --git a/src/Utility/HttpClient.c b/src/Utility/HttpClient.c index bdd2f4d..0ffb9ff 100644 --- a/src/Utility/HttpClient.c +++ b/src/Utility/HttpClient.c @@ -31,6 +31,17 @@ static size_t write_callback(void *contents, size_t size, size_t nmemb, return realsize; } +static struct curl_slist *build_http_headers(void) { + struct curl_slist *headers = NULL; + headers = curl_slist_append( + headers, + "Accept: " + "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); + headers = curl_slist_append(headers, "Accept-Language: en-US,en;q=0.5"); + headers = curl_slist_append(headers, "DNT: 1"); + return headers; +} + HttpResponse http_get(const char *url, const char *user_agent) { HttpResponse resp = {.memory = NULL, .size = 0, .capacity = 0}; @@ -51,16 +62,24 @@ HttpResponse http_get(const char *url, const char *user_agent) { return resp; } + struct curl_slist *headers = build_http_headers(); + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp); curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent ? user_agent : "libcurl-agent/1.0"); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_TIMEOUT, 15L); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, CURL_TIMEOUT_SECS); + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); + curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""); + curl_easy_setopt(curl, CURLOPT_DNS_CACHE_TIMEOUT, CURL_DNS_TIMEOUT_SECS); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); apply_proxy_settings(curl); CURLcode res = curl_easy_perform(curl); + curl_slist_free_all(headers); curl_easy_cleanup(curl); if (res != CURLE_OK) { diff --git a/src/Utility/Utility.c b/src/Utility/Utility.c index 8bcb748..1428722 100644 --- a/src/Utility/Utility.c +++ b/src/Utility/Utility.c @@ -1,11 +1,80 @@ #include "Utility.h" #include "../Scraping/Scraping.h" #include <beaker.h> +#include <dirent.h> #include <stdio.h> #include <stdlib.h> #include <string.h> static char global_default_locale[32] = "en_gb"; +static char **themes_list = NULL; +static int themes_count = 0; +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); + + DIR *dir = opendir(themes_dir); + if (!dir) + return; + + struct dirent *entry; + int capacity = 4; + themes_list = malloc(sizeof(char *) * capacity); + 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); + } + themes_list[themes_count] = strndup(entry->d_name, len - 4); + themes_count++; + } + } + closedir(dir); + + for (int i = 0; i < themes_count; i++) { + for (int j = i + 1; j < themes_count; j++) { + int priority_i = 0, priority_j = 0; + if (strcmp(themes_list[i], "system") == 0) + priority_i = 0; + else if (strcmp(themes_list[i], "light") == 0) + priority_i = 1; + else if (strcmp(themes_list[i], "dark") == 0) + priority_i = 2; + else + priority_i = 3; + if (strcmp(themes_list[j], "system") == 0) + priority_j = 0; + else if (strcmp(themes_list[j], "light") == 0) + priority_j = 1; + else if (strcmp(themes_list[j], "dark") == 0) + priority_j = 2; + else + priority_j = 3; + if (priority_i > priority_j || + (priority_i == priority_j && + strcmp(themes_list[i], themes_list[j]) > 0)) { + char *tmp = themes_list[i]; + themes_list[i] = themes_list[j]; + themes_list[j] = tmp; + } + } + } +} + +void get_available_themes(char ***out_themes, int *out_count) { + *out_themes = themes_list; + *out_count = themes_count; +} void set_default_locale(const char *locale) { if (locale && strlen(locale) > 0) { @@ -26,13 +95,16 @@ int hex_to_int(char c) { char *get_theme(const char *default_theme) { char *cookie = get_cookie("theme"); - if (cookie && - (strcmp(cookie, "light") == 0 || - strcmp(cookie, "dark") == 0)) { - return cookie; + if (cookie && strlen(cookie) > 0) { + for (int i = 0; i < themes_count; i++) { + if (strcmp(cookie, themes_list[i]) == 0) { + return cookie; + } + } } free(cookie); - return strdup(default_theme); + return strdup(default_theme && strlen(default_theme) > 0 ? default_theme + : "system"); } char *get_locale(const char *default_locale) { @@ -41,7 +113,8 @@ char *get_locale(const char *default_locale) { return cookie; } free(cookie); - const char *fallback = default_locale ? default_locale : global_default_locale; + const char *fallback = + default_locale ? default_locale : global_default_locale; return strdup(fallback); } @@ -49,9 +122,12 @@ static int engine_id_casecmp(const char *a, const char *b) { while (*a && *b) { char la = *a; char lb = *b; - if (la >= 'A' && la <= 'Z') la = la - 'A' + 'a'; - if (lb >= 'A' && lb <= 'Z') lb = lb - 'A' + 'a'; - if (la != lb) return 0; + if (la >= 'A' && la <= 'Z') + la = la - 'A' + 'a'; + if (lb >= 'A' && lb <= 'Z') + lb = lb - 'A' + 'a'; + if (la != lb) + return 0; a++; b++; } @@ -59,7 +135,8 @@ static int engine_id_casecmp(const char *a, const char *b) { } int is_engine_id_enabled(const char *engine_id) { - if (!engine_id) return 0; + if (!engine_id) + return 0; for (int i = 0; i < ENGINE_COUNT; i++) { if (ENGINE_REGISTRY[i].enabled && engine_id_casecmp(ENGINE_REGISTRY[i].id, engine_id)) { @@ -120,7 +197,8 @@ int get_user_engines(char ***out_ids, int *out_count) { } int user_engines_contains(const char *engine_id, char **ids, int count) { - if (!engine_id || !ids) return 0; + if (!engine_id || !ids) + return 0; for (int i = 0; i < count; i++) { if (engine_id_casecmp(ids[i], engine_id)) return 1; @@ -135,8 +213,7 @@ int add_link_to_collection(const char *href, const char *label, int *old_inner_counts = *inner_counts; char ***new_collection = (char ***)malloc(sizeof(char **) * (current_count + 1)); - int *new_inner_counts = - (int *)malloc(sizeof(int) * (current_count + 1)); + int *new_inner_counts = (int *)malloc(sizeof(int) * (current_count + 1)); if (!new_collection || !new_inner_counts) { free(new_collection); @@ -188,9 +265,8 @@ int add_link_to_collection(const char *href, const char *label, return current_count + 1; } -int build_pagination(int page, - char *(*href_builder)(int page, void *data), void *data, - char ****out_matrix, int **out_inner_counts) { +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 }; *out_matrix = NULL; @@ -202,8 +278,8 @@ int build_pagination(int page, 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); + count = add_link_to_collection(href, "←", "pagination-btn prev", out_matrix, + out_inner_counts, count); free(href); } @@ -219,8 +295,8 @@ int build_pagination(int page, } char *href = href_builder(page + 1, data); - count = add_link_to_collection(href, "→", "pagination-btn next", - out_matrix, out_inner_counts, count); + count = add_link_to_collection(href, "→", "pagination-btn next", out_matrix, + out_inner_counts, count); free(href); return count; diff --git a/src/Utility/Utility.h b/src/Utility/Utility.h index bd48295..1e1de09 100644 --- a/src/Utility/Utility.h +++ b/src/Utility/Utility.h @@ -15,6 +15,8 @@ int hex_to_int(char c); char *get_theme(const char *default_theme); +void init_themes(const char *static_path); +void get_available_themes(char ***out_themes, int *out_count); void set_default_locale(const char *locale); char *get_locale(const char *default_locale); @@ -26,8 +28,7 @@ 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 build_pagination(int page, char *(*href_builder)(int page, void *data), + void *data, char ****out_matrix, int **out_inner_counts); #endif |
