Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2578304
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
30 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/c_src/myhtml_worker.c b/c_src/myhtml_worker.c
index 70a016e..2990b98 100644
--- a/c_src/myhtml_worker.c
+++ b/c_src/myhtml_worker.c
@@ -1,441 +1,501 @@
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <ctype.h>
#include "erl_interface.h"
#include "ei.h"
-#include "tstack.h"
-
#include <myhtml/myhtml.h>
#include <myhtml/mynamespace.h>
+#include "tstack.h"
+
typedef struct _state_t {
int fd;
myhtml_t * myhtml;
ei_cnode ec;
bool looping;
ei_x_buff buffer;
} state_t;
static void handle_emsg(state_t * state, erlang_msg * emsg);
static void handle_send(state_t * state, erlang_msg * emsg);
static void err_term(ei_x_buff * response, const char * error_atom);
+static unsigned char decode_parse_flags(state_t * state, int arity);
+static void decode(state_t * state, ei_x_buff * response, const char * bin_data, size_t bin_size, unsigned char parse_flags);
-ETERM * decode(state_t* state, ErlMessage* emsg, ETERM* bin, ETERM* args);
-ETERM * build_tree(myhtml_tree_t* tree, myhtml_tree_node_t* node, unsigned char* parse_flags);
-ETERM * build_node_attrs(myhtml_tree_t* tree, myhtml_tree_node_t* node);
-unsigned char read_parse_flags(ETERM* list);
-static inline char * lowercase(char* c);
+static void build_tree(ei_x_buff * response, myhtml_tree_t * tree, myhtml_tree_node_t * node, unsigned char parse_flags);
+static void prepare_node_attrs(ei_x_buff * response, myhtml_tree_node_t * node);
+
+static inline char * lowercase(char * c);
const unsigned char FLAG_HTML_ATOMS = 1 << 0;
const unsigned char FLAG_NIL_SELF_CLOSING = 1 << 1;
const unsigned char FLAG_COMMENT_TUPLE3 = 1 << 2;
#ifdef __GNUC__
# define AFP(x, y) __attribute__((format (printf, x, y)))
#else
# define AFP(x, y)
#endif
#ifdef __GNUC__
# define NORETURN __attribute__((noreturn))
#else
# define NORETURN
#endif
static void panic(const char *fmt, ...) AFP(1, 2);
static void panic(const char *fmt, ...) {
char buf[4096];
va_list va;
va_start (va, fmt);
vsnprintf (buf, sizeof buf, fmt, va);
va_end (va);
fprintf (stderr, "myhtml worker: error: %s\n", buf);
exit (EXIT_FAILURE);
}
static void usage (void) NORETURN;
static void usage (void) {
fputs ("usage: myhtml_worker sname hostname cookie tname\n\n"
" sname the short name you want this c-node to connect as\n"
" hostname the hostname\n"
" cookie the authentication cookie\n"
" tname the target node short name to connect to\n", stderr);
exit (EXIT_FAILURE);
}
int main(int argc, const char *argv[]) {
if (argc != 5)
usage ();
const char *sname = argv[1];
const char *hostname = argv[2];
const char *cookie = argv[3];
const char *tname = argv[4];
char full_name[1024];
char target_node[1024];
snprintf(full_name, sizeof full_name, "%s@%s", sname, hostname);
snprintf(target_node, sizeof target_node, "%s@%s", tname, hostname);
struct in_addr addr;
addr.s_addr = htonl(INADDR_ANY);
// fd to erlang node
state_t* state = calloc (1, sizeof(state_t));
state->looping = true;
ei_x_new (&state->buffer);
// initialize all of Erl_Interface
erl_init(NULL, 0);
// initialize this node
printf("initialising %s\n", full_name);
if (ei_connect_xinit (&state->ec, hostname, sname, full_name, &addr, cookie, 0) == -1)
panic ("ei_connect_xinit failed.");
// connect to target node
printf("connecting to %s\n", target_node);
if ((state->fd = ei_connect (&state->ec, target_node)) < 0)
panic ("ei_connect failed.");
state->myhtml = myhtml_create ();
myhtml_init (state->myhtml, MyHTML_OPTIONS_DEFAULT, 1, 0);
// signal to stdout that we are ready
printf ("%s ready\n", full_name);
fflush (stdout);
while (state->looping)
{
erlang_msg emsg;
switch (ei_xreceive_msg (state->fd, &emsg, &state->buffer))
{
case ERL_TICK:
break;
case ERL_ERROR:
panic ("ei_xreceive_msg: %s\n", strerror (erl_errno));
break;
default:
handle_emsg (state, &emsg);
break;
}
}
// shutdown: free all state
ei_x_free (&state->buffer);
myhtml_destroy (state->myhtml);
free (state);
return EXIT_SUCCESS;
}
+// handle an erlang_msg structure and call handle_send() if relevant
static void handle_emsg (state_t * state, erlang_msg * emsg)
{
+ // XXX: don't know why this is necessary?
+ state->buffer.index = 0;
+
switch (emsg->msgtype)
{
case ERL_REG_SEND:
case ERL_SEND:
handle_send (state, emsg);
break;
case ERL_LINK:
case ERL_UNLINK:
break;
case ERL_EXIT:
break;
}
}
// handle ERL_SEND message type.
// we expect a tuple with arity of 3 in state->buffer.
// we expect the first argument to be an atom (`decode`),
// the second argument to be the HTML payload, and the
// third argument to be the argument list.
// any other message: respond with an {error, unknown_call} tuple.
static void handle_send (state_t * state, erlang_msg * emsg)
{
// response holds our response, prepare it
ei_x_buff response;
ei_x_new (&response);
-#if 0
- ETERM *decode_pattern = erl_format("{decode, Bin, Args}");
+ // check the protocol version, if it's unsupported, panic
+ int version;
+ if (ei_decode_version (state->buffer.buff, &state->buffer.index, &version) < 0)
+ panic ("malformed message - bad version (%d).", version);
- if (erl_match(decode_pattern, emsg->msg))
+ // decode the tuple header, make sure we have an arity of 3.
+ int arity;
+ if (ei_decode_tuple_header (state->buffer.buff, &state->buffer.index, &arity) < 0 || arity != 3)
{
- ETERM *bin = erl_var_content(decode_pattern, "Bin");
- ETERM *args = erl_var_content(decode_pattern, "Args");
-
- response = decode(state, emsg, bin, args);
+ err_term (&response, "badmatch");
+ goto out;
+ }
- // free allocated resources
- erl_free_term(bin);
- erl_free_term(args);
+ // the tuple should begin with a `decode` atom.
+ char atom[MAXATOMLEN];
+ if (ei_decode_atom (state->buffer.buff, &state->buffer.index, atom) < 0)
+ {
+ err_term (&response, "badmatch");
+ goto out;
}
- else
-#endif
+
+ if (strcmp (atom, "decode"))
+ {
err_term (&response, "unknown_call");
+ goto out;
+ }
+ // the next argument should be a binary, allocate it dynamically.
+ int bin_type, bin_size;
+ if (ei_get_type (state->buffer.buff, &state->buffer.index, &bin_type, &bin_size) < 0)
+ panic ("failed to decode binary size in message");
+
+ // verify the type
+ if (bin_type != ERL_BINARY_EXT)
+ {
+ err_term (&response, "badmatch");
+ goto out;
+ }
+
+ // decode the binary
+ char * bin_data = calloc (1, bin_size + 1);
+ if (ei_decode_binary (state->buffer.buff, &state->buffer.index, bin_data, NULL) < 0)
+ panic ("failed to decode binary in message");
+
+ // next should be the options list
+ if (ei_decode_list_header (state->buffer.buff, &state->buffer.index, &arity) < 0)
+ panic ("failed to decode options list header in message");
+
+ unsigned char parse_flags = decode_parse_flags (state, arity);
+ decode (state, &response, bin_data, bin_size, parse_flags);
+
+ free (bin_data);
+
+out:
// send response
ei_send (state->fd, &emsg->from, response.buff, response.buffsz);
+ // free response
+ ei_x_free (&response);
+
return;
}
static void err_term (ei_x_buff * response, const char * error_atom)
{
response->index = 0;
ei_x_encode_version (response);
ei_x_encode_tuple_header (response, 2);
ei_x_encode_atom (response, "error");
ei_x_encode_atom (response, error_atom);
}
-ETERM*
-decode(state_t* state, ErlMessage* emsg, ETERM* bin, ETERM* args)
+static unsigned char decode_parse_flags (state_t * state, int arity)
{
unsigned char parse_flags = 0;
- if (!ERL_IS_BINARY(bin) || !ERL_IS_LIST(args))
+ for (int i = 0; i < arity; i++)
{
-// return err_term("badarg");
+ char atom[MAXATOMLEN];
+
+ if (ei_decode_atom (state->buffer.buff, &state->buffer.index, atom) < 0)
+ continue;
+
+ if (! strcmp ("html_atoms", atom))
+ parse_flags |= FLAG_HTML_ATOMS;
+ else if (! strcmp ("nil_self_closing", atom))
+ parse_flags |= FLAG_NIL_SELF_CLOSING;
+ else if (! strcmp ("comment_tuple3", atom))
+ parse_flags |= FLAG_COMMENT_TUPLE3;
}
- // get contents of binary argument
- char* binary = (char*)ERL_BIN_PTR(bin);
- size_t binary_len = ERL_BIN_SIZE(bin);
+ return parse_flags;
+}
- myhtml_tree_t* tree = myhtml_tree_create();
- myhtml_tree_init(tree, state->myhtml);
+static void decode (state_t * state, ei_x_buff * response, const char * bin_data, size_t bin_size, unsigned char parse_flags)
+{
+ myhtml_tree_t * tree = myhtml_tree_create ();
+ myhtml_tree_init (tree, state->myhtml);
// parse tree
- mystatus_t status = myhtml_parse(tree, MyENCODING_UTF_8, binary, binary_len);
+ mystatus_t status = myhtml_parse (tree, MyENCODING_UTF_8, bin_data, bin_size);
if (status != MyHTML_STATUS_OK)
{
-// return err_term("myhtml_parse_failed");
+ err_term (response, "myhtml_parse_failed");
+ return;
}
- // read parse flags
- parse_flags = read_parse_flags(args);
+// err_term (response, "tree_building_unimplemented");
// build tree
- myhtml_tree_node_t *root = myhtml_tree_get_document(tree);
- ETERM* result = build_tree(tree, myhtml_node_last_child(root), &parse_flags);
- myhtml_tree_destroy(tree);
+ myhtml_tree_node_t * root = myhtml_tree_get_document (tree);
+ build_tree (response, tree, root->child, parse_flags);
+ myhtml_tree_destroy (tree);
+}
+
+// a tag is sent as a tuple:
+// - a string or atom for the tag name
+// - an attribute list
+// - a children list
+// in this function, we prepare the atom and complete attribute list
+static void prepare_tag_header (ei_x_buff * response, const char * tag_string, myhtml_tree_node_t * node, unsigned char parse_flags)
+{
+ myhtml_tag_id_t tag_id = myhtml_node_tag_id (node);
+ myhtml_namespace_t tag_ns = myhtml_node_namespace (node);
- return result;
+ ei_x_encode_tuple_header (response, 3);
+
+ if (! (parse_flags & FLAG_HTML_ATOMS) || (tag_id == MyHTML_TAG__UNDEF || tag_id == MyHTML_TAG_LAST_ENTRY || tag_ns != MyHTML_NAMESPACE_HTML))
+ ei_x_encode_binary (response, tag_string, strlen (tag_string));
+ else
+ ei_x_encode_atom (response, tag_string);
+
+ prepare_node_attrs (response, node);
}
-unsigned char
-read_parse_flags(ETERM* list)
+// prepare an attribute node
+static void prepare_node_attrs(ei_x_buff * response, myhtml_tree_node_t * node)
{
- unsigned char parse_flags = 0;
- ETERM *flag;
- ETERM *html_atoms = erl_mk_atom("html_atoms");
- ETERM *nil_self_closing = erl_mk_atom("nil_self_closing");
- ETERM *comment_tuple3 = erl_mk_atom("comment_tuple3");
-
- for (; !ERL_IS_EMPTY_LIST(list); list = ERL_CONS_TAIL(list)) {
- flag = ERL_CONS_HEAD(list);
- if (erl_match(html_atoms, flag))
- {
- parse_flags |= FLAG_HTML_ATOMS;
- }
- else if (erl_match(nil_self_closing, flag))
- {
- parse_flags |= FLAG_NIL_SELF_CLOSING;
- }
- else if (erl_match(comment_tuple3, flag))
- {
- parse_flags |= FLAG_COMMENT_TUPLE3;
- }
- }
+ myhtml_tree_attr_t * attr;
- erl_free_term(html_atoms);
- erl_free_term(nil_self_closing);
- erl_free_term(comment_tuple3);
+ for (attr = myhtml_node_attribute_first (node); attr != NULL; attr = myhtml_attribute_next (attr))
+ {
+ size_t attr_name_len;
+ const char *attr_name = myhtml_attribute_key (attr, &attr_name_len);
+ size_t attr_value_len;
+ const char *attr_value = myhtml_attribute_value (attr, &attr_value_len);
- return parse_flags;
+ /* guard against poisoned attribute nodes */
+ if (! attr_name_len)
+ continue;
+
+ ei_x_encode_list_header (response, 1);
+ ei_x_encode_tuple_header (response, 2);
+ ei_x_encode_binary (response, attr_name, attr_name_len);
+
+ if (attr_value_len)
+ ei_x_encode_binary (response, attr_value, attr_value_len);
+ else
+ ei_x_encode_binary (response, attr_name, attr_name_len);
+ }
+
+ ei_x_encode_empty_list (response);
}
-ETERM* build_tree(myhtml_tree_t* tree, myhtml_tree_node_t* node, unsigned char* parse_flags)
+// dump a comment node
+static void prepare_comment (ei_x_buff * response, const char * node_comment, size_t comment_len, unsigned char parse_flags)
{
- ETERM* result;
- ETERM* atom_nil = erl_mk_atom("nil");
- ETERM* empty_list = erl_mk_empty_list();
- myhtml_tree_node_t* prev_node = NULL;
+ ei_x_encode_tuple_header (response, parse_flags & FLAG_COMMENT_TUPLE3 ? 3 : 2);
+ ei_x_encode_atom (response, "comment");
- tstack stack;
- tstack_init(&stack, 30);
- for(myhtml_tree_node_t* current_node = node;;) {
- ETERM* children;
+ if (parse_flags & FLAG_COMMENT_TUPLE3)
+ ei_x_encode_list_header (response, 0);
- // If we are going up the tree, get the children from the stack
- if (prev_node && !(current_node->next == prev_node || current_node->parent == prev_node)) {
- children = tstack_pop(&stack);
- // Else, try to go down the tree
- } else if(current_node->last_child) {
- tstack_push(&stack, erl_mk_empty_list());
+ ei_x_encode_binary (response, node_comment, comment_len);
+}
- prev_node = current_node;
- current_node = current_node->last_child;
+#ifdef DEBUG_LIST_MANIP
- continue;
- } else {
- if ((myhtml_node_is_close_self(current_node) || myhtml_node_is_void_element(current_node))
- && (*parse_flags & FLAG_NIL_SELF_CLOSING)) {
- children = atom_nil;
- } else {
- children = empty_list;
- }
- }
+#define EMIT_LIST_HDR \
+ printf ("list hdr for node %p\n", current_node); \
+ fflush (stdout); \
+ ei_x_encode_list_header (response, 1)
+
+#define EMIT_EMPTY_LIST_HDR \
+ printf ("list empty for node %p\n", current_node); \
+ fflush (stdout); \
+ ei_x_encode_list_header (response, 0)
+
+#define EMIT_LIST_TAIL \
+ printf ("list tail for node %p\n", current_node); \
+ fflush (stdout); \
+ ei_x_encode_empty_list (response)
+
+#else
+
+#define EMIT_LIST_HDR ei_x_encode_list_header (response, 1)
+#define EMIT_EMPTY_LIST_HDR ei_x_encode_list_header (response, 0)
+#define EMIT_LIST_TAIL ei_x_encode_empty_list (response)
+
+#endif
- myhtml_tag_id_t tag_id = myhtml_node_tag_id(current_node);
- myhtml_namespace_t tag_ns = myhtml_node_namespace(current_node);
+static void build_tree (ei_x_buff * response, myhtml_tree_t * tree, myhtml_tree_node_t * node, unsigned char parse_flags)
+{
+ myhtml_tree_node_t * current_node = node;
+
+ tstack stack;
+ tstack_init (&stack, 30);
+
+ // ok we're going to send an actual response so start encoding it
+ response->index = 0;
+ ei_x_encode_version (response);
+
+ while (current_node != NULL)
+ {
+ myhtml_tag_id_t tag_id = myhtml_node_tag_id (current_node);
+ myhtml_namespace_t tag_ns = myhtml_node_namespace (current_node);
if (tag_id == MyHTML_TAG__TEXT)
{
size_t text_len;
+ const char * node_text = myhtml_node_text (current_node, &text_len);
- const char* node_text = myhtml_node_text(current_node, &text_len);
- result = erl_mk_binary(node_text, text_len);
+ EMIT_LIST_HDR;
+ ei_x_encode_binary (response, node_text, text_len);
}
else if (tag_id == MyHTML_TAG__COMMENT)
{
size_t comment_len;
- const char* node_comment = myhtml_node_text(current_node, &comment_len);
+ const char* node_comment = myhtml_node_text (current_node, &comment_len);
- // For <!----> myhtml_node_text will return a null pointer, which will make erl_format segfault
- ETERM* comment = erl_mk_binary(node_comment ? node_comment : "", comment_len);
-
- if (*parse_flags & FLAG_COMMENT_TUPLE3)
- {
- result = erl_format("{comment, [], ~w}", comment);
- }
- else
- {
- result = erl_format("{comment, ~w}", comment);
- }
+ EMIT_LIST_HDR;
+ prepare_comment (response, node_comment, comment_len, parse_flags);
}
else
{
- ETERM* tag;
- ETERM* attrs;
-
// get name of tag
size_t tag_name_len;
- const char *tag_name = myhtml_tag_name_by_id(tree, tag_id, &tag_name_len);
+ const char *tag_name = myhtml_tag_name_by_id (tree, tag_id, &tag_name_len);
// get namespace of tag
size_t tag_ns_len;
- const char *tag_ns_name_ptr = myhtml_namespace_name_by_id(tag_ns, &tag_ns_len);
+ const char *tag_ns_name_ptr = myhtml_namespace_name_by_id (tag_ns, &tag_ns_len);
char buffer [tag_ns_len + tag_name_len + 2];
char *tag_string = buffer;
- size_t tag_string_len;
if (tag_ns != MyHTML_NAMESPACE_HTML)
{
// tag_ns_name_ptr is unmodifyable, copy it in our tag_ns_buffer to make it modifyable.
// +1 because myhtml uses strlen for length returned, which doesn't include the null-byte
// https://github.com/lexborisov/myhtml/blob/0ade0e564a87f46fd21693a7d8c8d1fa09ffb6b6/source/myhtml/mynamespace.c#L80
char tag_ns_buffer[tag_ns_len + 1];
- strncpy(tag_ns_buffer, tag_ns_name_ptr, sizeof(tag_ns_buffer));
- lowercase(tag_ns_buffer);
+ strncpy (tag_ns_buffer, tag_ns_name_ptr, sizeof(tag_ns_buffer));
+ lowercase (tag_ns_buffer);
- tag_string_len = tag_ns_len + tag_name_len + 1; // +1 for colon
- snprintf(tag_string, sizeof(buffer), "%s:%s", tag_ns_buffer, tag_name);
+ snprintf (tag_string, sizeof(buffer), "%s:%s", tag_ns_buffer, tag_name);
}
else
+ strncpy (tag_string, tag_name, sizeof(buffer));
+
+ if (response->index > 1)
{
- strncpy(tag_string, tag_name, sizeof(buffer));
- tag_string_len = tag_name_len;
+ EMIT_LIST_HDR;
}
- // attributes
- attrs = build_node_attrs(tree, current_node);
+ prepare_tag_header (response, tag_string, current_node, parse_flags);
- if (!(*parse_flags & FLAG_HTML_ATOMS) || (tag_id == MyHTML_TAG__UNDEF || tag_id == MyHTML_TAG_LAST_ENTRY || tag_ns != MyHTML_NAMESPACE_HTML))
- tag = erl_mk_binary(tag_string, tag_string_len);
- else
- tag = erl_mk_atom(tag_string);
-
- result = erl_format("{~w, ~w, ~w}", tag, attrs, children);
- }
+ if (current_node->child)
+ {
+ tstack_push (&stack, current_node);
+ current_node = current_node->child;
- if (stack.used == 0) {
- tstack_free(&stack);
- break;
- } else {
- tstack_push(&stack, erl_cons(result, tstack_pop(&stack)));
- prev_node = current_node;
- current_node = current_node->prev ? current_node->prev : current_node->parent;
+ continue;
+ }
+ else
+ {
+ if (parse_flags & FLAG_NIL_SELF_CLOSING && (myhtml_node_is_close_self(current_node) || myhtml_node_is_void_element(current_node)))
+ {
+#ifdef DEBUG_LIST_MANIP
+ printf ("self-closing tag %s emit nil?\n", tag_string); fflush (stdout);
+#endif
+ ei_x_encode_atom (response, "nil");
+ }
+ else
+ {
+ EMIT_EMPTY_LIST_HDR;
+ }
+ }
}
- }
-
- erl_free_term(atom_nil);
-
- return result;
-}
-
-ETERM*
-build_node_attrs(myhtml_tree_t* tree, myhtml_tree_node_t* node)
-{
- myhtml_tree_attr_t* attr;
-
- ETERM* list = erl_mk_empty_list();
-
- for (attr = myhtml_node_attribute_last(node); attr != NULL; attr = myhtml_attribute_prev(attr))
- {
- ETERM* name;
- ETERM* value;
- ETERM* attr_tuple;
-
- size_t attr_name_len;
- const char *attr_name = myhtml_attribute_key(attr, &attr_name_len);
- size_t attr_value_len;
- const char *attr_value = myhtml_attribute_value(attr, &attr_value_len);
-
- /* guard against poisoned attribute nodes */
- if (! attr_name_len)
- continue;
- name = erl_mk_binary(attr_name, attr_name_len);
- value = attr_value_len ? erl_mk_binary(attr_value, attr_value_len) : name;
+ if (current_node->next)
+ current_node = current_node->next;
+ else
+ {
+ while (! current_node->next && stack.used != 0)
+ {
+ EMIT_LIST_TAIL;
+ current_node = tstack_pop (&stack);
+ }
- /* ETERM* tuple2[] = {name, value}; */
- /* attr_tuple = erl_mk_tuple(tuple2, 2); */
- attr_tuple = erl_format("{~w, ~w}", name, value);
+ if (current_node->next)
+ current_node = current_node->next;
+ }
- list = erl_cons(attr_tuple, list);
+ // are we at root?
+ if (current_node == node)
+ break;
}
-
- return list;
}
static inline char*
lowercase(char* c)
{
char* p = c;
while(*p)
{
*p = tolower((unsigned char)*p);
p++;
}
return c;
}
diff --git a/c_src/tstack.h b/c_src/tstack.h
index 48141bc..bd8d51d 100644
--- a/c_src/tstack.h
+++ b/c_src/tstack.h
@@ -1,39 +1,38 @@
#ifndef TSTACK_H
#define TSTACK_H
-#include "ei.h"
#define GROW_BY 30
typedef struct {
- ETERM* *data;
+ myhtml_tree_node_t **data;
size_t used;
size_t size;
} tstack;
void tstack_init(tstack *stack, size_t initial_size) {
- stack->data = (ETERM **) malloc(initial_size * sizeof(ETERM*));
+ stack->data = (myhtml_tree_node_t **) malloc(initial_size * sizeof(myhtml_tree_node_t *));
stack->used = 0;
stack->size = initial_size;
}
void tstack_free(tstack *stack) {
free(stack->data);
}
void tstack_resize(tstack *stack, size_t new_size) {
- stack->data = (ETERM **)realloc(stack->data, new_size * sizeof(ETERM*));
+ stack->data = (myhtml_tree_node_t **) realloc(stack->data, new_size * sizeof(myhtml_tree_node_t *));
stack->size = new_size;
}
-void tstack_push(tstack *stack, ETERM* element) {
+void tstack_push(tstack *stack, myhtml_tree_node_t * element) {
if(stack->used == stack->size) {
tstack_resize(stack, stack->size + GROW_BY);
}
- stack->data[stack->used++] = element;
+ stack->data[stack->used++] = element;
}
-ETERM* tstack_pop(tstack *stack) {
+myhtml_tree_node_t* tstack_pop(tstack *stack) {
return stack->data[--(stack->used)];
}
#endif
diff --git a/test/myhtmlex.safe_test.exs b/test/myhtmlex.safe_test.exs
index 0054815..122cd9a 100644
--- a/test/myhtmlex.safe_test.exs
+++ b/test/myhtmlex.safe_test.exs
@@ -1,7 +1,140 @@
defmodule Myhtmlex.SafeTest do
- use MyhtmlexSharedTests, module: Myhtmlex.Safe
+ use ExUnit.Case
+ doctest Myhtmlex
test "doesn't segfault when <!----> is encountered" do
assert {"html", _attrs, _children} = Myhtmlex.decode("<div> <!----> </div>")
end
+
+ test "builds a tree, formatted like mochiweb by default" do
+ assert {"html", [],
+ [
+ {"head", [], []},
+ {"body", [],
+ [
+ {"br", [], []}
+ ]}
+ ]} = Myhtmlex.decode("<br>")
+ end
+
+ test "builds a tree, html tags as atoms" do
+ assert {:html, [],
+ [
+ {:head, [], []},
+ {:body, [],
+ [
+ {:br, [], []}
+ ]}
+ ]} = Myhtmlex.decode("<br>", format: [:html_atoms])
+ end
+
+ test "builds a tree, nil self closing" do
+ assert {"html", [],
+ [
+ {"head", [], []},
+ {"body", [],
+ [
+ {"br", [], nil},
+ {"esi:include", [], nil}
+ ]}
+ ]} = Myhtmlex.decode("<br><esi:include />", format: [:nil_self_closing])
+ end
+
+ test "builds a tree, multiple format options" do
+ assert {:html, [],
+ [
+ {:head, [], []},
+ {:body, [],
+ [
+ {:br, [], nil}
+ ]}
+ ]} = Myhtmlex.decode("<br>", format: [:html_atoms, :nil_self_closing])
+ end
+
+ test "attributes" do
+ assert {:html, [],
+ [
+ {:head, [], []},
+ {:body, [],
+ [
+ {:span, [{"id", "test"}, {"class", "foo garble"}], []}
+ ]}
+ ]} =
+ Myhtmlex.decode(~s'<span id="test" class="foo garble"></span>',
+ format: [:html_atoms]
+ )
+ end
+
+ test "single attributes" do
+ assert {:html, [],
+ [
+ {:head, [], []},
+ {:body, [],
+ [
+ {:button, [{"disabled", "disabled"}, {"class", "foo garble"}], []}
+ ]}
+ ]} =
+ Myhtmlex.decode(~s'<button disabled class="foo garble"></span>',
+ format: [:html_atoms]
+ )
+ end
+
+ test "text nodes" do
+ assert {:html, [],
+ [
+ {:head, [], []},
+ {:body, [],
+ [
+ "text node"
+ ]}
+ ]} = Myhtmlex.decode(~s'<body>text node</body>', format: [:html_atoms])
+ end
+
+ test "broken input" do
+ assert {:html, [],
+ [
+ {:head, [], []},
+ {:body, [],
+ [
+ {:a, [{"<", "<"}], [" asdf"]}
+ ]}
+ ]} = Myhtmlex.decode(~s'<a <> asdf', format: [:html_atoms])
+ end
+
+ test "namespaced tags" do
+ assert {:html, [],
+ [
+ {:head, [], []},
+ {:body, [],
+ [
+ {"svg:svg", [],
+ [
+ {"svg:path", [], []},
+ {"svg:a", [], []}
+ ]}
+ ]}
+ ]} = Myhtmlex.decode(~s'<svg><path></path><a></a></svg>', format: [:html_atoms])
+ end
+
+ test "custom namespaced tags" do
+ assert {:html, [],
+ [
+ {:head, [], []},
+ {:body, [],
+ [
+ {"esi:include", [], nil}
+ ]}
+ ]} = Myhtmlex.decode(~s'<esi:include />', format: [:html_atoms, :nil_self_closing])
+ end
+
+ test "html comments" do
+ assert {:html, [],
+ [
+ {:head, [], []},
+ {:body, [],
+ [
+ comment: " a comment "
+ ]}
+ ]} = Myhtmlex.decode(~s'<body><!-- a comment --></body>', format: [:html_atoms])
+ end
end
diff --git a/test/myhtmlex_shared_tests.ex b/test/myhtmlex_shared_tests.ex
deleted file mode 100644
index 7ba8d79..0000000
--- a/test/myhtmlex_shared_tests.ex
+++ /dev/null
@@ -1,152 +0,0 @@
-defmodule MyhtmlexSharedTests do
- defmacro __using__(opts) do
- module = Keyword.fetch!(opts, :module)
-
- quote do
- use ExUnit.Case
- doctest Myhtmlex
-
- setup_all(_) do
- Application.put_env(:myhtmlex, :mode, unquote(module))
- :ok
- end
-
- test "builds a tree, formatted like mochiweb by default" do
- assert {"html", [],
- [
- {"head", [], []},
- {"body", [],
- [
- {"br", [], []}
- ]}
- ]} = Myhtmlex.decode("<br>")
- end
-
- test "builds a tree, html tags as atoms" do
- assert {:html, [],
- [
- {:head, [], []},
- {:body, [],
- [
- {:br, [], []}
- ]}
- ]} = Myhtmlex.decode("<br>", format: [:html_atoms])
- end
-
- test "builds a tree, nil self closing" do
- assert {"html", [],
- [
- {"head", [], []},
- {"body", [],
- [
- {"br", [], nil},
- {"esi:include", [], nil}
- ]}
- ]} = Myhtmlex.decode("<br><esi:include />", format: [:nil_self_closing])
- end
-
- test "builds a tree, multiple format options" do
- assert {:html, [],
- [
- {:head, [], []},
- {:body, [],
- [
- {:br, [], nil}
- ]}
- ]} = Myhtmlex.decode("<br>", format: [:html_atoms, :nil_self_closing])
- end
-
- test "attributes" do
- assert {:html, [],
- [
- {:head, [], []},
- {:body, [],
- [
- {:span, [{"id", "test"}, {"class", "foo garble"}], []}
- ]}
- ]} =
- Myhtmlex.decode(~s'<span id="test" class="foo garble"></span>',
- format: [:html_atoms]
- )
- end
-
- test "single attributes" do
- assert {:html, [],
- [
- {:head, [], []},
- {:body, [],
- [
- {:button, [{"disabled", "disabled"}, {"class", "foo garble"}], []}
- ]}
- ]} =
- Myhtmlex.decode(~s'<button disabled class="foo garble"></span>',
- format: [:html_atoms]
- )
- end
-
- test "text nodes" do
- assert {:html, [],
- [
- {:head, [], []},
- {:body, [],
- [
- "text node"
- ]}
- ]} = Myhtmlex.decode(~s'<body>text node</body>', format: [:html_atoms])
- end
-
- test "broken input" do
- assert {:html, [],
- [
- {:head, [], []},
- {:body, [],
- [
- {:a, [{"<", "<"}], [" asdf"]}
- ]}
- ]} = Myhtmlex.decode(~s'<a <> asdf', format: [:html_atoms])
- end
-
- test "namespaced tags" do
- assert {:html, [],
- [
- {:head, [], []},
- {:body, [],
- [
- {"svg:svg", [],
- [
- {"svg:path", [], []},
- {"svg:a", [], []}
- ]}
- ]}
- ]} = Myhtmlex.decode(~s'<svg><path></path><a></a></svg>', format: [:html_atoms])
- end
-
- test "custom namespaced tags" do
- assert {:html, [],
- [
- {:head, [], []},
- {:body, [],
- [
- {"esi:include", [], nil}
- ]}
- ]} =
- Myhtmlex.decode(~s'<esi:include />', format: [:html_atoms, :nil_self_closing])
- end
-
- test "html comments" do
- assert {:html, [],
- [
- {:head, [], []},
- {:body, [],
- [
- comment: " a comment "
- ]}
- ]} = Myhtmlex.decode(~s'<body><!-- a comment --></body>', format: [:html_atoms])
- end
- end
-
- # quote
- end
-
- # defmacro __using__
-end
diff --git a/test/test_helper.exs b/test/test_helper.exs
index 8efc5f3..869559e 100644
--- a/test/test_helper.exs
+++ b/test/test_helper.exs
@@ -1,3 +1 @@
-Code.require_file("myhtmlex_shared_tests.ex", "test")
-
ExUnit.start()
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Wed, Jun 25, 12:58 AM (16 h, 55 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
235093
Default Alt Text
(30 KB)
Attached To
Mode
R16 fast_html
Attached
Detach File
Event Timeline
Log In to Comment