Page MenuHomePhorge

No OneTemporary

Size
30 KB
Referenced Files
None
Subscribers
None
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

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)

Event Timeline