Current section

6 Versions

Jump to

Compare versions

23 files changed
+479 additions
-17 deletions
  @@ -6,6 +6,37 @@ Nokogiri. But like, for Elixir.
6 6
7 7 **DOUBLE NOTE**: C is **not** my first language. I've probably got memory leaks and just plain logic errors hanging about all over. Contributions and criticism welcome.
8 8
9 + ## Features
10 +
11 + * XML Documents
12 + * [x] Read document from string
13 + * [ ] Read document from path
14 + * [x] Create new documents
15 + * [ ] Attribute support
16 + * [ ] Get/set for attributes by namespace
17 + * [x] Get/set without namespace
18 + * [ ] Element support
19 + * [x] Create new elements
20 + * [x] Remove elements
21 + * [x] Move elements
22 + * [ ] Copy elements
23 + * [ ] XML fragment support
24 + * [x] Set element content
25 + * [ ] Append element content
26 + * XML Schemas
27 + * [x] Load schemas from string (with path awareness for includes)
28 + * [ ] Load schemas from path
29 + * [x] In-memory document validation
30 + * [ ] Streaming document validation (not fully supported by libxml2 - may not ever be completed)
31 + * XSL Transforms
32 + * [x] Simple File transforms and parameters
33 + * [ ] Included/multi-file transform support
34 + * XPath Support
35 + * [x] Return element lists
36 + * [ ] Return non-element result sets
37 + * [x] Namespaced queries
38 + * [ ] Namespaceless queries
39 +
9 40 ## Installation
10 41
11 42 Using [Hex](https://hex.pm), the package can be installed by adding `exogiri` to your list of dependencies in `mix.exs`:
  @@ -13,7 +44,7 @@ Using [Hex](https://hex.pm), the package can be installed by adding `exogiri` to
13 44 ```elixir
14 45 def deps do
15 46 [
16 - {:exogiri, "~> 0.2.3"}
47 + {:exogiri, "~> 0.3.0"}
17 48 ]
18 49 end
19 50 ```
  @@ -3,24 +3,29 @@
3 3 #include "document.h"
4 4 #include "xpath.h"
5 5 #include "schema.h"
6 + #include "stylesheet.h"
6 7
7 8 static int open_resource(ErlNifEnv* env)
8 9 {
9 10 const char* d_mod_str = "Elixir.Exogiri.Xml.Internal.Nif.Document";
10 11 const char* n_mod_str = "Elixir.Exogiri.Xml.Internal.Nif.Node";
11 12 const char* s_mod_str = "Elixir.Exogiri.Xml.Internal.Nif.Schema";
13 + const char* ss_mod_str = "Elixir.Exogiri.Xml.Internal.Nif.StyleSheet";
12 14 const char* d_name = "exogiri_xml_document";
13 15 const char* n_name = "exogiri_xml_node";
14 16 const char* s_name = "exogiri_xml_schema";
17 + const char* ss_name = "exogiri_xml_stylesheet";
15 18
16 19 int flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER;
17 20
18 21 EXD_RES_TYPE = enif_open_resource_type(env, d_mod_str, d_name, free_document, flags, NULL);
19 22 EXN_RES_TYPE = enif_open_resource_type(env, n_mod_str, n_name, free_node, flags, NULL);
20 23 EXS_RES_TYPE = enif_open_resource_type(env, s_mod_str, s_name, free_schema, flags, NULL);
24 + EXSS_RES_TYPE = enif_open_resource_type(env, ss_mod_str, ss_name, free_stylesheet, flags, NULL);
21 25 if(EXD_RES_TYPE == NULL) return -1;
22 26 if(EXN_RES_TYPE == NULL) return -1;
23 27 if(EXS_RES_TYPE == NULL) return -1;
28 + if(EXSS_RES_TYPE == NULL) return -1;
24 29 return 0;
25 30 }
26 31
  @@ -76,6 +81,8 @@ static ErlNifFunc nif_funcs[] =
76 81 {"priv_schema_from_string", 1, priv_schema_from_string, ERL_NIF_DIRTY_JOB_CPU_BOUND},
77 82 {"priv_schema_from_string_with_path", 2, priv_schema_from_string_with_path, ERL_NIF_DIRTY_JOB_CPU_BOUND},
78 83 {"priv_schema_validate_doc", 2, priv_schema_validate_doc, ERL_NIF_DIRTY_JOB_CPU_BOUND},
84 + {"priv_stylesheet_from_string", 1, priv_stylesheet_from_string, ERL_NIF_DIRTY_JOB_CPU_BOUND},
85 + {"priv_stylesheet_transform", 3, priv_stylesheet_transform, ERL_NIF_DIRTY_JOB_CPU_BOUND},
79 86 {"priv_doc_new_root_no_ns", 1, priv_doc_new_root_no_ns, 0},
80 87 {"priv_doc_new_root_with_ns", 3, priv_doc_new_root_with_ns, 0}
81 88 };
  @@ -7,12 +7,16 @@
7 7 #include <libxml/xpathInternals.h>
8 8 #include <libxml/c14n.h>
9 9 #include <libxml/xmlschemas.h>
10 + #include <libxslt/xsltInternals.h>
11 + #include <libxslt/xsltutils.h>
12 + #include <libxslt/transform.h>
10 13
11 14 #include "unlinked_node.h"
12 15
13 16 ErlNifResourceType* EXD_RES_TYPE;
14 17 ErlNifResourceType* EXN_RES_TYPE;
15 18 ErlNifResourceType* EXS_RES_TYPE;
19 + ErlNifResourceType* EXSS_RES_TYPE;
16 20
17 21 typedef struct {
18 22 ErlNifPid* owner;
  @@ -39,7 +43,13 @@ typedef struct {
39 43 xmlDocPtr doc;
40 44 } Schema;
41 45
46 + typedef struct {
47 + ErlNifPid* owner;
48 + xsltStylesheetPtr stylesheet;
49 + } Stylesheet;
50 +
42 51 void Exogiri_error_array_pusher(void *ctx, xmlErrorPtr error);
52 + char *nif_binary_to_char(ErlNifBinary * bin);
43 53 xmlChar *nif_binary_to_xmlChar(ErlNifBinary * bin);
44 54 ERL_NIF_TERM xml_char_to_binary_term(ErlNifEnv* env, xmlChar *content);
  @@ -3,6 +3,7 @@
3 3 void free_schema(__attribute__((unused))ErlNifEnv* env, void* obj) {
4 4 Schema* schema = (Schema*)obj;
5 5 xmlSchemaFree(schema->schema);
6 + enif_free(schema->owner);
6 7 if (schema->doc) {
7 8 xmlFreeDoc(schema->doc);
8 9 }
  @@ -0,0 +1,325 @@
1 + #include "exogiri.h"
2 +
3 + void free_stylesheet(__attribute__((unused))ErlNifEnv* env, void* obj) {
4 + Stylesheet* stylesheet = (Stylesheet*)obj;
5 + xsltFreeStylesheet(stylesheet->stylesheet);
6 + enif_free(stylesheet->owner);
7 + }
8 +
9 + static void
10 + xslt_generic_error_handler(void *ctx, const char *msg, ...)
11 + {
12 + Errors* errors = (Errors*)ctx;
13 + ERL_NIF_TERM new_error;
14 + ERL_NIF_TERM existing_errors;
15 + char *message;
16 + size_t err_size;
17 + va_list args;
18 + va_list args_for_len;
19 +
20 + va_start(args, msg);
21 + va_copy(args_for_len, args);
22 + err_size = vsnprintf(NULL, 0, msg, args_for_len);
23 + message = (char*)malloc(sizeof(char) * err_size + 1);
24 + vsnprintf(message, err_size, msg, args);
25 + va_end(args);
26 + existing_errors = errors->errors;
27 + new_error = enif_make_string(errors->env, message, ERL_NIF_LATIN1);
28 + errors->errors = enif_make_list_cell(
29 + errors->env,
30 + new_error,
31 + existing_errors
32 + );
33 + free(message);
34 + }
35 +
36 + static ERL_NIF_TERM read_stylesheet_from_doc(ErlNifEnv* env, xmlDocPtr doc) {
37 + ErlNifPid *self;
38 + xsltStylesheetPtr ss;
39 + Errors* parse_errors;
40 + ERL_NIF_TERM result;
41 + ERL_NIF_TERM pf_atom;
42 + ERL_NIF_TERM atom_result;
43 + unsigned int err_len;
44 +
45 + self = (ErlNifPid *)enif_alloc(sizeof(ErlNifPid));
46 + enif_self(env, self);
47 +
48 + parse_errors = (Errors*)enif_alloc(sizeof(Errors));
49 + parse_errors->env = env;
50 + parse_errors->errors = enif_make_list(env, 0);
51 +
52 + xsltSetGenericErrorFunc((void *)parse_errors, xslt_generic_error_handler);
53 + ss = xsltParseStylesheetDoc(doc);
54 + xsltSetGenericErrorFunc(NULL, NULL);
55 + enif_get_list_length(env,parse_errors->errors,&err_len);
56 + if (err_len > 0) {
57 + if (!ss) {
58 + xmlFreeDoc(doc);
59 + } else {
60 + xsltFreeStylesheet(ss);
61 + }
62 + ASSIGN_ERROR(env, atom_result);
63 + result = enif_make_tuple2(
64 + env,
65 + atom_result,
66 + parse_errors->errors
67 + );
68 + enif_free(parse_errors);
69 + return result;
70 + }
71 + if (!ss) {
72 + xmlFreeDoc(doc);
73 + enif_make_existing_atom(
74 + env,
75 + "unknown_error",
76 + &pf_atom,
77 + ERL_NIF_LATIN1
78 + );
79 + ASSIGN_ERROR(env, atom_result);
80 + result = enif_make_tuple2(
81 + env,
82 + atom_result,
83 + pf_atom
84 + );
85 + enif_free(parse_errors);
86 + return result;
87 + }
88 +
89 + enif_free(parse_errors);
90 +
91 + Stylesheet* ssRes = (Stylesheet *)enif_alloc_resource(EXSS_RES_TYPE, sizeof(Stylesheet));
92 + ssRes->owner = self;
93 + ssRes->stylesheet = ss;
94 + result = enif_make_resource(env, ssRes);
95 + enif_release_resource(ssRes);
96 +
97 + ASSIGN_OK(env, atom_result);
98 + return enif_make_tuple2(
99 + env,
100 + atom_result,
101 + result
102 + );
103 + }
104 +
105 + ERL_NIF_TERM priv_stylesheet_from_string(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
106 + ErlNifBinary nb;
107 + ERL_NIF_TERM atom_result;
108 + xmlDocPtr doc;
109 + Errors* parse_errors;
110 + ERL_NIF_TERM pf_atom;
111 + ERL_NIF_TERM result;
112 + unsigned int err_len;
113 +
114 + if(argc != 1)
115 + {
116 + return enif_make_badarg(env);
117 + }
118 + if (!enif_inspect_binary(env, argv[0], &nb)) {
119 + return enif_make_badarg(env);
120 + }
121 +
122 + parse_errors = (Errors*)enif_alloc(sizeof(Errors));
123 + parse_errors->env = env;
124 + parse_errors->errors = enif_make_list(env, 0);
125 +
126 + xmlSetStructuredErrorFunc((void *)parse_errors, Exogiri_error_array_pusher);
127 + doc = xmlReadMemory(
128 + (const char *)nb.data,
129 + nb.size,
130 + NULL,
131 + NULL,
132 + 0
133 + );
134 + xmlSetStructuredErrorFunc(NULL, NULL);
135 +
136 + enif_get_list_length(env,parse_errors->errors,&err_len);
137 + if (err_len > 0) {
138 + if (doc) {
139 + xmlFreeDoc(doc);
140 + }
141 + ASSIGN_ERROR(env, atom_result);
142 + result = enif_make_tuple2(
143 + env,
144 + atom_result,
145 + parse_errors->errors
146 + );
147 + enif_free(parse_errors);
148 + return result;
149 + }
150 + if (!doc) {
151 + enif_make_existing_atom(
152 + env,
153 + "unknown_error",
154 + &pf_atom,
155 + ERL_NIF_LATIN1
156 + );
157 + ASSIGN_ERROR(env, atom_result);
158 + result = enif_make_tuple2(
159 + env,
160 + atom_result,
161 + pf_atom
162 + );
163 + enif_free(parse_errors);
164 + return result;
165 + }
166 +
167 + enif_free(parse_errors);
168 +
169 + return read_stylesheet_from_doc(env, doc);
170 + }
171 +
172 + static void free_params_array(char **p_array) {
173 + char *cur_ptr;
174 +
175 + if (!p_array) {
176 + return;
177 + }
178 +
179 + int i = 0;
180 + cur_ptr = p_array[i];
181 +
182 + while(cur_ptr != NULL) {
183 + enif_free(cur_ptr);
184 + i++;
185 + cur_ptr = p_array[i];
186 + }
187 +
188 + enif_free(p_array);
189 + }
190 +
191 + static char **extract_transform_params(ErlNifEnv* env, ERL_NIF_TERM param_list, unsigned int list_size) {
192 + char **transform_params;
193 + ERL_NIF_TERM head;
194 + ERL_NIF_TERM tail;
195 + ERL_NIF_TERM tail2;
196 + ERL_NIF_TERM current;
197 + ErlNifBinary kb;
198 + ErlNifBinary vb;
199 + char *kc;
200 + char *vc;
201 + size_t allocated_size;
202 +
203 + if (list_size < 1) {
204 + return NULL;
205 + }
206 +
207 + allocated_size = (sizeof(char *) * list_size) + 1;
208 +
209 + transform_params = enif_alloc(allocated_size);
210 + transform_params[list_size] = NULL;
211 +
212 + current = param_list;
213 +
214 + for (unsigned int i = 0; i < list_size; i = i + 2) {
215 + enif_get_list_cell(env, current, &head, &tail);
216 + enif_inspect_binary(env, head, &kb);
217 + kc = nif_binary_to_char(&kb);
218 + enif_get_list_cell(env, tail, &head, &tail2);
219 + enif_inspect_binary(env, head, &vb);
220 + vc = nif_binary_to_char(&vb);
221 + transform_params[i] = kc;
222 + transform_params[i + 1] = vc;
223 + current = tail2;
224 + }
225 +
226 + return transform_params;
227 + }
228 +
229 + ERL_NIF_TERM priv_stylesheet_transform(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
230 + ERL_NIF_TERM atom_result;
231 + ERL_NIF_TERM pf_atom;
232 + ERL_NIF_TERM result;
233 + xmlDocPtr result_doc;
234 + Document *document;
235 + Stylesheet *s_sheet;
236 + ErlNifPid self;
237 + ErlNifPid *doc_owner;
238 + Errors *parse_errors;
239 + char **transform_params;
240 + unsigned int err_len;
241 + unsigned int param_len;
242 +
243 + if(argc != 3)
244 + {
245 + return enif_make_badarg(env);
246 + }
247 + if (!enif_get_resource(env, argv[0],EXSS_RES_TYPE,(void **)&s_sheet)) {
248 + return enif_make_badarg(env);
249 + }
250 + if (!enif_get_resource(env, argv[1],EXD_RES_TYPE,(void **)&document)) {
251 + return enif_make_badarg(env);
252 + }
253 + if (!enif_is_list(env, argv[2])) {
254 + return enif_make_badarg(env);
255 + }
256 +
257 + CHECK_STRUCT_OWNER(env, self, s_sheet)
258 + CHECK_STRUCT_OWNER(env, self, document)
259 +
260 + parse_errors = (Errors*)enif_alloc(sizeof(Errors));
261 + parse_errors->env = env;
262 + parse_errors->errors = enif_make_list(env, 0);
263 +
264 + enif_get_list_length(env, argv[2], &param_len);
265 + transform_params = extract_transform_params(env, argv[2],param_len);
266 +
267 + xsltSetGenericErrorFunc((void *)parse_errors, xslt_generic_error_handler);
268 + xmlSetGenericErrorFunc((void *)parse_errors, xslt_generic_error_handler);
269 + result_doc = xsltApplyStylesheet(s_sheet->stylesheet, document->doc, (const char **)transform_params);;
270 + xsltSetGenericErrorFunc(NULL, NULL);
271 + xmlSetGenericErrorFunc(NULL, NULL);
272 +
273 + free_params_array(transform_params);
274 +
275 + enif_get_list_length(env,parse_errors->errors,&err_len);
276 + if (err_len > 0) {
277 + if (result_doc) {
278 + xmlFreeDoc(result_doc);
279 + }
280 + ASSIGN_ERROR(env, atom_result);
281 + result = enif_make_tuple2(
282 + env,
283 + atom_result,
284 + parse_errors->errors
285 + );
286 + enif_free(parse_errors);
287 + return result;
288 + }
289 + if (!result_doc) {
290 + enif_make_existing_atom(
291 + env,
292 + "unknown_error",
293 + &pf_atom,
294 + ERL_NIF_LATIN1
295 + );
296 + ASSIGN_ERROR(env, atom_result);
297 + result = enif_make_tuple2(
298 + env,
299 + atom_result,
300 + pf_atom
301 + );
302 + enif_free(parse_errors);
303 + return result;
304 + }
305 +
306 + enif_free(parse_errors);
307 +
308 + doc_owner = (ErlNifPid *)enif_alloc(sizeof(ErlNifPid));
309 + enif_self(env, doc_owner);
310 +
311 + Document* docRes = (Document *)enif_alloc_resource(EXD_RES_TYPE, sizeof(Document));
312 + docRes->owner = doc_owner;
313 + docRes->doc = result_doc;
314 + docRes->unlinked_nodes = NULL;
315 + docRes->unlinked_nses = NULL;
316 + result = enif_make_resource(env, docRes);
317 + enif_release_resource(docRes);
318 +
319 + ASSIGN_OK(env, atom_result);
320 + return enif_make_tuple2(
321 + env,
322 + atom_result,
323 + result
324 + );
325 + }
\ No newline at end of file
Loading more files…