made formatting more consistent
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user