made formatting more consistent

This commit is contained in:
frosty
2026-03-05 04:50:32 +00:00
parent 5ed5a6ecc7
commit 24cec7a350
16 changed files with 1363 additions and 1363 deletions

View File

@@ -8,108 +8,108 @@
static char logic_log[4096];
typedef struct {
const char *buffer;
int pos;
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++;
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;
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;
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];
char step[256];
snprintf(step, sizeof(step), "<div>%g %c %g = <b>%g</b></div>", old, op,
right, left);
strncat(logic_log, step, sizeof(logic_log) - strlen(logic_log) - 1);
} else
break;
}
return left;
snprintf(step, sizeof(step), "<div>%g %c %g = <b>%g</b></div>", 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;
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];
char step[256];
snprintf(step, sizeof(step), "<div>%g %c %g = <b>%g</b></div>", old, op,
right, left);
strncat(logic_log, step, sizeof(logic_log) - strlen(logic_log) - 1);
} else
break;
}
return left;
snprintf(step, sizeof(step), "<div>%g %c %g = <b>%g</b></div>", 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);
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;
InfoBox info = {NULL, NULL, NULL, NULL};
if (!math_input) return info;
double result = evaluate(math_input);
double result = evaluate(math_input);
char html_output[5120];
snprintf(html_output, sizeof(html_output),
"<div class='calc-container' style='line-height: 1.6;'>"
"%s"
"<div style='margin-top: 8px; border-top: 1px solid #eee; "
"padding-top: 8px; font-size: 1.2em;'>"
"<b>%g</b>"
"</div>"
"</div>",
strlen(logic_log) > 0 ? logic_log : "<div>Constant value</div>",
result);
char html_output[5120];
snprintf(html_output, sizeof(html_output),
"<div class='calc-container' style='line-height: 1.6;'>"
"%s"
"<div style='margin-top: 8px; border-top: 1px solid #eee; "
"padding-top: 8px; font-size: 1.2em;'>"
"<b>%g</b>"
"</div>"
"</div>",
strlen(logic_log) > 0 ? logic_log : "<div>Constant value</div>",
result);
info.title = strdup("Calculation");
info.extract = strdup(html_output);
info.thumbnail_url =
strdup("/static/calculation.svg");
info.url = strdup("#");
info.title = strdup("Calculation");
info.extract = strdup(html_output);
info.thumbnail_url =
strdup("/static/calculation.svg");
info.url = strdup("#");
return info;
return info;
}

View File

