From 51258871e3ad354ceebc4912548178705b5700d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=A6=8C=EA=A6=AB=EA=A6=B6=EA=A6=8F=EA=A7=80=EA=A6=A6?= =?UTF-8?q?=EA=A6=BF=EA=A6=A7=EA=A6=AE=EA=A6=91=EA=A6=A9=EA=A6=AD=EA=A7=80?= Date: Mon, 14 Jun 2021 15:24:23 +0800 Subject: Strap results handler --- Makefile.am | 22 ++++++--- client.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- configure.ac | 1 + icclient.h | 8 ++-- main.c | 82 +++++-------------------------- request.c | 3 +- request.h | 16 ++----- 7 files changed, 192 insertions(+), 94 deletions(-) diff --git a/Makefile.am b/Makefile.am index 2929e04..f783d0d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,18 +8,28 @@ libicclient_la_SOURCES = \ login.c \ member.c \ admin.c -libicclient_la_CPPFLAGS = -I${prefix}/include -if WASM -libicclient_la_LDFLAGS = -static -else -libicclient_la_LDFLAGS = -lcurl -endif +libicclient_la_CPPFLAGS = -I${prefix}/include $(TIDY_CFLAGS) +libicclient_la_LDFLAGS = $(TIDY_LIBS) if IOS libicclient_la_LDFLAGS += -static endif +if WASM +libicclient_la_LDFLAGS += -static +else +libicclient_la_LDFLAGS += -lcurl +endif include_HEADERS = icclient.h pkginclude_HEADERS = \ icclient/typedefs.h \ icclient/ord.h \ icclient/member.h \ icclient/admin.h +bin_PROGRAMS = icclient +icclient_SOURCES = \ + main.c \ + client.c \ + request.c \ + login.c \ + member.c +icclient_CPPFLAGS = $(TIDY_CFLAGS) +icclient_LDFLAGS = -lcurl $(TIDY_LIBS) diff --git a/client.c b/client.c index 4d8530a..fd7dda2 100644 --- a/client.c +++ b/client.c @@ -1,10 +1,156 @@ #include +#include +#include +#include +#include +#include #include "request.h" #include "icclient.h" -void icclient_init(const char *url, const char *certificate) +char *sampleurl; +static char *image_dir; +static locale_t loc = 0; + +void icclient_init(const char *url, const char *dir, const char *certificate) +{ + size_t length = strlen(url); + size_t append = url[length - 1] != '/'; + sampleurl = malloc(length + append + 1); + strcpy(sampleurl, url); + if (append) + strcat(sampleurl, "/"); + image_dir = malloc(strlen(dir) + 1); + strcpy(image_dir, dir); + init(certificate); +} + +static void dumpNode(TidyDoc doc, TidyNode tnod, struct icclient_catalog **catalog, char **category, + bool is_sku, bool is_category, bool is_price) +{ + struct icclient_product *product; + for (TidyNode child = tidyGetChild(tnod); child; child = tidyGetNext(child)) { + ctmbstr name = tidyNodeGetName(child); + if (name) { + if (!strcmp(name, "html")) { + char locale[11]; + for (TidyAttr attr = tidyAttrFirst(child); attr; attr = tidyAttrNext(attr)) { + if (!strcmp(tidyAttrValue(attr), "en")) + strcpy(locale, "en_US.utf8"); + else if (!strcmp(tidyAttrValue(attr), "id")) + strcpy(locale, "id_ID.utf8"); + if (!loc) + loc = newlocale(LC_MONETARY_MASK | LC_NUMERIC_MASK, locale, 0); + } + } else if (!strcmp(name, "h3")) { + is_sku = false; + is_category = true; + is_price = false; + } else if (!strcmp(name, "img")) { + static const char *subdir = "/items/"; + char prefix[strlen(image_dir) + strlen(subdir) + 1]; + sprintf(prefix, "%s%s", image_dir, subdir); + size_t prefix_len = strlen(prefix); + for (TidyAttr attr = tidyAttrFirst(child); attr; attr = tidyAttrNext(attr)) { + if (!strcmp(tidyAttrName(attr), "src")) { + if (strncmp(tidyAttrValue(attr), prefix, prefix_len)) + break; + product = malloc(sizeof(struct icclient_product)); + memset(product, '\0', sizeof(struct icclient_product)); + size_t len = strlen(tidyAttrValue(attr)) - prefix_len; + product->image = malloc(len + 1); + strncpy(product->image, tidyAttrValue(attr) + prefix_len, len + 1); + } + if (strcmp(tidyAttrName(attr), "title")) + continue; + product->description = malloc(strlen(tidyAttrValue(attr)) + 1); + strcpy(product->description, tidyAttrValue(attr)); + if (*category) { + product->category = malloc(strlen(*category) + 1); + strcpy(product->category, *category); + } + (*catalog)->length++; + *catalog = realloc(*catalog, sizeof(struct icclient_catalog) + + sizeof(struct icclient_product *[(*catalog)->length])); + (*catalog)->products[(*catalog)->length - 1] = product; + } + } else if (!strcmp(name, "h4")) { + is_sku = true; + is_category = false; + is_price = false; + } else if (is_sku && !strcmp(name, "a")) { + is_sku = false; + for (TidyAttr attr = tidyAttrFirst(child); attr; attr = tidyAttrNext(attr)) { + char *sku = strrchr(tidyAttrValue(attr), '/'); + char *session_id = strchr(sku++, '?'); + if (session_id) + *session_id = '\0'; + product = (*catalog)->products[(*catalog)->length - 1]; + product->sku = malloc(strlen(sku) + 1); + strcpy(product->sku, sku); + } + } else if (!strcmp(name, "p") && !((*catalog)->products[(*catalog)->length - 1])->price) { + is_sku = false; + is_category = false; + is_price = true; + } else { + is_sku = false; + is_category = false; + is_price = false; + } + } else if (is_category || is_price) { + TidyBuffer buf; + tidyBufInit(&buf); + tidyNodeGetText(doc, child, &buf); + char *text = (char *)buf.bp; + product = (*catalog)->products[(*catalog)->length - 1]; + if (is_category) { + is_category = false; + text[strlen(text) - 1] = '\0'; + if (*category) + free(*category); + *category = malloc(strlen(text) + 1); + strcpy(*category, text); + } else { + is_price = false; +#ifndef __ANDROID__ + char *symbol = nl_langinfo_l(CRNCYSTR, loc); + size_t symbol_len = strlen(symbol) - 1; + if (symbol[0] == '-') + text += symbol_len; + char *separator = nl_langinfo_l(THOUSEP, loc); + if (strstr(text, separator)) { + size_t price_len = strlen(text) - symbol_len - 1; + char price[price_len]; + memset(price, '\0', price_len); + strcat(price, strtok(text, separator)); + strcat(price, strtok(NULL, separator)); + product->price = atof(price); + } else + product->price = atof(text); +#endif + } + tidyBufFree(&buf); + } + dumpNode(doc, child, catalog, category, is_sku, is_category, is_price); + } +} + +void handle_results(icclient_fetch_t *fetch) { - init(url, certificate); + TidyDoc tdoc = tidyCreate(); + tidyOptSetBool(tdoc, TidyXhtmlOut, yes); + tidyParseString(tdoc, fetch->data); + tidyCleanAndRepair(tdoc); + tidyOptSetBool(tdoc, TidyForceOutput, yes); + TidyBuffer output = {0}; + tidyBufInit(&output); + tidySaveBuffer(tdoc, &output); + struct icclient_catalog *catalog = malloc(sizeof(struct icclient_catalog)); + catalog->length = 0; + dumpNode(tdoc, tidyGetRoot(tdoc), &catalog, &(char *){NULL}, false, false, false); + tidyBufFree(&output); + tidyRelease(tdoc); + ((void (*)(struct icclient_catalog *))fetch->userData)(catalog); } void icclient_results(const char *prod_group, void (*callback)(struct icclient_catalog *), void (*handler)(icclient_fetch_t *)) @@ -14,7 +160,7 @@ void icclient_results(const char *prod_group, void (*callback)(struct icclient_c char *space = NULL; while ((space = strchr(nonspaced, ' '))) *space = '-'; - request(handler, (void *)callback, 0, "%s", nonspaced); + request(handler ? handler : handle_results, (void *)callback, 0, "%s", nonspaced); } void icclient_flypage(const char *sku, void (*handler)(icclient_fetch_t *), struct icclient_product **productptr) @@ -57,6 +203,8 @@ void icclient_free_catalog(struct icclient_catalog *catalog) void icclient_cleanup() { + if (loc) + freelocale(loc); #ifndef __EMSCRIPTEN__ cleanup(); #endif diff --git a/configure.ac b/configure.ac index ba95804..e20dbb9 100644 --- a/configure.ac +++ b/configure.ac @@ -3,6 +3,7 @@ AM_INIT_AUTOMAKE([-Wall -Werror foreign]) AC_PROG_CC AM_PROG_AR LT_INIT +PKG_CHECK_MODULES([TIDY], [tidy]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([Makefile]) AC_CHECK_HEADER_STDBOOL diff --git a/icclient.h b/icclient.h index d537263..38fdb6e 100644 --- a/icclient.h +++ b/icclient.h @@ -1,7 +1,7 @@ #ifndef ICCLIENT_H #define ICCLIENT_H -#include +#include "icclient/typedefs.h" #define icclient_allproducts(callback, handler) icclient_results("All-Products", callback, handler) @@ -13,6 +13,7 @@ struct icclient_product { char *image; double price; char *prod_group; + char *category; double weight; char *author; struct icclient_product_crosssell { @@ -32,11 +33,12 @@ extern "C" { /*! * \brief A function that needs to be run first. - * \param url Server root URL. + * \param sampleurl The value of the SAMPLEURL setting in products/variable.txt. + * \param image_dir The value of the IMAGE_DIR setting in products/variable.txt. * \param certificate Path to the CA certificate file. * \return True if the initialisation works, false otherwise. */ -void icclient_init(const char *url, const char *certificate); +void icclient_init(const char *sampleurl, const char *image_dir, const char *certificate); /*! * \brief For fetching data about products that belong a specific group. diff --git a/main.c b/main.c index 45ec093..b3e4f4f 100644 --- a/main.c +++ b/main.c @@ -2,7 +2,6 @@ #include #include #include -#include #include "icclient.h" #include "icclient/member.h" @@ -10,86 +9,31 @@ static void print_catalog(struct icclient_catalog *catalog) { for (size_t i = 0; i < catalog->length; i++) { struct icclient_product *product = catalog->products[i]; - printf("SKU: %s\n" - "Description: %s\n" - "Thumb: %s\n" - "Image: %s\n" - "Price: %f\n" - "Product Group: %s\n", + printf("sku: %s | " + "desc: %s | " + "img: %s | " + "prc: %f | " + "cat: %s\n", product->sku, product->description, - product->thumb, product->image, product->price, - product->prod_group + product->category ); } icclient_free_catalog(catalog); } - -static void handle_results(icclient_fetch_t *fetch) -{ - json_tokener *tokener = json_tokener_new(); - json_object *products = json_tokener_parse_ex(tokener, fetch->data, fetch->numBytes); - json_tokener_free(tokener); - size_t length = json_object_array_length(products); - struct icclient_catalog *catalog = malloc(sizeof(struct icclient_catalog) + sizeof(struct icclient_product *[length])); - catalog->length = length; - for (size_t i = 0; i < length; i++) { - catalog->products[i] = malloc(sizeof(struct icclient_product)); - struct icclient_product *product = catalog->products[i]; - memset(product, '\0', sizeof(struct icclient_product)); - json_object *object = json_object_array_get_idx(products, i); - struct json_object_iterator iterator = json_object_iter_begin(object); - struct json_object_iterator iterator_end = json_object_iter_end(object); - while (!json_object_iter_equal(&iterator, &iterator_end)) { - const char *key = json_object_iter_peek_name(&iterator); - json_object *val = json_object_iter_peek_value(&iterator); - if (!strcmp(key, "price")) - product->price = json_object_get_double(val); - else { - int len = json_object_get_string_len(val); - if (len) { - char *value = malloc(len + 1); - strcpy(value, json_object_get_string(val)); - if (!strcmp(key, "sku")) - product->sku = value; - else if (!strcmp(key, "thumb")) - product->thumb = value; - else if (!strcmp(key, "image")) - product->image = value; - else if (!strcmp(key, "description")) - product->description = value; - else if (!strcmp(key, "prod_group")) - product->prod_group = value; - } - } - json_object_iter_next(&iterator); - } - } - ((void (*)(struct icclient_catalog *))fetch->userData)(catalog); -} - +/* static void print_user(icclient_fetch_t *fetch) { printf("%s\n", fetch->data); } - +*/ int main(int argc, char *argv[]) { - char *url_line = NULL; - printf("\nURL: "); - ssize_t url_nread = getline(&url_line, &(size_t){0}, stdin); - char *url = malloc(--url_nread + 1); - strncpy(url, url_line, url_nread); - free(url_line); - - printf("\n"); - icclient_init(url, NULL); - free(url); - - icclient_allproducts(print_catalog, handle_results); - + icclient_init("https://demo.interchangecommerce.org/i/demo", "/demo/images", NULL); + icclient_allproducts(print_catalog, NULL); +/* char *name_line = NULL; printf("\nName: "); ssize_t name_nread = getline(&name_line, &(size_t){0}, stdin); @@ -108,7 +52,7 @@ int main(int argc, char *argv[]) struct icclient_member *member = icclient_member_login(name, pass, NULL, NULL, NULL, print_user); free(name); free(pass); - //icclient_member_logout(member); - + icclient_member_logout(member); +*/ icclient_cleanup(); } diff --git a/request.c b/request.c index 835bf3a..0a5f7fd 100644 --- a/request.c +++ b/request.c @@ -4,7 +4,6 @@ emscripten_fetch_attr_t attr; #else CURL *curl; -char *server_url; size_t append(char *data, size_t size, size_t nmemb, icclient_fetch_t *fetch) { size_t realsize = size * nmemb; @@ -16,6 +15,6 @@ size_t append(char *data, size_t size, size_t nmemb, icclient_fetch_t *fetch) } #endif -extern inline void init(const char *, const char *); +extern inline void init(const char *); extern inline void request(void (*)(icclient_fetch_t *), void *, struct body *, char *, ...); extern inline void cleanup(); diff --git a/request.h b/request.h index dd19400..536db0f 100644 --- a/request.h +++ b/request.h @@ -25,11 +25,11 @@ struct body { extern emscripten_fetch_attr_t attr; #else extern CURL *curl; -extern char *server_url; +extern char *sampleurl; size_t append(char *, size_t, size_t, icclient_fetch_t *); #endif -inline void init(const char *url, const char *certificate) +inline void init(const char *certificate) { #ifdef __EMSCRIPTEN__ emscripten_fetch_attr_init(&attr); @@ -44,12 +44,6 @@ inline void init(const char *url, const char *certificate) #ifdef DEBUG curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); #endif - size_t length = strlen(url); - size_t append = url[length - 1] != '/'; - server_url = malloc(length + append + 1); - strcpy(server_url, url); - if (append) - strcat(server_url, "/"); #endif } @@ -60,7 +54,7 @@ inline void request(void (*handler)(icclient_fetch_t *), void *callback, struct unsigned int ival; size_t length = #ifndef __EMSCRIPTEN__ - strlen(server_url) + + strlen(sampleurl) + #endif strlen(fmt); @@ -90,7 +84,7 @@ inline void request(void (*handler)(icclient_fetch_t *), void *callback, struct #ifdef __EMSCRIPTEN__ memset(url, '\0', length + 1); #else - strcpy(url, server_url); + strcpy(url, sampleurl); #endif va_start(ap, fmt); @@ -158,7 +152,7 @@ inline void request(void (*handler)(icclient_fetch_t *), void *callback, struct #ifndef __EMSCRIPTEN__ inline void cleanup() { - free(server_url); + free(sampleurl); curl_easy_cleanup(curl); curl_global_cleanup(); } -- cgit v1.2.3