aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksi Lindeman <dec05eba@protonmail.com>2019-05-25 02:17:15 +0200
committerAleksi Lindeman <dec05eba@protonmail.com>2019-05-25 02:18:48 +0200
commit36c6ce46a1abdb08eb9193704b9fce6bc7f3646b (patch)
tree58e3152073ac2268267e6a4af7963e6528ae4969
Initial commit
-rw-r--r--.gitignore6
-rw-r--r--LICENSE13
-rw-r--r--README.md1
-rw-r--r--include/quickmedia/HtmlSearch.h31
-rw-r--r--include/quickmedia/NodeSearch.h29
-rw-r--r--include/quickmedia/XpathParser.h8
-rw-r--r--include/quickmedia/XpathTokenizer.h32
-rw-r--r--project.conf12
-rw-r--r--src/HtmlSearch.c130
-rw-r--r--src/NodeSearch.c34
-rw-r--r--src/XpathParser.c88
-rw-r--r--src/XpathTokenizer.c104
-rw-r--r--test_files/test.html478
-rw-r--r--tests/main.c33
14 files changed, 999 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0dee329
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+# Compiled sibs files
+sibs-build/
+compile_commands.json
+tests/sibs-build/
+tests/compile_commands.json
+.vscode/
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c4f5a79
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,13 @@
+Copyright 2019 Aleksi Lindeman
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a6e5584
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+Html search using xpath, written in C
diff --git a/include/quickmedia/HtmlSearch.h b/include/quickmedia/HtmlSearch.h
new file mode 100644
index 0000000..e3bea33
--- /dev/null
+++ b/include/quickmedia/HtmlSearch.h
@@ -0,0 +1,31 @@
+#ifndef QUICKMEDIA_HTML_SEARCH_H
+#define QUICKMEDIA_HTML_SEARCH_H
+
+#include "NodeSearch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ const void *doc;
+ const void *node;
+ void *text;
+} QuickMediaHtmlNode;
+
+/* Returns NULL if attribute doesn't exist or if it doesn't have any value */
+const char* quickmedia_html_node_get_attribute_value(QuickMediaHtmlNode *self, const char *attribute_name);
+
+/* Returns StringView where data is NULL and size is 0 if node doesn't have any text */
+const QuickMediaStringView quickmedia_html_node_get_text(QuickMediaHtmlNode *self);
+
+/* @node is only valid within the callback function scope */
+typedef void (*QuickMediaHtmlSearchResultCallback)(QuickMediaHtmlNode *node, void *userdata);
+
+int quickmedia_html_find_nodes_xpath(const char *html_source, const char *xpath, QuickMediaHtmlSearchResultCallback result_callback, void *userdata);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/quickmedia/NodeSearch.h b/include/quickmedia/NodeSearch.h
new file mode 100644
index 0000000..b512296
--- /dev/null
+++ b/include/quickmedia/NodeSearch.h
@@ -0,0 +1,29 @@
+#ifndef QUICKMEDIA_NODE_SEARCH_H
+#define QUICKMEDIA_NODE_SEARCH_H
+
+typedef struct {
+ char *name;
+ char *value;
+} QuickMediaNodeSearchParam;
+
+typedef struct QuickMediaNodeSearch QuickMediaNodeSearch;
+
+struct QuickMediaNodeSearch {
+ char *name; /* optional */
+ int recursive;
+ QuickMediaNodeSearchParam param; /* optional */
+ int param_defined;
+
+ QuickMediaNodeSearch *child; /* optional */
+};
+
+typedef struct {
+ const char *data;
+ unsigned long long size;
+} QuickMediaStringView;
+
+void quickmedia_node_search_param_init(QuickMediaNodeSearchParam *self);
+void quickmedia_node_search_init(QuickMediaNodeSearch *self);
+void quickmedia_node_search_deinit(QuickMediaNodeSearch *self);
+
+#endif
diff --git a/include/quickmedia/XpathParser.h b/include/quickmedia/XpathParser.h
new file mode 100644
index 0000000..2dfc81e
--- /dev/null
+++ b/include/quickmedia/XpathParser.h
@@ -0,0 +1,8 @@
+#ifndef QUICKMEDIA_XPATH_PARSER_H
+#define QUICKMEDIA_XPATH_PARSER_H
+
+#include "NodeSearch.h"
+
+int quickmedia_parse_xpath(const char *xpath, QuickMediaNodeSearch *result);
+
+#endif
diff --git a/include/quickmedia/XpathTokenizer.h b/include/quickmedia/XpathTokenizer.h
new file mode 100644
index 0000000..8827cff
--- /dev/null
+++ b/include/quickmedia/XpathTokenizer.h
@@ -0,0 +1,32 @@
+#ifndef QUICKMEDIA_XPATH_TOKENIZER_H
+#define QUICKMEDIA_XPATH_TOKENIZER_H
+
+#include "NodeSearch.h"
+
+typedef struct {
+ const char *code;
+ union {
+ QuickMediaStringView string;
+ QuickMediaStringView identifier;
+ };
+} QuickMediaXpathTokenizer;
+
+typedef enum {
+ QUICKMEDIA_XPATH_TOKEN_INVALID,
+ QUICKMEDIA_XPATH_TOKEN_END_OF_FILE,
+ QUICKMEDIA_XPATH_TOKEN_CHILD,
+ QUICKMEDIA_XPATH_TOKEN_CHILD_RECURSIVE,
+ QUICKMEDIA_XPATH_TOKEN_IDENTIFIER,
+ QUICKMEDIA_XPATH_TOKEN_STRING,
+ QUICKMEDIA_XPATH_TOKEN_OPEN_BRACKET,
+ QUICKMEDIA_XPATH_TOKEN_CLOSING_BRACKET,
+ QUICKMEDIA_XPATH_TOKEN_EQUAL
+} QuickMediaXpathToken;
+
+void quickmedia_xpath_tokenizer_init(QuickMediaXpathTokenizer *self, const char *xpath);
+QuickMediaXpathToken quickmedia_xpath_tokenizer_next(QuickMediaXpathTokenizer *self);
+int quickmedia_xpath_tokenizer_next_if(QuickMediaXpathTokenizer *self, QuickMediaXpathToken token);
+char* quickmedia_xpath_tokenizer_copy_identifier(QuickMediaXpathTokenizer *self);
+char* quickmedia_xpath_tokenizer_copy_string(QuickMediaXpathTokenizer *self);
+
+#endif
diff --git a/project.conf b/project.conf
new file mode 100644
index 0000000..6f63e20
--- /dev/null
+++ b/project.conf
@@ -0,0 +1,12 @@
+[package]
+name = "html-search"
+type = "static"
+version = "0.1.0"
+platforms = ["any"]
+
+[config]
+expose_include_dirs = ["include"]
+ignore_dirs = ["test_files"]
+
+[dependencies]
+tidy = "5" \ No newline at end of file
diff --git a/src/HtmlSearch.c b/src/HtmlSearch.c
new file mode 100644
index 0000000..e59dc1e
--- /dev/null
+++ b/src/HtmlSearch.c
@@ -0,0 +1,130 @@
+#include "../include/quickmedia/HtmlSearch.h"
+#include "../include/quickmedia/XpathParser.h"
+
+#include <tidy.h>
+#include <tidybuffio.h>
+
+static TidyAttr get_attribute_by_name(TidyNode node, const char *name) {
+ assert(name);
+ for(TidyAttr attr = tidyAttrFirst(node); attr; attr = tidyAttrNext(attr)) {
+ const char *attr_name = tidyAttrName(attr);
+ if(attr_name && strcmp(name, attr_name) == 0)
+ return attr;
+ }
+ return NULL;
+}
+
+static void find_child_nodes(TidyDoc tdoc, TidyNode node, const QuickMediaNodeSearch *search_data, QuickMediaHtmlSearchResultCallback result_callback, void *userdata) {
+ /* We use two loops because we want to find children before grandchildren */
+ for(TidyNode child = tidyGetChild(node); child; child = tidyGetNext(child)) {
+ const char *child_node_name = tidyNodeGetName(child);
+ /* A text node doesn't have a name */
+ if(!child_node_name)
+ continue;
+
+ /* Match without node name or node name matches */
+ if(!search_data->name || strcmp(search_data->name, child_node_name) == 0) {
+ #define on_match() do { \
+ if(search_data->child) \
+ find_child_nodes(tdoc, child, search_data->child, result_callback, userdata); \
+ else { \
+ QuickMediaHtmlNode node; \
+ node.doc = tdoc; \
+ node.node = child; \
+ node.text = NULL; \
+ result_callback(&node, userdata); \
+ if(node.text){ \
+ tidyBufFree(node.text); \
+ free(node.text); \
+ } \
+ } \
+ } while(0)
+
+ /* If we search without param, then it's a match */
+ if(!search_data->param_defined) {
+ on_match();
+ continue;
+ }
+
+ TidyAttr child_attr = get_attribute_by_name(child, search_data->param.name);
+ /* Couldn't find the param that we want to match against */
+ if(!child_attr)
+ continue;
+
+ const char *attr_value = tidyAttrValue(child_attr);
+ assert(search_data->param.value);
+ /* If the param value matches what we want to search for */
+ if(attr_value && strcmp(search_data->param.value, attr_value) == 0) {
+ on_match();
+ continue;
+ }
+ }
+ }
+
+ if(search_data->recursive) {
+ for(TidyNode child = tidyGetChild(node); child; child = tidyGetNext(child)) {
+ find_child_nodes(tdoc, child, search_data, result_callback, userdata);
+ }
+ }
+}
+
+const char* quickmedia_html_node_get_attribute_value(QuickMediaHtmlNode *self, const char *attribute_name) {
+ TidyAttr attr = get_attribute_by_name((TidyNode)self->node, attribute_name);
+ if(!attr)
+ return NULL;
+ return tidyAttrValue(attr);
+}
+
+const QuickMediaStringView quickmedia_html_node_get_text(QuickMediaHtmlNode *self) {
+ QuickMediaStringView string_view;
+ string_view.data = NULL;
+ string_view.size = 0;
+
+ if(self->text) {
+ string_view.data = (const char*)((TidyBuffer*)self->text)->bp;
+ string_view.size = ((TidyBuffer*)self->text)->size;
+ return string_view;
+ }
+
+ TidyNode child_node = tidyGetChild(self->node);
+ if(tidyNodeGetType(child_node) != TidyNode_Text)
+ return string_view;
+
+ self->text = malloc(sizeof(TidyBuffer));
+ tidyBufInit(self->text);
+ tidyNodeGetText(self->doc, child_node, self->text);
+
+ string_view.data = (const char*)((TidyBuffer*)self->text)->bp;
+ string_view.size = ((TidyBuffer*)self->text)->size;
+ return string_view;
+}
+
+static int quickmedia_html_find_nodes(const char *html_source, QuickMediaNodeSearch *search_data, QuickMediaHtmlSearchResultCallback result_callback, void *userdata) {
+ assert(html_source);
+ assert(search_data);
+ assert(result_callback);
+ if(!html_source || !search_data || !result_callback)
+ return -1;
+
+ TidyDoc tdoc = tidyCreate();
+ tidyOptSetBool(tdoc, TidyShowWarnings, no);
+ /* tidyOptSetBool(tdoc, TidyForceOutput, yes); */
+ int rc = tidyParseString( tdoc, html_source);
+ if(rc < 0) {
+ tidyRelease(tdoc);
+ return rc;
+ }
+
+ TidyNode root_node = tidyGetRoot(tdoc);
+ find_child_nodes(tdoc, root_node, search_data, result_callback, userdata);
+ tidyRelease(tdoc);
+ return 0;
+}
+
+int quickmedia_html_find_nodes_xpath(const char *html_source, const char *xpath, QuickMediaHtmlSearchResultCallback result_callback, void *userdata) {
+ QuickMediaNodeSearch search_data;
+ int xpath_result = quickmedia_parse_xpath(xpath, &search_data);
+ if(xpath_result != 0)
+ return xpath_result;
+ return quickmedia_html_find_nodes(html_source, &search_data, result_callback, userdata);
+}
diff --git a/src/NodeSearch.c b/src/NodeSearch.c
new file mode 100644
index 0000000..198b8cd
--- /dev/null
+++ b/src/NodeSearch.c
@@ -0,0 +1,34 @@
+#include "../include/quickmedia/NodeSearch.h"
+#include <stdlib.h>
+
+void quickmedia_node_search_param_init(QuickMediaNodeSearchParam *self) {
+ self->name = NULL;
+ self->value = NULL;
+}
+
+static void quickmedia_node_search_param_deinit(QuickMediaNodeSearchParam *self) {
+ free(self->name);
+ free(self->value);
+ self->name = NULL;
+ self->value = NULL;
+}
+
+void quickmedia_node_search_init(QuickMediaNodeSearch *self) {
+ self->name = NULL;
+ self->recursive = 0;
+ quickmedia_node_search_param_init(&self->param);
+ self->param_defined = 0;
+ self->child = NULL;
+}
+
+void quickmedia_node_search_deinit(QuickMediaNodeSearch *self) {
+ free(self->name);
+ self->name = NULL;
+ quickmedia_node_search_param_deinit(&self->param);
+
+ if(self->child) {
+ quickmedia_node_search_deinit(self->child);
+ free(self->child);
+ self->child = NULL;
+ }
+}
diff --git a/src/XpathParser.c b/src/XpathParser.c
new file mode 100644
index 0000000..24e1d6e
--- /dev/null
+++ b/src/XpathParser.c
@@ -0,0 +1,88 @@
+#include "../include/quickmedia/XpathParser.h"
+#include "../include/quickmedia/XpathTokenizer.h"
+#include <stdlib.h>
+
+typedef struct {
+ QuickMediaXpathTokenizer tokenizer;
+} QuickMediaXpathParser;
+
+static void quickmedia_xpath_parser_init(QuickMediaXpathParser *self, const char *xpath) {
+ quickmedia_xpath_tokenizer_init(&self->tokenizer, xpath);
+}
+
+/* ('[' IDENTIFIER '=' '"' STRING '"' ']')? */
+static int xpath_parse_param(QuickMediaXpathParser *self, QuickMediaNodeSearchParam *result) {
+ if(quickmedia_xpath_tokenizer_next_if(&self->tokenizer, QUICKMEDIA_XPATH_TOKEN_OPEN_BRACKET) != 0)
+ return 1;
+
+ QuickMediaXpathToken token = quickmedia_xpath_tokenizer_next(&self->tokenizer);
+ if(token != QUICKMEDIA_XPATH_TOKEN_IDENTIFIER)
+ return -1;
+
+ result->name = quickmedia_xpath_tokenizer_copy_identifier(&self->tokenizer);
+
+ token = quickmedia_xpath_tokenizer_next(&self->tokenizer);
+ if(token != QUICKMEDIA_XPATH_TOKEN_EQUAL)
+ return -2;
+
+ token = quickmedia_xpath_tokenizer_next(&self->tokenizer);
+ if(token != QUICKMEDIA_XPATH_TOKEN_STRING)
+ return -3;
+
+ result->value = quickmedia_xpath_tokenizer_copy_string(&self->tokenizer);
+
+ token = quickmedia_xpath_tokenizer_next(&self->tokenizer);
+ if(token != QUICKMEDIA_XPATH_TOKEN_CLOSING_BRACKET)
+ return -4;
+
+ return 0;
+}
+
+static int xpath_parse_node(QuickMediaXpathParser *self, QuickMediaNodeSearch *result) {
+ quickmedia_node_search_init(result);
+ QuickMediaXpathToken token = quickmedia_xpath_tokenizer_next(&self->tokenizer);
+ /* // or / */
+ if(token == QUICKMEDIA_XPATH_TOKEN_CHILD || token == QUICKMEDIA_XPATH_TOKEN_CHILD_RECURSIVE) {
+ result->recursive = (token == QUICKMEDIA_XPATH_TOKEN_CHILD_RECURSIVE);
+
+ token = quickmedia_xpath_tokenizer_next(&self->tokenizer);
+ if(token != QUICKMEDIA_XPATH_TOKEN_IDENTIFIER)
+ return -1;
+
+ result->name = quickmedia_xpath_tokenizer_copy_identifier(&self->tokenizer);
+
+ int param_result = xpath_parse_param(self, &result->param);
+ if(param_result < 0) {
+ quickmedia_node_search_deinit(result);
+ return param_result;
+ } else if(param_result == 0) {
+ result->param_defined = 1;
+ }
+
+ result->child = malloc(sizeof(QuickMediaNodeSearch));
+ int node_result = xpath_parse_node(self, result->child);
+ if(node_result > 0) {
+ node_result = 0;
+ /* Didn't have child, remove child */
+ free(result->child);
+ result->child = NULL;
+ } else if(node_result < 0) {
+ quickmedia_node_search_deinit(result);
+ }
+
+ return node_result;
+ } else if(token == QUICKMEDIA_XPATH_TOKEN_END_OF_FILE) {
+ return 1;
+ } else {
+ return -2;
+ }
+}
+
+int quickmedia_parse_xpath(const char *xpath, QuickMediaNodeSearch *result) {
+ QuickMediaXpathParser parser;
+ quickmedia_xpath_parser_init(&parser, xpath);
+ int parse_result = xpath_parse_node(&parser, result);
+ if(parse_result > 0)
+ parse_result = -1;
+ return parse_result;
+}
diff --git a/src/XpathTokenizer.c b/src/XpathTokenizer.c
new file mode 100644
index 0000000..32bede9
--- /dev/null
+++ b/src/XpathTokenizer.c
@@ -0,0 +1,104 @@
+#include "../include/quickmedia/XpathTokenizer.h"
+#include <stdlib.h>
+#include <string.h>
+
+void quickmedia_xpath_tokenizer_init(QuickMediaXpathTokenizer *self, const char *xpath) {
+ self->code = xpath;
+ self->identifier.data = NULL;
+ self->identifier.size = 0;
+}
+
+static int is_alpha(char c) {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+}
+
+static int is_num(char c) {
+ return c >= '0' && c <= '9';
+}
+
+static int is_alphanum(char c) {
+ return is_alpha(c) || is_num(c);
+}
+
+static const char* find_end_of_string(const char *str, char escape_symbol) {
+ int escape = 0;
+ while(*str != '\0') {
+ char c = *str;
+ if(c == '\\') {
+ escape = !escape;
+ } else if(c == escape_symbol) {
+ if(!escape)
+ return str;
+ } else {
+ escape = 0;
+ }
+ ++str;
+ }
+ return str;
+}
+
+QuickMediaXpathToken quickmedia_xpath_tokenizer_next(QuickMediaXpathTokenizer *self) {
+ char c = *self->code;
+ if(c == '/') {
+ ++self->code;
+ c = *self->code;
+ if(c == '/') {
+ ++self->code;
+ return QUICKMEDIA_XPATH_TOKEN_CHILD_RECURSIVE;
+ }
+ return QUICKMEDIA_XPATH_TOKEN_CHILD;
+ } else if(is_alpha(c)) {
+ self->identifier.data = self->code;
+ ++self->code;
+ while(is_alphanum(*self->code) || *self->code == '_' || *self->code == '-') {
+ ++self->code;
+ }
+ self->identifier.size = self->code - self->identifier.data;
+ return QUICKMEDIA_XPATH_TOKEN_IDENTIFIER;
+ } else if(c == '[') {
+ ++self->code;
+ return QUICKMEDIA_XPATH_TOKEN_OPEN_BRACKET;
+ } else if(c == ']') {
+ ++self->code;
+ return QUICKMEDIA_XPATH_TOKEN_CLOSING_BRACKET;
+ } else if(c == '=') {
+ ++self->code;
+ return QUICKMEDIA_XPATH_TOKEN_EQUAL;
+ } else if(c == '"' || c == '\'') {
+ char escape_symbol = c;
+ ++self->code;
+ self->string.data = self->code;
+ self->code = find_end_of_string(self->string.data, escape_symbol);
+ if(*self->code == '\0') {
+ /* Reached end of xpath before end of string */
+ return QUICKMEDIA_XPATH_TOKEN_INVALID;
+ }
+ self->string.size = self->code - self->string.data;
+ ++self->code;
+ return QUICKMEDIA_XPATH_TOKEN_STRING;
+ } else if(c == '\0') {
+ return QUICKMEDIA_XPATH_TOKEN_END_OF_FILE;
+ } else {
+ /* Invalid symbol @c */
+ return QUICKMEDIA_XPATH_TOKEN_INVALID;
+ }
+}
+
+int quickmedia_xpath_tokenizer_next_if(QuickMediaXpathTokenizer *self, QuickMediaXpathToken token) {
+ const char *restore_point = self->code;
+ if(quickmedia_xpath_tokenizer_next(self) == token)
+ return 0;
+ self->code = restore_point;
+ return -1;
+}
+
+char* quickmedia_xpath_tokenizer_copy_identifier(QuickMediaXpathTokenizer *self) {
+ char *result = malloc(self->identifier.size + 1);
+ result[self->identifier.size] = '\0';
+ memcpy(result, self->identifier.data, self->identifier.size);
+ return result;
+}
+
+char* quickmedia_xpath_tokenizer_copy_string(QuickMediaXpathTokenizer *self) {
+ return quickmedia_xpath_tokenizer_copy_identifier(self);
+}
diff --git a/test_files/test.html b/test_files/test.html
new file mode 100644
index 0000000..a33081d
--- /dev/null
+++ b/test_files/test.html
@@ -0,0 +1,478 @@
+<!doctype html>
+<html lang="en" prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#">
+<head>
+<link rel="alternate" hreflang="en-US" href="https://manganelo.com/search/naruto" />
+<meta name="google-site-verification" content="N3i4EGIjvenSSdLdsmaEtRf6tCyc6yAj6fa7mBRWZeo" />
+<meta name="robots" content="noindex,nofollow" />
+<meta charset="utf-8">
+<title>Naruto Manga - Browse & Search Manga At MangaNelo</title>
+<meta name="description" content="Search: naruto Manga - Search for your favorite mangas scans and scanlations online at Manganelo" />
+<meta name="keywords" itemprop="keywords" content="naruto,manga,read manga,manga online,magna scans,manga volume,manga chapter,online manga,read free manga,read free manga online,manga genre" />
+<meta name="viewport" content="width=device-width" />
+<meta name="twitter:card" content="summary_large_image">
+<meta name="twitter:site" content="manganelo">
+<meta name="twitter:title" content="Naruto Manga - Browse & Search Manga At MangaNelo">
+<meta name="twitter:description" content="Search: naruto, manga,read manga,manga online,magna scans,manga volume,manga chapter,online manga,read free manga,read free manga online,manga genre">
+<meta name="twitter:image" content="https://lh3.googleusercontent.com/-iK1y5xV28gs/WCcslYmPKAI/AAAAAAACQGg/o9pv0oioVlY/s0/5826ca23a4ad3.jpg">
+<meta property="og:type" content="website" />
+<meta property="og:title" content="Naruto Manga - Browse & Search Manga At MangaNelo" />
+<meta property="og:url" content="https://manganelo.com/search/naruto" />
+<meta property="og:image" content="https://lh3.googleusercontent.com/-iK1y5xV28gs/WCcslYmPKAI/AAAAAAACQGg/o9pv0oioVlY/s0/5826ca23a4ad3.jpg" />
+<meta property="og:description" content="Search: naruto, manga,read manga,manga online,magna scans,manga volume,manga chapter,online manga,read free manga,read free manga online,manga genre" />
+<meta property="og:site_name" content="https://manganelo.com/" />
+<meta property="fb:app_id" content="1664224760511779" />
+<link rel="shortcut icon" href="https://manganelo.com/favicon.ico" />
+<script type="text/javascript" src="https://manganelo.com/themes/home/js/jquery-1.9.1.min.js?v=1.2.2"></script>
+<script type="text/javascript" src="https://manganelo.com/themes/home/js/owl.carousel.js?v=1.2.2"></script>
+<script type="text/javascript" src="https://manganelo.com/themes/home/js/back-to-top.js?v=1.2.2"></script>
+<script type="text/javascript" src="https://manganelo.com/themes/home/js/ddimgtooltip.js?v=1.2.2"></script>
+<script type="text/javascript" src="https://manganelo.com/themes/home/js/fsearch.js?v=1.2.2"></script>
+<script type="text/javascript" src="https://manganelo.com/themes/home/js/custom.js?v=1.2.2"></script>
+<script type="text/javascript" src="https://manganelo.com/themes/home/js/cookie.js?v=1.2.2"></script>
+<script type="text/javascript" src="https://manganelo.com/themes/starrate/js/starwarsjs.js?v=1.2.2"></script>
+<script src="https://manganelo.com/themes/home/js/lab.js"></script>
+<link rel="stylesheet" href="https://manganelo.com/themes/home/styles/style.css?v=1.2.2">
+<link rel="stylesheet" href="https://manganelo.com/themes/home/styles/owl.carousel.css?v=1.2.2">
+<link rel="stylesheet" href="https://manganelo.com/themes/home/styles/owl.theme.css?v=1.2.2">
+<link rel="stylesheet" href="https://manganelo.com/themes/home/styles/ddimgtooltip.css?v=1.2.2">
+<link rel="stylesheet" href="https://manganelo.com/themes/home/styles/style_search.css?v=1.2.2">
+<link rel="stylesheet" href="https://manganelo.com/themes/starrate/css/style.css?v=1.2.2" />
+<script type="application/javascript">
+ baseurljs = 'https://manganelo.com/';
+ tooltypejs = 'Computer';
+
+ _base_url_search = 'https://manganelo.com/search/';
+ _base_url_search_author = "https://manganelo.com/search_author/";
+</script> </head>
+<body>
+<div id="fb-root"></div>
+<script>
+ $appidfb = '1664224760511779';
+ (function(d, s, id) {
+ var js, fjs = d.getElementsByTagName(s)[0];
+ if (d.getElementById(id)) return;
+ js = d.createElement(s); js.id = id;
+ js.src = "//connect.facebook.net/en_US/all.js#xfbml=1&appId=" + $appidfb;
+ fjs.parentNode.insertBefore(js, fjs);
+ }(document, 'script', 'facebook-jssdk'));
+</script> <header>
+<div class="container container-top">
+<div class="top-logo">
+<a href="https://manganelo.com/" title="Manga Online"><img src="https://manganelo.com/themes/home/icons/logo.png" alt="Manga Online" title="Manga Online"></a>
+</div>
+<div class="top-header">
+<div class="searching">
+<form name="frmsearch">
+<input id="search_story" autocomplete="off" placeholder="Search manga..." />
+<div style="display: none;"><input /></div>
+</form>
+</div>
+<div class="user-options">
+<div class="login-top">
+<a rel="nofollow" href="https://user.manganelo.com/login?l=manganelo" class="btn-login">Login</a>
+<a rel="nofollow" href="https://user.manganelo.com/register?l=manganelo" class="btn-register">Register</a>
+</div>
+</div>
+</div>
+<a class="mobile-menu">MENU</a>
+<nav class="wrap-menu-primary">
+<ul id="menu-menu-top" class="menu-primary">
+<li class="menu-item"><a href="https://manganelo.com/">HOME</a></li>
+<li class="menu-item"><a href="https://manganelo.com/manga_list?type=latest&category=all&state=all&page=1" title="LATEST MANGA">LATEST MANGA</a></li>
+<li class="menu-item"><a href="https://manganelo.com/manga_list?type=topview&category=all&state=all&page=1" title="HOT MANGA">HOT MANGA</a></li>
+<li class="menu-item"><a href="https://manganelo.com/manga_list?type=newest&category=all&state=all&page=1" title="NEW MANGA">NEW MANGA</a></li>
+<li class="menu-item"><a href="https://manganelo.com/manga_list?type=newest&category=all&state=Completed&page=1" title="COMPLETED MANGA">COMPLETED MANGA</a></li>
+</ul>
+</nav>
+</div>
+</header>
+<div class="container">
+<div class="slide">
+<h3 class="title update-slide">POPULAR MANGA</h3>
+<div id="owl-demo" class="owl-carousel">
+<div class="item">
+<img src="https://avt.mkklcdnv3.com/avatar_225/21619-pn918005.jpg" onerror="javascript:this.src='//manganelo.com/themes/home/images/404-avatar.png';" />
+<div class="slide-caption">
+<h3><a rel="nofollow" href="https://manganelo.com/manga/pn918005" title="Solo Leveling">Solo Leveling</a></h3>
+<a rel="nofollow" href="https://manganelo.com/chapter/pn918005/chapter_72" title="Chapter 72">Chapter 72</a>
+</div>
+</div>
+<div class="item">
+<img src="https://avt.mkklcdnv3.com/avatar_225/464-tales_of_demons_and_gods.jpg" onerror="javascript:this.src='//manganelo.com/themes/home/images/404-avatar.png';" />
+<div class="slide-caption">
+<h3><a href="https://manganelo.com/manga/tales_of_demons_and_gods" title="Tales of Demons and Gods">Tales of Demons and Gods</a></h3>
+<a href="https://manganelo.com/chapter/tales_of_demons_and_gods/chapter_224" title="Chapter 224: Entering the Black Spring">Chapter 224: Entering the Black Spring</a>
+</div>
+</div>
+<div class="item">
+<img src="https://avt.mkklcdnv3.com/avatar_225/19862-apotheosis.jpg" onerror="javascript:this.src='//manganelo.com/themes/home/images/404-avatar.png';" />
+<div class="slide-caption">
+<h3><a href="https://manganelo.com/manga/apotheosis" title="Apotheosis">Apotheosis</a></h3>
+<a href="https://manganelo.com/chapter/apotheosis/chapter_164" title="Chapter 164">Chapter 164</a>
+</div>
+</div>
+<div class="item">
+<img src="https://avt.mkklcdnv3.com/avatar_225/2551-the_great_ruler.jpg" onerror="javascript:this.src='//manganelo.com/themes/home/images/404-avatar.png';" />
+<div class="slide-caption">
+<h3><a href="https://manganelo.com/manga/the_great_ruler" title="The Great Ruler">The Great Ruler</a></h3>
+<a href="https://manganelo.com/chapter/the_great_ruler/chapter_127" title="Chapter 127">Chapter 127</a>
+</div>
+</div>
+<div class="item">
+<img src="https://avt.mkklcdnv3.com/avatar_225/19973-everlasting_god_of_sword.jpg" onerror="javascript:this.src='//manganelo.com/themes/home/images/404-avatar.png';" />
+<div class="slide-caption">
+<h3><a href="https://manganelo.com/manga/everlasting_god_of_sword" title="Everlasting God of Sword">Everlasting God of Sword</a></h3>
+<a href="https://manganelo.com/chapter/everlasting_god_of_sword/chapter_73" title="Chapter 73">Chapter 73</a>
+</div>
+</div>
+<div class="item">
+<img src="https://avt.mkklcdnv3.com/avatar_225/21339-zu917722.jpg" onerror="javascript:this.src='//manganelo.com/themes/home/images/404-avatar.png';" />
+<div class="slide-caption">
+<h3><a href="https://manganelo.com/manga/zu917722" title="A Returner's Magic Should Be Special">A Returner's Magic Should Be Special</a></h3>
+<a href="https://manganelo.com/chapter/zu917722/chapter_64" title="Chapter 64">Chapter 64</a>
+</div>
+</div>
+<div class="item">
+<img src="https://avt.mkklcdnv3.com/avatar_225/20891-spirit_sword_sovereign.jpg" onerror="javascript:this.src='//manganelo.com/themes/home/images/404-avatar.png';" />
+<div class="slide-caption">
+<h3><a href="https://manganelo.com/manga/spirit_sword_sovereign" title="Spirit Sword Sovereign">Spirit Sword Sovereign</a></h3>
+<a href="https://manganelo.com/chapter/spirit_sword_sovereign/chapter_184" title="Chapter 184">Chapter 184</a>
+</div>
+</div>
+<div class="item">
+<img src="https://avt.mkklcdnv3.com/avatar_225/22141-wp918498.jpg" onerror="javascript:this.src='//manganelo.com/themes/home/images/404-avatar.png';" />
+<div class="slide-caption">
+<h3><a href="https://manganelo.com/manga/wp918498" title="My Wife is a Demon Queen">My Wife is a Demon Queen</a></h3>
+<a href="https://manganelo.com/chapter/wp918498/chapter_72" title="Chapter 72">Chapter 72</a>
+</div>
+</div>
+<div class="item">
+<img src="https://avt.mkklcdnv3.com/avatar_225_new/482-xi919082.jpg" onerror="javascript:this.src='//manganelo.com/themes/home/images/404-avatar.png';" />
+<div class="slide-caption">
+<h3><a href="https://manganelo.com/manga/xi919082" title="The Reincarnation Magician Of The Inferior Eyes">The Reincarnation Magician Of The Inferior Eyes</a></h3>
+<a href="https://manganelo.com/chapter/xi919082/chapter_7" title="Chapter 7">Chapter 7</a>
+</div>
+</div>
+<div class="item">
+<img src="https://avt.mkklcdnv3.com/avatar_225/20122-kuro_no_shoukanshi.jpg" onerror="javascript:this.src='//manganelo.com/themes/home/images/404-avatar.png';" />
+<div class="slide-caption">
+<h3><a href="https://manganelo.com/manga/kuro_no_shoukanshi" title="Kuro no Shoukanshi">Kuro no Shoukanshi</a></h3>
+<a href="https://manganelo.com/chapter/kuro_no_shoukanshi/chapter_27" title="Chapter 27: Declaration of War II">Chapter 27: Declaration of War II</a>
+</div>
+</div>
+<div class="item">
+<img src="https://avt.mkklcdnv3.com/avatar_225/16363-kimetsu_no_yaiba.jpg" onerror="javascript:this.src='//manganelo.com/themes/home/images/404-avatar.png';" />
+<div class="slide-caption">
+<h3><a rel="nofollow" href="https://manganelo.com/manga/kimetsu_no_yaiba" title="Kimetsu no Yaiba">Kimetsu no Yaiba</a></h3>
+ <a rel="nofollow" href="https://manganelo.com/chapter/kimetsu_no_yaiba/chapter_158" title="Chapter 158: Absurd">Chapter 158: Absurd</a>
+</div>
+</div>
+<div class="item">
+<img src="https://avt.mkklcdnv3.com/avatar_225/1917-komisan_wa_komyushou_desu.jpg" onerror="javascript:this.src='//manganelo.com/themes/home/images/404-avatar.png';" />
+<div class="slide-caption">
+<h3><a href="https://manganelo.com/manga/komisan_wa_komyushou_desu" title="Komi-san wa Komyushou Desu">Komi-san wa Komyushou Desu</a></h3>
+<a href="https://manganelo.com/chapter/komisan_wa_komyushou_desu/chapter_198" title="Chapter 198: Out & Law ~The Movie~">Chapter 198: Out & Law ~The Movie~</a>
+</div>
+</div>
+<div class="item">
+<img src="https://avt.mkklcdnv3.com/avatar_225/19255-the_wrong_way_to_use_healing_magic.jpg" onerror="javascript:this.src='//manganelo.com/themes/home/images/404-avatar.png';" />
+<div class="slide-caption">
+<h3><a rel="nofollow" href="https://manganelo.com/manga/the_wrong_way_to_use_healing_magic" title="The Wrong Way to use Healing Magic">The Wrong Way to use Healing Magic</a></h3>
+<a rel="nofollow" href="https://manganelo.com/chapter/the_wrong_way_to_use_healing_magic/chapter_20" title="Chapter 20">Chapter 20</a>
+</div>
+</div>
+<div class="item">
+<img src="https://avt.mkklcdnv3.com/avatar_225/19074-assassins_pride.jpg" onerror="javascript:this.src='//manganelo.com/themes/home/images/404-avatar.png';" />
+<div class="slide-caption">
+<h3><a href="https://manganelo.com/manga/assassins_pride" title="Assassin's Pride">Assassin's Pride</a></h3>
+<a href="https://manganelo.com/chapter/assassins_pride/chapter_16.5" title="Vol.3 Chapter 16.5: Extras">Vol.3 Chapter 16.5: Extras</a>
+</div>
+</div>
+<div class="item">
+<img src="https://avt.mkklcdnv3.com/avatar_225/22124-hn918480.jpg" onerror="javascript:this.src='//manganelo.com/themes/home/images/404-avatar.png';" />
+<div class="slide-caption">
+<h3><a href="https://manganelo.com/manga/hn918480" title="Release That Witch">Release That Witch</a></h3>
+<a href="https://manganelo.com/chapter/hn918480/chapter_37" title="Chapter 37: Wall of Flames">Chapter 37: Wall of Flames</a>
+</div>
+</div>
+<div class="item">
+<img src="https://avt.mkklcdnv3.com/avatar_225/18045-isekai_yakkyoku.jpg" onerror="javascript:this.src='//manganelo.com/themes/home/images/404-avatar.png';" />
+<div class="slide-caption">
+<h3><a href="https://manganelo.com/manga/isekai_yakkyoku" title="Isekai Yakkyoku">Isekai Yakkyoku</a></h3>
+<a href="https://manganelo.com/chapter/isekai_yakkyoku/chapter_21" title="Chapter 21: Cutting Edge">Chapter 21: Cutting Edge</a>
+</div>
+</div>
+<div class="item">
+<img src="https://avt.mkklcdnv3.com/avatar_225/708-tamen_de_gushi.jpg" onerror="javascript:this.src='//manganelo.com/themes/home/images/404-avatar.png';" />
+<div class="slide-caption">
+<h3><a href="https://manganelo.com/manga/tamen_de_gushi" title="Tamen De Gushi">Tamen De Gushi</a></h3>
+<a href="https://manganelo.com/chapter/tamen_de_gushi/chapter_185.2" title="Chapter 185.2">Chapter 185.2</a>
+</div>
+</div>
+<div class="item">
+<img src="https://avt.mkklcdnv3.com/avatar_225/19650-god_of_martial_arts.jpg" onerror="javascript:this.src='//manganelo.com/themes/home/images/404-avatar.png';" />
+<div class="slide-caption">
+<h3><a href="https://manganelo.com/manga/god_of_martial_arts" title="God of Martial Arts">God of Martial Arts</a></h3>
+<a href="https://manganelo.com/chapter/god_of_martial_arts/chapter_83.2" title="Vol.1 Chapter 83.2: Ba Dao(2)">Vol.1 Chapter 83.2: Ba Dao(2)</a>
+</div>
+</div>
+<div class="item">
+<img src="https://avt.mkklcdnv3.com/avatar_225/422-read_one_punch_man_manga_online_free3.jpg" onerror="javascript:this.src='//manganelo.com/themes/home/images/404-avatar.png';" />
+<div class="slide-caption">
+<h3><a rel="nofollow" href="https://manganelo.com/manga/read_one_punch_man_manga_online_free3" title="Onepunch-Man">Onepunch-Man</a></h3>
+<a rel="nofollow" href="https://manganelo.com/chapter/read_one_punch_man_manga_online_free3/chapter_107" title="Chapter 107: N/A">Chapter 107: N/A</a>
+</div>
+</div>
+<div class="item">
+<img src="https://avt.mkklcdnv3.com/avatar_225/19316-saikyou_no_shokugyou_wa_yuusha_demo_kenja_demo_naku_kanteishi_kari_rashii_desu_yo.jpg" onerror="javascript:this.src='//manganelo.com/themes/home/images/404-avatar.png';" />
+<div class="slide-caption">
+<h3><a href="https://manganelo.com/manga/saikyou_no_shokugyou_wa_yuusha_demo_kenja_demo_naku_kanteishi_kari_rashii_desu_yo" title="Saikyou no Shokugyou wa Yuusha demo Kenja demo naku Kanteishi (Kari) rashii desu yo?">Saikyou no Shokugyou wa Yuusha demo Kenja demo naku Kanteishi (Kari) rashii desu yo?</a></h3>
+<a href="https://manganelo.com/chapter/saikyou_no_shokugyou_wa_yuusha_demo_kenja_demo_naku_kanteishi_kari_rashii_desu_yo/chapter_16" title="Chapter 16">Chapter 16</a>
+</div>
+</div>
+</div>
+</div>
+<div class="main-wrapper">
+<div class="leftCol">
+<div style="width: 100%;overflow: hidden;text-align: center;float: left;">
+<div style="max-width: 728px;max-height: 90px;overflow: hidden;margin: 0px auto;">
+<iframe src="/ads/adtrue_728x90_desktop.html" scrolling="no" frameborder="0" style="width: 728px;height: 90px;"></iframe>
+</div>
+</div>
+<div class="breadcrumb breadcrumbs">
+<p>
+<a href="https://manganelo.com/">
+<span>Manga Online</span>
+</a>
+<span>»</span>
+<span>Search</span>
+</p>
+</div>
+<div class="daily-update">
+<h3 class="title update-title">Keyword: naruto</h3>
+<div class="panel_story_list">
+<div class="story_item">
+<a rel="nofollow" href="https://manganelo.com/manga/read_naruto_manga_online_free3">
+<img src="https://avt.mkklcdnv3.com/avatar_225/1203-read_naruto_manga_online_free3.jpg" alt="Naruto" />
+</a>
+<div class="story_item_right">
+<h3 class="story_name">
+<a rel="nofollow" href="https://manganelo.com/manga/read_naruto_manga_online_free3">Naruto</a>
+<em class="hot"></em>
+</h3>
+<em class="story_chapter">
+<a rel="nofollow" href="https://manganelo.com/chapter/read_naruto_manga_online_free3/chapter_700.5" title="Naruto chapter 700.5 : Uzumaki Naruto">
+Chapter 700.5 : Uzumaki Naruto </a>
+</em>
+<em class="story_chapter">
+<a rel="nofollow" href="https://manganelo.com/chapter/read_naruto_manga_online_free3/chapter_700.1" title="Naruto vol.72 chapter 700.1 : Book of Thunder">Vol.72 Chapter 700.1 : Book Of Thunder</a>
+</em>
+<span>Author(s) : Kishimoto Masashi</span>
+<span>Updated : Jan-20-2016 11:54</span>
+<span>View : 13,306,950</span>
+</div>
+</div>
+<div class="story_item">
+<a rel="nofollow" href="https://manganelo.com/manga/boruto_naruto_next_generations">
+ <img src="https://avt.mkklcdnv3.com/avatar_225/16679-boruto_naruto_next_generations.jpg" alt="Boruto: Naruto Next Generations" />
+</a>
+<div class="story_item_right">
+<h3 class="story_name">
+<a rel="nofollow" href="https://manganelo.com/manga/boruto_naruto_next_generations">Boruto: Naruto Next Generations</a>
+<em class=""></em>
+</h3>
+<em class="story_chapter">
+<a rel="nofollow" href="https://manganelo.com/chapter/boruto_naruto_next_generations/chapter_34" title="Boruto: Naruto Next Generations Chapter 34: Training!!">
+Chapter 34: Training!! </a>
+</em>
+<em class="story_chapter">
+<a rel="nofollow" href="https://manganelo.com/chapter/boruto_naruto_next_generations/chapter_33" title="Boruto: Naruto Next Generations Chapter 33: Breaking The Limit">Chapter 33: Breaking The Limit</a>
+</em>
+<span>Author(s) : Kodachi Ukyo, Ikemoto Mikio</span>
+<span>Updated : Apr-20-2019 06:52</span>
+<span>View : 3,453,805</span>
+</div>
+</div>
+<div class="story_item">
+<a rel="nofollow" href="https://manganelo.com/manga/naruto_gaiden_the_seventh_hokage2">
+<img src="https://avt.mkklcdnv3.com/avatar_225/2790-naruto_gaiden_the_seventh_hokage2.jpg" alt="Naruto Gaiden: The Seventh Hokage" />
+</a>
+<div class="story_item_right">
+<h3 class="story_name">
+<a rel="nofollow" href="https://manganelo.com/manga/naruto_gaiden_the_seventh_hokage2">Naruto Gaiden: The Seventh Hokage</a>
+<em class=""></em>
+</h3>
+<em class="story_chapter">
+<a rel="nofollow" href="https://manganelo.com/chapter/naruto_gaiden_the_seventh_hokage2/chapter_10.1" title="Naruto Gaiden: The Seventh Hokage ch.10.1 : Projected Into These Eyes (Full Color Version)">
+Ch.10.1 : Projected Into These Eyes (Full Color Version) </a>
+</em>
+<em class="story_chapter">
+<a rel="nofollow" href="https://manganelo.com/chapter/naruto_gaiden_the_seventh_hokage2/chapter_10" title="Naruto Gaiden: The Seventh Hokage ch.10 : Projected Into These Eyes">Ch.10 : Projected Into These Eyes</a>
+</em>
+<span>Author(s) : Kishimoto Masashi</span>
+<span>Updated : Jan-20-2016 11:44</span>
+<span>View : 230,877</span>
+</div>
+</div>
+<div class="story_item">
+<a rel="nofollow" href="https://manganelo.com/manga/kw919198">
+<img src="https://avt.mkklcdnv3.com/avatar_225_new/598-kw919198.jpg" alt="Naruto - Full Color" />
+</a>
+<div class="story_item_right">
+<h3 class="story_name">
+<a rel="nofollow" href="https://manganelo.com/manga/kw919198">Naruto - Full Color</a>
+<em class="hot"></em>
+</h3>
+<em class="story_chapter">
+<a rel="nofollow" href="https://manganelo.com/chapter/kw919198/chapter_46" title="Naruto - Full Color Vol.5 Chapter 46: The Password Is...">
+Vol.5 Chapter 46: The Password Is... </a>
+</em>
+<em class="story_chapter">
+<a rel="nofollow" href="https://manganelo.com/chapter/kw919198/chapter_45" title="Naruto - Full Color Vol.5 Chapter 45: The Second Exam">Vol.5 Chapter 45: The Second Exam</a>
+</em>
+ <span>Author(s) : Masashi Kishimoto</span>
+<span>Updated : May-01-2019 16:19</span>
+<span>View : 86,639</span>
+</div>
+</div>
+<div class="story_item">
+<a rel="nofollow" href="https://manganelo.com/manga/bt917589">
+<img src="https://avt.mkklcdnv3.com/avatar_225/21209-bt917589.jpg" alt="Naruto: Chibi Sasuke's Sharingan Legend" />
+</a>
+<div class="story_item_right">
+<h3 class="story_name">
+<a rel="nofollow" href="https://manganelo.com/manga/bt917589">Naruto: Chibi Sasuke's Sharingan Legend</a>
+<em class=""></em>
+</h3>
+<em class="story_chapter">
+<a rel="nofollow" href="https://manganelo.com/chapter/bt917589/chapter_23.1" title="Naruto: Chibi Sasuke's Sharingan Legend Volume 3 Final Chapter: The Uchiha Clan!!">
+Volume 3 Final Chapter: The Uchiha Clan!! </a>
+</em>
+<em class="story_chapter">
+<a rel="nofollow" href="https://manganelo.com/chapter/bt917589/chapter_23" title="Naruto: Chibi Sasuke's Sharingan Legend Chapter 23: Karin's Battle!!">Chapter 23: Karin's Battle!!</a>
+</em>
+<span>Author(s) : Taira Kenji</span>
+<span>Updated : Oct-18-2018 14:15</span>
+<span>View : 66,363</span>
+</div>
+</div>
+<div class="story_item">
+<a rel="nofollow" href="https://manganelo.com/manga/seikimatsu_darling">
+<img src="https://avt.mkklcdnv3.com/avatar_225/1804-seikimatsu_darling.jpg" alt="Seikimatsu Darling" />
+</a>
+<div class="story_item_right">
+<h3 class="story_name">
+<a href="https://manganelo.com/manga/seikimatsu_darling">Seikimatsu Darling</a>
+<em class=""></em>
+</h3>
+<em class="story_chapter">
+<a href="https://manganelo.com/chapter/seikimatsu_darling/chapter_9" title="Seikimatsu Darling vol.2 chapter 9">
+Vol.2 Chapter 9 </a>
+</em>
+<em class="story_chapter">
+<a href="https://manganelo.com/chapter/seikimatsu_darling/chapter_8" title="Seikimatsu Darling vol.2 chapter 8">Vol.2 Chapter 8</a>
+</em>
+<span>Author(s) : Naruto Maki</span>
+<span>Updated : Jan-20-2016 13:56</span>
+<span>View : 50,352</span>
+</div>
+</div>
+<div class="story_item">
+<a rel="nofollow" href="https://manganelo.com/manga/420_renpai_girl">
+<img src="https://avt.mkklcdnv3.com/avatar_225/5691-420_renpai_girl.jpg" alt="420 Renpai Girl" />
+</a>
+<div class="story_item_right">
+<h3 class="story_name">
+<a href="https://manganelo.com/manga/420_renpai_girl">420 Renpai Girl</a>
+<em class=""></em>
+</h3>
+<em class="story_chapter">
+<a href="https://manganelo.com/chapter/420_renpai_girl/chapter_1" title="420 Renpai Girl vol.1 ch.1 : Teaser">
+Vol.1 Ch.1 : Teaser </a>
+</em>
+<span>Author(s) : Kiriyama Naruto</span>
+<span>Updated : Jan-21-2016 13:12</span>
+<span>View : 17,103</span>
+</div>
+</div>
+</div>
+</div>
+<div style="clear: both"></div>
+<div style="background: #FFF; float: left; margin-top: 10px;padding: 10px; width: calc(100% - 20px);">
+<div class="fb-comments" data-href="http://manganelo.com/story_list" data-width="100%" data-numposts="10" data-colorscheme="light"></div>
+</div> </div>
+<div class="middleCol">
+<div style="float: left;width: 100%;text-align: center;">
+<div style="max-width: 300px;max-height: 250px;margin: 0px auto;overflow: hidden;">
+<iframe src="/ads/adtrue_300x250_desktop.html" scrolling="no" frameborder="0" style="width: 300px;height: 250px;"></iframe>
+</div>
+</div>
+<div class="xem-nhieu">
+<h3 class="title all-title">Most Popular Manga</h3>
+<div class="all">
+<div class="xem-nhieu-item">
+<h3><a rel="nofollow" href="https://manganelo.com/manga/pn918005" title="Solo Leveling">Solo Leveling - Chapter 72</a></h3>
+</div>
+<div class="xem-nhieu-item">
+<h3><a href="https://manganelo.com/manga/tales_of_demons_and_gods" title="Tales of Demons and Gods">Tales of Demons and Gods - Chapter 224: Entering the Black Spring</a></h3>
+</div>
+<div class="xem-nhieu-item">
+<h3><a href="https://manganelo.com/manga/the_great_ruler" title="The Great Ruler">The Great Ruler - Chapter 127</a></h3>
+</div>
+<div class="xem-nhieu-item">
+<h3><a href="https://manganelo.com/manga/nidoume_no_jinsei_wo_isekai_de" title="Nidoume no Jinsei wo Isekai de">Nidoume no Jinsei wo Isekai de - Vol.7 Chapter 32: It Seems Like Another One</a></h3>
+</div>
+<div class="xem-nhieu-item">
+<h3><a href="https://manganelo.com/manga/zk919364" title="29-sai Dokushin wa Isekai de Jiyuu ni Ikita……katta">29-sai Dokushin wa Isekai de Jiyuu ni Ikita……katta - Chapter 2.1</a></h3>
+</div>
+<div class="xem-nhieu-item">
+<h3><a href="https://manganelo.com/manga/wp918498" title="My Wife is a Demon Queen">My Wife is a Demon Queen - Chapter 72</a></h3>
+</div>
+<div class="xem-nhieu-item">
+<h3><a href="https://manganelo.com/manga/potiondanomi_de_ikinobimasu" title="Potion-danomi de Ikinobimasu!">Potion-danomi de Ikinobimasu! - Chapter 24.2</a></h3>
+</div>
+<div class="xem-nhieu-item">
+<h3><a href="https://manganelo.com/manga/uu918143" title="My Girlfriend is a Villain">My Girlfriend is a Villain - Chapter 26: Childhood Nightmare</a></h3>
+</div>
+<div class="xem-nhieu-item">
+<h3><a href="https://manganelo.com/manga/god_of_martial_arts" title="God of Martial Arts">God of Martial Arts - Vol.1 Chapter 83.2: Ba Dao(2)</a></h3>
+</div>
+<div class="xem-nhieu-item">
+<h3><a href="https://manganelo.com/manga/spirit_sword_sovereign" title="Spirit Sword Sovereign">Spirit Sword Sovereign - Chapter 184</a></h3>
+</div>
+</div>
+</div>
+<div class="panel-category">
+<h3 class="panel-category-title">GENRES</h3>
+<table>
+<tbody>
+<tr>
+<td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=all&state=all&page=1">Latest</a></td>
+<td><a rel="nofollow" href="https://manganelo.com/manga_list?type=newest&category=all&state=all&page=1">Newest</a></td>
+<td><a rel="nofollow" href="https://manganelo.com/manga_list?type=topview&category=all&state=all&page=1">Top view</a></td>
+</tr>
+<tr class="bordertop">
+<td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=all&state=all&page=1">ALL</a></td>
+<td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=all&state=completed&page=1">Completed</a></td>
+<td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=all&state=ongoing&page=1">Ongoing</a></td>
+</tr>
+<tr class="bordertop"><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=all&state=all&page=1">ALL</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=2&state=all&page=1" title="Action" />Action</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=3&state=all&page=1" title="Adult" />Adult</a></td></tr><tr><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=4&state=all&page=1" title="Adventure" />Adventure</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=6&state=all&page=1" title="Comedy" />Comedy</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=7&state=all&page=1" title="Cooking" />Cooking</a></td></tr><tr><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=9&state=all&page=1" title="Doujinshi" />Doujinshi</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=10&state=all&page=1" title="Drama" />Drama</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=11&state=all&page=1" title="Ecchi" />Ecchi</a></td></tr><tr><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=12&state=all&page=1" title="Fantasy" />Fantasy</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=13&state=all&page=1" title="Gender bender" />Gender bender</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=14&state=all&page=1" title="Harem" />Harem</a></td></tr><tr><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=15&state=all&page=1" title="Historical" />Historical</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=16&state=all&page=1" title="Horror" />Horror</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=17&state=all&page=1" title="Josei" />Josei</a></td></tr><tr><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=44&state=all&page=1" title="Manhua" />Manhua</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=43&state=all&page=1" title="Manhwa" />Manhwa</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=19&state=all&page=1" title="Martial arts" />Martial arts</a></td></tr><tr><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=20&state=all&page=1" title="Mature" />Mature</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=21&state=all&page=1" title="Mecha" />Mecha</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=22&state=all&page=1" title="Medical" />Medical</a></td></tr><tr><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=24&state=all&page=1" title="Mystery" />Mystery</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=25&state=all&page=1" title="One shot" />One shot</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=26&state=all&page=1" title="Psychological" />Psychological</a></td></tr><tr><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=27&state=all&page=1" title="Romance" />Romance</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=28&state=all&page=1" title="School life" />School life</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=29&state=all&page=1" title="Sci fi" />Sci fi</a></td></tr><tr><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=30&state=all&page=1" title="Seinen" />Seinen</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=31&state=all&page=1" title="Shoujo" />Shoujo</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=32&state=all&page=1" title="Shoujo ai" />Shoujo ai</a></td></tr><tr><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=33&state=all&page=1" title="Shounen" />Shounen</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=34&state=all&page=1" title="Shounen ai" />Shounen ai</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=35&state=all&page=1" title="Slice of life" />Slice of life</a></td></tr><tr><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=36&state=all&page=1" title="Smut" />Smut</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=37&state=all&page=1" title="Sports" />Sports</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=38&state=all&page=1" title="Supernatural" />Supernatural</a></td></tr><tr><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=39&state=all&page=1" title="Tragedy" />Tragedy</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=40&state=all&page=1" title="Webtoons" />Webtoons</a></td><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=41&state=all&page=1" title="Yaoi" />Yaoi</a></td></tr><tr><td><a rel="nofollow" href="https://manganelo.com/manga_list?type=latest&category=42&state=all&page=1" title="Yuri" />Yuri</a></td></tr> </tbody>
+</table>
+</div> <div style="float: left;width: 100%;text-align: center;">
+<div style="max-width: 336px;max-height: 280px;margin: 0px auto;overflow: hidden;">
+<iframe src="/ads/bidgear_300x250_desktop.html" scrolling="no" frameborder="0" style="width: 300px;height: 250px;"></iframe>
+</div>
+</div>
+<div style="clear: both"></div>
+</div>
+</div>
+</div>
+<footer>
+<div class="footer-content">
+<p>©2016 MangaNelo.com, all rights reserved. Top speed, completely free. </p>
+<p>Current Time is May-24-2019 08:59:37 AM.</p>
+</div>
+</footer>
+<script type="text/javascript" src="https://manganelo.com/themes/home/js/custom-fotter.js?v=2.9.1"></script>
+</body>
+</html> \ No newline at end of file
diff --git a/tests/main.c b/tests/main.c
new file mode 100644
index 0000000..eb1abc7
--- /dev/null
+++ b/tests/main.c
@@ -0,0 +1,33 @@
+#include <stdio.h>
+#include "../include/quickmedia/HtmlSearch.h"
+#include <assert.h>
+#include <stdlib.h>
+
+static char* get_file_content(const char *filepath) {
+ FILE *file = fopen(filepath, "rb");
+ assert(file);
+
+ fseek(file, 0, SEEK_END);
+ size_t filesize = ftell(file);
+ fseek(file, 0, SEEK_SET);
+
+ char *buffer = malloc(filesize + 1);
+ buffer[filesize] = '\0';
+ fread(buffer, 1, filesize, file);
+
+ return buffer;
+}
+
+static void result_callback(QuickMediaHtmlNode *node, void *userdata) {
+ const char *href = quickmedia_html_node_get_attribute_value(node, "href");
+ QuickMediaStringView text = quickmedia_html_node_get_text(node);
+ printf("a href: %s, node value: %.*s\n", href, text.size, text.data);
+}
+
+int main(int argc, char **argv)
+{
+ char *file_content = get_file_content("test_files/test.html");
+ int result = quickmedia_html_find_nodes_xpath(file_content, "//h3[class=\"story_name\"]//a", result_callback, NULL);
+ free(file_content);
+ return result;
+}