@@ -11,239 +11,239 @@
#include <ctype.h>
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
"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
" 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;
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;
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;
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), "<div class='dict-container' style='line-height: 1.6;'>");
if (word) n += snprintf(html + n, sizeof(html) - n,
"<div style='font-size: 1.3em; font-weight: bold; margin-bottom: 4px;'>%s</div>", word);
if (pron) n += snprintf(html + n, sizeof(html) - n,
"<div style='color: #666; margin-bottom: 8px;'>/%s/</div>", pron);
if (pos) n += snprintf(html + n, sizeof(html) - n,
"<div style='font-style: italic; color: #888; margin-bottom: 8px;'>%s</div>", pos);
if (def) n += snprintf(html + n, sizeof(html) - n,
"<div style='margin-bottom: 8px;'>%s</div>", def);
if (ex) n += snprintf(html + n, sizeof(html) - n,
"<div style='color: #555; font-style: italic; margin-top: 8px;'>\"%s\"</div>", ex);
snprintf(html + n, sizeof(html) - n, "</div>");
return strdup(html);
const char *def, const char *ex) {
char html[4096];
int n = snprintf(html, sizeof(html), "<div class='dict-container' style='line-height: 1.6;'>");
if (word) n += snprintf(html + n, sizeof(html) - n,
"<div style='font-size: 1.3em; font-weight: bold; margin-bottom: 4px;'>%s</div>", word);
if (pron) n += snprintf(html + n, sizeof(html) - n,
"<div style='color: #666; margin-bottom: 8px;'>/%s/</div>", pron);
if (pos) n += snprintf(html + n, sizeof(html) - n,
"<div style='font-style: italic; color: #888; margin-bottom: 8px;'>%s</div>", pos);
if (def) n += snprintf(html + n, sizeof(html) - n,
"<div style='margin-bottom: 8px;'>%s</div>", def);
if (ex) n += snprintf(html + n, sizeof(html) - n,
"<div style='color: #555; font-style: italic; margin-top: 8px;'>\"%s\"</div>", ex);
snprintf(html + n, sizeof(html) - n, "</div>");
return strdup(html);
}
static char *extract_word(const char *query) {
if (!query) return NULL;
if (!query) return NULL;
const char *start = query;
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;
}
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;
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;
}
}
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;
}
}
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';
}
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; }
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';
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;
return word;
}
int is_dictionary_query(const char *query) {
if (!query) return 0;
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; 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;
}
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;
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;
return 0;
}
char *construct_dictionary_url(const char *query) {
char *word = extract_word(query);
if (!word) return NULL;
char *word = extract_word(query);
if (!word) return NULL;
CURL *curl = curl_easy_init();
if (!curl) { free(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);
}
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;
curl_free(escaped);
curl_easy_cleanup(curl);
free(word);
return url;
}
InfoBox fetch_dictionary_data(const char *query) {
InfoBox info = {NULL, NULL, NULL, NULL};
InfoBox info = {NULL, NULL, NULL, NULL};
char *url = construct_dictionary_url(query);
if (!url) return info;
char *url = construct_dictionary_url(query);
if (!url) return info;
CURL *curl = curl_easy_init();
if (!curl) { free(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);
apply_proxy_settings(curl);
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);
apply_proxy_settings(curl);
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 (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);
}
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);
}
free(word); free(pron); free(pos); free(def); free(ex);
xmlFreeDoc(doc);
}
}
curl_easy_cleanup(curl);
free(chunk.memory);
free(url);
return info;
curl_easy_cleanup(curl);
free(chunk.memory);
free(url);
return info;
}

View File

@@ -2,12 +2,12 @@
#include <stdlib.h>
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);
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);
}

View File

@@ -2,10 +2,10 @@
#define INFOBOX_H
typedef struct {
char *title;
char *thumbnail_url;
char *extract;
char *url;
char *title;
char *thumbnail_url;
char *extract;
char *url;
} InfoBox;
void free_infobox(InfoBox *info);

View File

