From b280ab6bcdf6c9bae46a7a21b7138d46d953dd71 Mon Sep 17 00:00:00 2001 From: frosty Date: Mon, 23 Feb 2026 00:57:21 -0500 Subject: oopsies --- src/Infobox/Calculator.c | 115 ++++++++++++++++++++++ src/Infobox/Calculator.h | 9 ++ src/Infobox/Dictionary.c | 246 +++++++++++++++++++++++++++++++++++++++++++++++ src/Infobox/Dictionary.h | 10 ++ src/Infobox/Infobox.c | 13 +++ src/Infobox/Infobox.h | 13 +++ src/Infobox/Wikipedia.c | 165 +++++++++++++++++++++++++++++++ src/Infobox/Wikipedia.h | 9 ++ 8 files changed, 580 insertions(+) create mode 100644 src/Infobox/Calculator.c create mode 100644 src/Infobox/Calculator.h create mode 100644 src/Infobox/Dictionary.c create mode 100644 src/Infobox/Dictionary.h create mode 100644 src/Infobox/Infobox.c create mode 100644 src/Infobox/Infobox.h create mode 100644 src/Infobox/Wikipedia.c create mode 100644 src/Infobox/Wikipedia.h (limited to 'src/Infobox') diff --git a/src/Infobox/Calculator.c b/src/Infobox/Calculator.c new file mode 100644 index 0000000..b80ce21 --- /dev/null +++ b/src/Infobox/Calculator.c @@ -0,0 +1,115 @@ +#include "Calculator.h" +#include +#include +#include +#include +#include + +static char logic_log[4096]; + +typedef struct { + const char *buffer; + int pos; +} Parser; + +static double parse_expression(Parser *p); + +static void skip_ws(Parser *p) { + while (p->buffer[p->pos] == ' ') p->pos++; +} + +static double parse_factor(Parser *p) { + skip_ws(p); + if (p->buffer[p->pos] == '-') { + p->pos++; + return -parse_factor(p); + } + if (p->buffer[p->pos] == '(') { + p->pos++; + double res = parse_expression(p); + if (p->buffer[p->pos] == ')') p->pos++; + return res; + } + char *endptr; + double val = strtod(&p->buffer[p->pos], &endptr); + p->pos = (int)(endptr - p->buffer); + return val; +} + +static double parse_term(Parser *p) { + double left = parse_factor(p); + while (1) { + skip_ws(p); + char op = p->buffer[p->pos]; + if (op == '*' || op == '/') { + p->pos++; + double right = parse_factor(p); + double old = left; + left = (op == '*') ? left * right : left / right; + + char step[256]; + + snprintf(step, sizeof(step), "
%g %c %g = %g
", old, op, + right, left); + strncat(logic_log, step, sizeof(logic_log) - strlen(logic_log) - 1); + } else + break; + } + return left; +} + +static double parse_expression(Parser *p) { + double left = parse_term(p); + while (1) { + skip_ws(p); + char op = p->buffer[p->pos]; + if (op == '+' || op == '-') { + p->pos++; + double right = parse_term(p); + double old = left; + left = (op == '+') ? left + right : left - right; + + char step[256]; + + snprintf(step, sizeof(step), "
%g %c %g = %g
", old, op, + right, left); + strncat(logic_log, step, sizeof(logic_log) - strlen(logic_log) - 1); + } else + break; + } + return left; +} + +double evaluate(const char *expr) { + logic_log[0] = '\0'; + if (!expr || strlen(expr) == 0) return 0.0; + Parser p = {expr, 0}; + return parse_expression(&p); +} + +InfoBox fetch_calc_data(char *math_input) { + InfoBox info = {NULL, NULL, NULL, NULL}; + if (!math_input) return info; + + double result = evaluate(math_input); + + char html_output[5120]; + snprintf(html_output, sizeof(html_output), + "
" + "%s" + "
" + "%g" + "
" + "
", + strlen(logic_log) > 0 ? logic_log : "
Constant value
", + result); + + info.title = strdup("Calculation"); + info.extract = strdup(html_output); + info.thumbnail_url = + strdup("/static/calculation.svg"); + info.url = strdup("#"); + + return info; +} diff --git a/src/Infobox/Calculator.h b/src/Infobox/Calculator.h new file mode 100644 index 0000000..275aed6 --- /dev/null +++ b/src/Infobox/Calculator.h @@ -0,0 +1,9 @@ +#ifndef CALCULATOR_H +#define CALCULATOR_H + +#include "Infobox.h" + +double evaluate(const char *expr); +InfoBox fetch_calc_data(char *math_input); + +#endif diff --git a/src/Infobox/Dictionary.c b/src/Infobox/Dictionary.c new file mode 100644 index 0000000..a835899 --- /dev/null +++ b/src/Infobox/Dictionary.c @@ -0,0 +1,246 @@ +#include "Dictionary.h" +#include +#include +#include +#include +#include +#include +#include +#include + +static const char *PREFIXES[] = { + "what is the definition of ", "what's the definition of ", + "what is the meaning of ", "what's the meaning of ", + "what does the word ", "definition of ", "meaning of ", "def of ", + "define ", "definition ", "define:", "def ", "def:", + "what does ", "what is ", "what's ", "whats ", + "meaning ", "dictionary ", "dict ", NULL +}; + +static const char *SUFFIXES[] = { + " definition", " def", " meaning", " mean", " means", + " dictionary", " dict", " define", " defined", + " definition?", " def?", " meaning?", " mean?", " means?", + " in english", " in english?", NULL +}; + +static const char *SKIP_WORDS[] = {"of ", "the ", "a ", "an ", NULL}; + +static const char *strcasestr_impl(const char *haystack, const char *needle) { + if (!haystack || !needle || !*needle) return haystack; + size_t len = strlen(needle); + for (const char *h = haystack; *h; h++) { + if (strncasecmp(h, needle, len) == 0) return h; + } + return NULL; +} + +struct MemStruct { char *memory; size_t size; }; + +static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) { + size_t realsize = size * nmemb; + struct MemStruct *mem = (struct MemStruct *)userp; + char *ptr = realloc(mem->memory, mem->size + realsize + 1); + if (!ptr) return 0; + mem->memory = ptr; + memcpy(&(mem->memory[mem->size]), contents, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + return realsize; +} + +static char *xpath_text(xmlDocPtr doc, const char *xpath) { + xmlXPathContextPtr ctx = xmlXPathNewContext(doc); + if (!ctx) return NULL; + xmlXPathObjectPtr obj = xmlXPathEvalExpression((const xmlChar *)xpath, ctx); + xmlXPathFreeContext(ctx); + if (!obj || !obj->nodesetval || obj->nodesetval->nodeNr == 0) { + if (obj) xmlXPathFreeObject(obj); + return NULL; + } + xmlChar *content = xmlNodeGetContent(obj->nodesetval->nodeTab[0]); + char *result = content ? strdup((char *)content) : NULL; + if (content) xmlFree(content); + xmlXPathFreeObject(obj); + return result; +} + +static char *build_html(const char *word, const char *pron, const char *pos, + const char *def, const char *ex) { + char html[4096]; + int n = snprintf(html, sizeof(html), "
"); + if (word) n += snprintf(html + n, sizeof(html) - n, + "
%s
", word); + if (pron) n += snprintf(html + n, sizeof(html) - n, + "
/%s/
", pron); + if (pos) n += snprintf(html + n, sizeof(html) - n, + "
%s
", pos); + if (def) n += snprintf(html + n, sizeof(html) - n, + "
%s
", def); + if (ex) n += snprintf(html + n, sizeof(html) - n, + "
\"%s\"
", ex); + snprintf(html + n, sizeof(html) - n, "
"); + return strdup(html); +} + +static char *extract_word(const char *query) { + if (!query) return NULL; + + const char *start = query; + + for (int i = 0; PREFIXES[i]; i++) { + size_t len = strlen(PREFIXES[i]); + if (strncasecmp(start, PREFIXES[i], len) == 0) { + start += len; + break; + } + } + + while (*start == ' ') start++; + char *word = strdup(start); + if (!word) return NULL; + + int changed = 1; + while (changed) { + changed = 0; + for (int i = 0; SKIP_WORDS[i]; i++) { + size_t len = strlen(SKIP_WORDS[i]); + if (strncasecmp(word, SKIP_WORDS[i], len) == 0) { + memmove(word, word + len, strlen(word + len) + 1); + changed = 1; + break; + } + } + } + + changed = 1; + while (changed) { + changed = 0; + for (int i = 0; SUFFIXES[i]; i++) { + const char *found = strcasestr_impl(word, SUFFIXES[i]); + if (found) { + char *pos = word + (found - word); + *pos = '\0'; + changed = 1; + break; + } + } + } + + size_t len = strlen(word); + while (len > 0 && (word[len-1] == ' ' || word[len-1] == '?' || + word[len-1] == '!' || word[len-1] == '.')) { + word[--len] = '\0'; + } + + if (len == 0) { free(word); return NULL; } + + for (size_t i = 0; i < len; i++) word[i] = tolower((unsigned char)word[i]); + char *space = strchr(word, ' '); + if (space) *space = '\0'; + + return word; +} + +int is_dictionary_query(const char *query) { + if (!query) return 0; + + for (int i = 0; PREFIXES[i]; i++) { + size_t len = strlen(PREFIXES[i]); + if (strncasecmp(query, PREFIXES[i], len) == 0) { + const char *after = query + len; + while (*after == ' ') after++; + if (*after != '\0') return 1; + } + } + + for (int i = 0; SUFFIXES[i]; i++) { + const char *pos = strcasestr_impl(query, SUFFIXES[i]); + if (pos) { + const char *after = pos + strlen(SUFFIXES[i]); + while (*after == ' ' || *after == '?' || *after == '!' || *after == '.') after++; + if (*after == '\0' && pos > query && (pos - query) < 100) return 1; + } + } + + if (strncasecmp(query, "what is ", 8) == 0 || + strncasecmp(query, "what's ", 7) == 0 || + strncasecmp(query, "whats ", 6) == 0) { + const char *word = query + (strncasecmp(query, "what is ", 8) == 0 ? 8 : + strncasecmp(query, "what's ", 7) == 0 ? 7 : 6); + const char *articles[] = {"the ", "your ", "my ", "his ", "her ", "their ", + "our ", "this ", "that ", "these ", "those ", "a ", "an ", NULL}; + for (int i = 0; articles[i]; i++) { + if (strncasecmp(word, articles[i], strlen(articles[i])) == 0) return 0; + } + const char *space = strchr(word, ' '); + if (!space || *(space + 1) == '\0' || *(space + 1) == '?') return 1; + } + + return 0; +} + +char *construct_dictionary_url(const char *query) { + char *word = extract_word(query); + if (!word) return NULL; + + CURL *curl = curl_easy_init(); + if (!curl) { free(word); return NULL; } + + char *escaped = curl_easy_escape(curl, word, 0); + const char *base = "https://dictionary.cambridge.org/dictionary/english/"; + char *url = malloc(strlen(base) + strlen(escaped) + 1); + if (url) { + strcpy(url, base); + strcat(url, escaped); + } + + curl_free(escaped); + curl_easy_cleanup(curl); + free(word); + return url; +} + +InfoBox fetch_dictionary_data(const char *query) { + InfoBox info = {NULL, NULL, NULL, NULL}; + + char *url = construct_dictionary_url(query); + if (!url) return info; + + CURL *curl = curl_easy_init(); + if (!curl) { free(url); return info; } + + struct MemStruct chunk = {malloc(1), 0}; + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &chunk); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "Mozilla/5.0"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + + if (curl_easy_perform(curl) == CURLE_OK && chunk.size > 0) { + htmlDocPtr doc = htmlReadMemory(chunk.memory, chunk.size, url, NULL, + HTML_PARSE_RECOVER | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING); + if (doc) { + char *word = xpath_text(doc, "//span[@class='hw dhw']"); + char *pron = xpath_text(doc, "//span[@class='us dpron-i']//span[@class='ipa dipa lpr-2 lpl-1']"); + char *pos = xpath_text(doc, "//span[@class='pos dpos']"); + char *def = xpath_text(doc, "(//div[@class='def ddef_d db'])[1]"); + char *ex = xpath_text(doc, "(//span[@class='eg deg'])[1]"); + + if (word && def) { + info.title = strdup("Dictionary"); + info.extract = build_html(word, pron, pos, def, ex); + info.thumbnail_url = strdup("/static/dictionary.jpg"); + info.url = strdup(url); + } + + free(word); free(pron); free(pos); free(def); free(ex); + xmlFreeDoc(doc); + } + } + + curl_easy_cleanup(curl); + free(chunk.memory); + free(url); + return info; +} \ No newline at end of file diff --git a/src/Infobox/Dictionary.h b/src/Infobox/Dictionary.h new file mode 100644 index 0000000..2f212c3 --- /dev/null +++ b/src/Infobox/Dictionary.h @@ -0,0 +1,10 @@ +#ifndef DICTIONARY_H +#define DICTIONARY_H + +#include "Infobox.h" + +InfoBox fetch_dictionary_data(const char *word); +char *construct_dictionary_url(const char *word); +int is_dictionary_query(const char *query); + +#endif \ No newline at end of file diff --git a/src/Infobox/Infobox.c b/src/Infobox/Infobox.c new file mode 100644 index 0000000..5043c05 --- /dev/null +++ b/src/Infobox/Infobox.c @@ -0,0 +1,13 @@ +#include "Infobox.h" +#include + +void free_infobox(InfoBox *info) { + if (info->title) + free(info->title); + if (info->thumbnail_url) + free(info->thumbnail_url); + if (info->extract) + free(info->extract); + if (info->url) + free(info->url); +} diff --git a/src/Infobox/Infobox.h b/src/Infobox/Infobox.h new file mode 100644 index 0000000..a052b80 --- /dev/null +++ b/src/Infobox/Infobox.h @@ -0,0 +1,13 @@ +#ifndef INFOBOX_H +#define INFOBOX_H + +typedef struct { + char *title; + char *thumbnail_url; + char *extract; + char *url; +} InfoBox; + +void free_infobox(InfoBox *info); + +#endif diff --git a/src/Infobox/Wikipedia.c b/src/Infobox/Wikipedia.c new file mode 100644 index 0000000..ed4645f --- /dev/null +++ b/src/Infobox/Wikipedia.c @@ -0,0 +1,165 @@ +#include "Wikipedia.h" +#include +#include +#include +#include +#include +#include + +struct WikiMemoryStruct { + char *memory; + size_t size; +}; + +static void shorten_summary(char **extract_ptr, int max_chars) { + if (!extract_ptr || !*extract_ptr) return; + + char *text = *extract_ptr; + int len = strlen(text); + + if (len <= max_chars) return; + + int end_pos = max_chars; + for (int i = max_chars; i > (max_chars / 2); i--) { + if (text[i] == '.' || text[i] == '!' || text[i] == '?') { + end_pos = i + 1; + break; + } + } + + char *new_text = (char *)malloc(end_pos + 4); + + if (new_text) { + strncpy(new_text, text, end_pos); + new_text[end_pos] = '\0'; + strcat(new_text, "..."); + free(*extract_ptr); + *extract_ptr = new_text; + } +} + +static size_t WikiWriteMemoryCallback(void *contents, size_t size, size_t nmemb, + void *userp) { + size_t realsize = size * nmemb; + struct WikiMemoryStruct *mem = (struct WikiMemoryStruct *)userp; + + char *ptr = realloc(mem->memory, mem->size + realsize + 1); + if (ptr == NULL) { + fprintf(stderr, "Not enough memory (realloc returned NULL)\n"); + return 0; + } + + mem->memory = ptr; + memcpy(&(mem->memory[mem->size]), contents, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + + return realsize; +} + +static void extract_wiki_info(xmlNode *node, InfoBox *info) { + xmlNode *cur_node = NULL; + + for (cur_node = node; cur_node; cur_node = cur_node->next) { + if (cur_node->type == XML_ELEMENT_NODE) { + if (strcmp((const char *)cur_node->name, "page") == 0) { + xmlChar *title = xmlGetProp(cur_node, (const xmlChar *)"title"); + if (title) { + info->title = strdup((const char *)title); + + const char *base_article_url = "https://en.wikipedia.org/wiki/"; + char *formatted_title = strdup((const char *)title); + for (int i = 0; formatted_title[i]; i++) { + if (formatted_title[i] == ' ') formatted_title[i] = '_'; + } + + info->url = + malloc(strlen(base_article_url) + strlen(formatted_title) + 1); + if (info->url) { + strcpy(info->url, base_article_url); + strcat(info->url, formatted_title); + } + free(formatted_title); + xmlFree(title); + } + } + + if (strcmp((const char *)cur_node->name, "thumbnail") == 0) { + xmlChar *source = xmlGetProp(cur_node, (const xmlChar *)"source"); + if (source) { + info->thumbnail_url = strdup((const char *)source); + xmlFree(source); + } + } + + if (strcmp((const char *)cur_node->name, "extract") == 0) { + xmlChar *content = xmlNodeGetContent(cur_node); + if (content) { + info->extract = strdup((const char *)content); + + shorten_summary(&(info->extract), 300); + xmlFree(content); + } + } + } + extract_wiki_info(cur_node->children, info); + } +} + +InfoBox fetch_wiki_data(char *api_url) { + CURL *curl_handle; + CURLcode res; + struct WikiMemoryStruct chunk; + InfoBox info = {NULL, NULL, NULL, NULL}; + + chunk.memory = malloc(1); + chunk.size = 0; + + curl_handle = curl_easy_init(); + + if (curl_handle) { + curl_easy_setopt(curl_handle, CURLOPT_URL, api_url); + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, + WikiWriteMemoryCallback); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + + res = curl_easy_perform(curl_handle); + + if (res == CURLE_OK) { + xmlDocPtr doc = + xmlReadMemory(chunk.memory, chunk.size, "noname.xml", NULL, 0); + if (doc != NULL) { + xmlNode *root_element = xmlDocGetRootElement(doc); + extract_wiki_info(root_element, &info); + xmlFreeDoc(doc); + } + } + + curl_easy_cleanup(curl_handle); + free(chunk.memory); + } + + return info; +} + +char *construct_wiki_url(const char *search_term) { + CURL *curl = curl_easy_init(); + if (!curl) return NULL; + + char *escaped_term = curl_easy_escape(curl, search_term, 0); + const char *base = + "https://en.wikipedia.org/w/" + "api.php?action=query&prop=extracts|pageimages&exintro&" + "explaintext&pithumbsize=400&format=xml&origin=*&titles="; + + char *full_url = malloc(strlen(base) + strlen(escaped_term) + 1); + if (full_url) { + strcpy(full_url, base); + strcat(full_url, escaped_term); + } + + curl_free(escaped_term); + curl_easy_cleanup(curl); + return full_url; +} diff --git a/src/Infobox/Wikipedia.h b/src/Infobox/Wikipedia.h new file mode 100644 index 0000000..8a8103e --- /dev/null +++ b/src/Infobox/Wikipedia.h @@ -0,0 +1,9 @@ +#ifndef WIKIPEDIA_H +#define WIKIPEDIA_H + +#include "Infobox.h" + +InfoBox fetch_wiki_data(char *api_url); +char *construct_wiki_url(const char *search_term); + +#endif -- cgit v1.2.3