@@ -82,24 +82,24 @@ static const UnitDef *find_unit(const char *str) {
size_t j = 0;
for (size_t i = 0; i < len && j < 63; i++) {
if ((unsigned char)str[i] == 0xC2 && (unsigned char)str[i+1] == 0xB0) {
i++;
continue;
}
if (str[i] == '^' && i + 1 < len && str[i + 1] == '2') {
normalized[j++] = '2';
i++;
continue;
}
normalized[j++] = tolower((unsigned char)str[i]);
if ((unsigned char)str[i] == 0xC2 && (unsigned char)str[i+1] == 0xB0) {
i++;
continue;
}
if (str[i] == '^' && i + 1 < len && str[i + 1] == '2') {
normalized[j++] = '2';
i++;
continue;
}
normalized[j++] = tolower((unsigned char)str[i]);
}
normalized[j] = '\0';
for (int i = 0; i < UNIT_COUNT; i++) {
if (strcmp(normalized, UNITS[i].name) == 0) return &UNITS[i];
for (int k = 0; k < 4 && UNITS[i].alias[k]; k++) {
if (strcmp(normalized, UNITS[i].alias[k]) == 0) return &UNITS[i];
}
if (strcmp(normalized, UNITS[i].name) == 0) return &UNITS[i];
for (int k = 0; k < 4 && UNITS[i].alias[k]; k++) {
if (strcmp(normalized, UNITS[i].alias[k]) == 0) return &UNITS[i];
}
}
return NULL;
}
@@ -108,37 +108,37 @@ int is_unit_conv_query(const char *query) {
if (!query) return 0;
const char *patterns[] = {
" to ", " in ", " into ",
" = ", " equals ", " equal ",
" convert ", " conversion ",
" -> ", "",
NULL
" to ", " in ", " into ",
" = ", " equals ", " equal ",
" convert ", " conversion ",
" -> ", "",
NULL
};
int has_pattern = 0;
for (int i = 0; patterns[i]; i++) {
if (strstr(query, patterns[i])) {
has_pattern = 1;
break;
}
if (strstr(query, patterns[i])) {
has_pattern = 1;
break;
}
}
if (!has_pattern) {
const char *last_space = strrchr(query, ' ');
if (last_space) {
const UnitDef *u = find_unit(last_space + 1);
if (u) {
const char *before = query;
while (*before && is_whitespace(*before)) before++;
const char *num_end = before;
while (*num_end &&
(isdigit(*num_end) || *num_end == '.' || *num_end == '-' ||
*num_end == '+' || *num_end == '/' || *num_end == '\'' || *num_end == '"')) {
num_end++;
}
if (num_end > before) has_pattern = 1;
}
const char *last_space = strrchr(query, ' ');
if (last_space) {
const UnitDef *u = find_unit(last_space + 1);
if (u) {
const char *before = query;
while (*before && is_whitespace(*before)) before++;
const char *num_end = before;
while (*num_end &&
(isdigit(*num_end) || *num_end == '.' || *num_end == '-' ||
*num_end == '+' || *num_end == '/' || *num_end == '\'' || *num_end == '"')) {
num_end++;
}
if (num_end > before) has_pattern = 1;
}
}
}
return has_pattern;
@@ -153,58 +153,58 @@ static double parse_value(const char **ptr) {
if (*p == '-' || *p == '+') p++;
while (*p >= '0' && *p <= '9') {
value = value * 10 + (*p - '0');
value = value * 10 + (*p - '0');
has_num = 1;
p++;
}
if (*p == '.') {
p++;
double frac = 0.1;
while (*p >= '0' && *p <= '9') {
value += (*p - '0') * frac;
frac *= 0.1;
has_num = 1;
p++;
}
}
if (*p == '/' && has_num) {
p++;
double denom = 0.0;
int has_denom = 0;
while (*p >= '0' && *p <= '9') {
denom = denom * 10 + (*p - '0');
has_denom = 1;
p++;
}
if (has_denom && denom > 0) {
value = value / denom;
}
}
while (*p == '\'' || *p == '"') {
double extra = 0.0;
p++;
while (*p >= '0' && *p <= '9') {
extra = extra * 10 + (*p - '0');
p++;
}
if (*p == '.') {
p++;
double frac = 0.1;
while (*p >= '0' && *p <= '9') {
value += (*p - '0') * frac;
frac *= 0.1;
has_num = 1;
p++;
extra += (*p - '0') * frac;
frac *= 0.1;
p++;
}
}
if (*p == '/' && has_num) {
p++;
double denom = 0.0;
int has_denom = 0;
while (*p >= '0' && *p <= '9') {
denom = denom * 10 + (*p - '0');
has_denom = 1;
p++;
}
if (has_denom && denom > 0) {
value = value / denom;
}
}
while (*p == '\'' || *p == '"') {
double extra = 0.0;
p++;
while (*p >= '0' && *p <= '9') {
extra = extra * 10 + (*p - '0');
p++;
}
if (*p == '.') {
p++;
double frac = 0.1;
while (*p >= '0' && *p <= '9') {
extra += (*p - '0') * frac;
frac *= 0.1;
p++;
}
}
if (*p == '\'' || *p == '"') p++;
value += extra * (p[-1] == '\'' ? 0.3048 : 0.0254);
if (*p == '\'' || *p == '"') p++;
value += extra * (p[-1] == '\'' ? 0.3048 : 0.0254);
}
if (!has_num) {
*ptr = p;
return 0.0;
*ptr = p;
return 0.0;
}
*ptr = p;
@@ -235,29 +235,29 @@ static int parse_conversion_query(const char *query, double *value, const UnitDe
const char *to_pos = NULL;
size_t keyword_len = 0;
for (int i = 0; to_keywords[i]; i++) {
const char *found = strstr(p, to_keywords[i]);
if (found) {
to_pos = found + strlen(to_keywords[i]);
keyword_len = strlen(to_keywords[i]);
break;
}
const char *found = strstr(p, to_keywords[i]);
if (found) {
to_pos = found + strlen(to_keywords[i]);
keyword_len = strlen(to_keywords[i]);
break;
}
}
if (!to_pos) {
const char *last_space = strrchr(p, ' ');
if (last_space && last_space > p) {
char from_part[64] = {0};
size_t len = last_space - p;
if (len < 63) {
strncpy(from_part, p, len);
*from_unit = find_unit(from_part);
if (*from_unit) {
*to_unit = find_unit(last_space + 1);
return *to_unit ? 1 : 0;
}
}
const char *last_space = strrchr(p, ' ');
if (last_space && last_space > p) {
char from_part[64] = {0};
size_t len = last_space - p;
if (len < 63) {
strncpy(from_part, p, len);
*from_unit = find_unit(from_part);
if (*from_unit) {
*to_unit = find_unit(last_space + 1);
return *to_unit ? 1 : 0;
}
return 0;
}
}
return 0;
}
char from_part[64] = {0};
@@ -271,20 +271,20 @@ static int parse_conversion_query(const char *query, double *value, const UnitDe
*from_unit = find_unit(from_part);
if (!*from_unit) {
char *end = from_part + strlen(from_part);
while (end > from_part) {
while (end > from_part && is_whitespace(end[-1])) end--;
if (end <= from_part) break;
char *start = end;
while (start > from_part && !is_whitespace(start[-1])) start--;
size_t word_len = end - start;
memmove(from_part + word_len + 1, from_part, start - from_part);
from_part[word_len] = ' ';
from_part[word_len + 1] = '\0';
*from_unit = find_unit(from_part);
if (*from_unit) break;
end = start;
}
char *end = from_part + strlen(from_part);
while (end > from_part) {
while (end > from_part && is_whitespace(end[-1])) end--;
if (end <= from_part) break;
char *start = end;
while (start > from_part && !is_whitespace(start[-1])) start--;
size_t word_len = end - start;
memmove(from_part + word_len + 1, from_part, start - from_part);
from_part[word_len] = ' ';
from_part[word_len + 1] = '\0';
*from_unit = find_unit(from_part);
if (*from_unit) break;
end = start;
}
}
if (!*from_unit) return 0;
@@ -297,30 +297,30 @@ static int parse_conversion_query(const char *query, double *value, const UnitDe
size_t to_len = 0;
const char *tp = to_pos;
while (*tp && !is_separator(*tp) && to_len < 63) {
to_part[to_len++] = *tp++;
to_part[to_len++] = *tp++;
}
to_part[to_len] = '\0';
*to_unit = find_unit(to_part);
if (!*to_unit) {
const char *try_ptr = to_pos;
while (*try_ptr && is_whitespace(*try_ptr)) try_ptr++;
char try_buf[64] = {0};
size_t try_len = 0;
while (*try_ptr && try_len < 63) {
try_buf[try_len++] = *try_ptr++;
}
while (try_len > 0) {
*to_unit = find_unit(try_buf);
if (*to_unit) {
strcpy(to_part, try_buf);
break;
}
char *last_space = strrchr(try_buf, ' ');
if (!last_space) break;
*last_space = '\0';
try_len = strlen(try_buf);
const char *try_ptr = to_pos;
while (*try_ptr && is_whitespace(*try_ptr)) try_ptr++;
char try_buf[64] = {0};
size_t try_len = 0;
while (*try_ptr && try_len < 63) {
try_buf[try_len++] = *try_ptr++;
}
while (try_len > 0) {
*to_unit = find_unit(try_buf);
if (*to_unit) {
strcpy(to_part, try_buf);
break;
}
char *last_space = strrchr(try_buf, ' ');
if (!last_space) break;
*last_space = '\0';
try_len = strlen(try_buf);
}
}
return *to_unit ? 1 : 0;
@@ -343,7 +343,7 @@ static double convert_value(double value, const UnitDef *from, const UnitDef *to
if (from->type != to->type) return 0;
if (from->type == UNIT_TEMP) {
return convert_temp(value, from, to);
return convert_temp(value, from, to);
}
double base_value = value * from->to_base;
@@ -353,23 +353,23 @@ static double convert_value(double value, const UnitDef *from, const UnitDef *to
static void format_number(double val, char *buf, size_t bufsize) {
if (bufsize == 0) return;
if (val == 0) {
snprintf(buf, bufsize, "0");
return;
snprintf(buf, bufsize, "0");
return;
}
if (fabs(val) < 0.01 && fabs(val) > 0) {
snprintf(buf, bufsize, "%.2g", val);
snprintf(buf, bufsize, "%.2g", val);
} else if (fabs(val) < 1) {
snprintf(buf, bufsize, "%.2f", val);
char *p = buf + strlen(buf) - 1;
while (p > buf && *p == '0') *p-- = '\0';
if (*p == '.') *p = '\0';
snprintf(buf, bufsize, "%.2f", val);
char *p = buf + strlen(buf) - 1;
while (p > buf && *p == '0') *p-- = '\0';
if (*p == '.') *p = '\0';
} else if (fmod(val + 0.0001, 1.0) < 0.0002) {
snprintf(buf, bufsize, "%.0f", val);
snprintf(buf, bufsize, "%.0f", val);
} else {
snprintf(buf, bufsize, "%.2f", val);
char *p = buf + strlen(buf) - 1;
while (p > buf && *p == '0') *p-- = '\0';
if (*p == '.') *p = '\0';
snprintf(buf, bufsize, "%.2f", val);
char *p = buf + strlen(buf) - 1;
while (p > buf && *p == '0') *p-- = '\0';
if (*p == '.') *p = '\0';
}
}
@@ -383,74 +383,74 @@ static const char *pluralize(const char *unit, double value, char *buf, size_t b
buf[bufsize - 1] = '\0';
if (strcmp(unit, "foot") == 0 || strcmp(unit, "square foot") == 0) {
if (is_one) strcpy(buf, unit);
else strcpy(buf, strcmp(unit, "square foot") == 0 ? "square feet" : "feet");
return buf;
if (is_one) strcpy(buf, unit);
else strcpy(buf, strcmp(unit, "square foot") == 0 ? "square feet" : "feet");
return buf;
}
if (strcmp(unit, "inch") == 0 || strcmp(unit, "square inch") == 0) {
if (is_one) strcpy(buf, unit);
else strcpy(buf, strcmp(unit, "square inch") == 0 ? "square inches" : "inches");
return buf;
if (is_one) strcpy(buf, unit);
else strcpy(buf, strcmp(unit, "square inch") == 0 ? "square inches" : "inches");
return buf;
}
if (strcmp(unit, "stone") == 0) {
if (is_one) strcpy(buf, "stone");
else strcpy(buf, "stones");
return buf;
if (is_one) strcpy(buf, "stone");
else strcpy(buf, "stones");
return buf;
}
if (strcmp(unit, "celsius") == 0 ||
strcmp(unit, "fahrenheit") == 0 ||
strcmp(unit, "kelvin") == 0) {
strcpy(buf, unit);
return buf;
strcmp(unit, "fahrenheit") == 0 ||
strcmp(unit, "kelvin") == 0) {
strcpy(buf, unit);
return buf;
}
if (unit[len-1] == 's' ||
unit[len-1] == 'x' ||
unit[len-1] == 'z' ||
(len >= 2 && unit[len-2] == 'c' && unit[len-1] == 'h') ||
(len >= 2 && unit[len-2] == 's' && unit[len-1] == 'h')) {
if (!is_one) {
buf[len] = 'e';
buf[len+1] = '\0';
}
unit[len-1] == 'x' ||
unit[len-1] == 'z' ||
(len >= 2 && unit[len-2] == 'c' && unit[len-1] == 'h') ||
(len >= 2 && unit[len-2] == 's' && unit[len-1] == 'h')) {
if (!is_one) {
buf[len] = 'e';
buf[len+1] = '\0';
}
} else if (unit[len-1] == 'y' && len >= 2 &&
!(unit[len-2] == 'a' || unit[len-2] == 'e' ||
unit[len-2] == 'i' || unit[len-2] == 'o' ||
unit[len-2] == 'u')) {
if (is_one) {
buf[len-1] = '\0';
} else {
buf[len] = 's';
buf[len+1] = '\0';
}
} else if (len >= 2 && unit[len-2] == 'f' && unit[len-1] == 'e') {
if (is_one) {
buf[len-2] = '\0';
} else {
buf[len-1] = 's';
buf[len] = '\0';
}
} else if (unit[len-1] == 'f' && len >= 1) {
if (is_one) {
buf[len-1] = '\0';
} else {
buf[len-1] = 'v';
buf[len] = 'e';
buf[len+1] = 's';
buf[len+2] = '\0';
}
} else if (unit[len-1] == 'e' && len >= 2 && unit[len-2] == 'f') {
if (is_one) {
buf[len-2] = '\0';
} else {
buf[len-1] = 's';
buf[len] = '\0';
}
!(unit[len-2] == 'a' || unit[len-2] == 'e' ||
unit[len-2] == 'i' || unit[len-2] == 'o' ||
unit[len-2] == 'u')) {
if (is_one) {
buf[len-1] = '\0';
} else {
if (!is_one) {
buf[len] = 's';
buf[len+1] = '\0';
}
buf[len] = 's';
buf[len+1] = '\0';
}
} else if (len >= 2 && unit[len-2] == 'f' && unit[len-1] == 'e') {
if (is_one) {
buf[len-2] = '\0';
} else {
buf[len-1] = 's';
buf[len] = '\0';
}
} else if (unit[len-1] == 'f' && len >= 1) {
if (is_one) {
buf[len-1] = '\0';
} else {
buf[len-1] = 'v';
buf[len] = 'e';
buf[len+1] = 's';
buf[len+2] = '\0';
}
} else if (unit[len-1] == 'e' && len >= 2 && unit[len-2] == 'f') {
if (is_one) {
buf[len-2] = '\0';
} else {
buf[len-1] = 's';
buf[len] = '\0';
}
} else {
if (!is_one) {
buf[len] = 's';
buf[len+1] = '\0';
}
}
return buf;
@@ -466,12 +466,12 @@ static char *build_html(double value, const UnitDef *from, double result, const
pluralize(to->name, result, to_name_buf, sizeof(to_name_buf));
int n = snprintf(html, sizeof(html),
"<div class='unit-conv-container' style='line-height: 1.6;'>"
"<div style='font-size: 1.3em; margin-bottom: 8px;'>"
"<b>%s %s</b> = <b>%s %s</b>"
"</div>",
val_buf, from_name_buf,
res_buf, to_name_buf);
"<div class='unit-conv-container' style='line-height: 1.6;'>"
"<div style='font-size: 1.3em; margin-bottom: 8px;'>"
"<b>%s %s</b> = <b>%s %s</b>"
"</div>",
val_buf, from_name_buf,
res_buf, to_name_buf);
snprintf(html + n, sizeof(html) - n, "</div>");
return html;
}

View File

@@ -23,32 +23,32 @@ static void shorten_summary(char **extract_ptr, int max_chars) {
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;
}
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;
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) {
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;
fprintf(stderr, "Not enough memory (realloc returned NULL)\n");
return 0;
}
mem->memory = ptr;
@@ -63,48 +63,48 @@ 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);
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);
}
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] = '_';
}
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);
}
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);
}
extract_wiki_info(cur_node->children, info);
}
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);
}
}
@@ -120,27 +120,27 @@ InfoBox fetch_wiki_data(char *api_url) {
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");
apply_proxy_settings(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");
apply_proxy_settings(curl_handle);
res = curl_easy_perform(curl_handle);
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);
}
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);
curl_easy_cleanup(curl_handle);
free(chunk.memory);
}
return info;
@@ -152,14 +152,14 @@ char *construct_wiki_url(const char *search_term) {
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=";
"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);
strcpy(full_url, base);
strcat(full_url, escaped_term);
}
curl_free(escaped_term);