rexpat 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,864 @@
1
+ /*
2
+ * Expat (XML Parser Toolkit) wrapper for Ruby.
3
+ *
4
+ * Copyright (C) 2013 吉田正人
5
+ * Copyright (C) 2026 gemmaro
6
+ *
7
+ * This work is licensed under the Ruby license or the Expat license.
8
+ */
9
+
10
+ /* The prefix "rbx_" means RuBy Xmlparser. TODO: Rename to "rbe_",
11
+ which means RuBy Expat. */
12
+
13
+ #include <ruby.h>
14
+ #include <ruby/encoding.h>
15
+
16
+ #include <expat.h>
17
+
18
+ #include <stdbool.h>
19
+ #include <stddef.h>
20
+
21
+ _Static_assert (sizeof (VALUE) >= sizeof (void *),
22
+ "It must be safe to cast from void* to Ruby's VALUE.");
23
+
24
+ static VALUE rbe_eError;
25
+ static VALUE rbe_cParser;
26
+ static ID id_on_start_element;
27
+ static ID id_on_end_element;
28
+ static ID id_on_character_data;
29
+ static ID id_on_processing_instruction;
30
+ static ID id_on_default;
31
+ static ID id_on_default_expand;
32
+ static ID id_on_notation_decl;
33
+ static ID id_on_external_entity_ref;
34
+ static ID id_on_comment;
35
+ static ID id_on_start_cdata_section;
36
+ static ID id_on_end_cdata_section;
37
+ static ID id_on_start_namespace_decl;
38
+ static ID id_on_end_namespace_decl;
39
+ static ID id_on_not_standalone;
40
+ static ID id_on_start_doctype_decl;
41
+ static ID id_on_end_doctype_decl;
42
+ static ID id_on_element_decl;
43
+ static ID id_on_attlist_decl;
44
+ static ID id_on_xml_decl;
45
+ static ID id_on_entity_decl;
46
+ static ID id_on_skipped_entity;
47
+
48
+ struct rbe_parser
49
+ {
50
+ XML_Parser parser;
51
+ bool default_current;
52
+ const XML_Char **last_attrs;
53
+ VALUE parent;
54
+ };
55
+
56
+ static void
57
+ rbx_cParser_free (void *ptr)
58
+ {
59
+ struct rbe_parser *parser = ptr;
60
+ if (parser->parser)
61
+ {
62
+ XML_ParserFree (parser->parser);
63
+ parser->parser = NULL;
64
+ }
65
+ ruby_xfree (parser);
66
+ }
67
+
68
+ static void
69
+ rbx_cParser_mark (void *ptr)
70
+ {
71
+ struct rbe_parser *parser = ptr;
72
+ if (!RB_NIL_P (parser->parent))
73
+ rb_gc_mark (parser->parent);
74
+ }
75
+
76
+ static size_t
77
+ rbx_cParser_size (const void *ptr)
78
+ {
79
+ (void)ptr;
80
+ return sizeof (struct rbe_parser);
81
+ }
82
+
83
+ static const rb_data_type_t rbx_cParser_type = {
84
+ .wrap_struct_name = "rexpat parser",
85
+ .function = {
86
+ .dfree = rbx_cParser_free,
87
+ .dmark = rbx_cParser_mark,
88
+ .dsize = rbx_cParser_size,
89
+ },
90
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
91
+ };
92
+
93
+ static VALUE
94
+ rbx_cParser_alloc (VALUE klass)
95
+ {
96
+ struct rbe_parser *parser = ruby_xmalloc (sizeof (struct rbe_parser));
97
+ *parser = (struct rbe_parser){ .parent = RUBY_Qnil };
98
+ return rb_data_typed_object_wrap (klass, parser, &rbx_cParser_type);
99
+ }
100
+
101
+ inline static VALUE
102
+ freeze_object (VALUE obj)
103
+ {
104
+ rb_obj_freeze_inline (obj);
105
+ return obj;
106
+ }
107
+
108
+ static VALUE
109
+ make_content_array (struct rbe_parser *parser, XML_Content *model)
110
+ {
111
+ VALUE type_name;
112
+ switch (model->type)
113
+ {
114
+ case XML_CTYPE_EMPTY:
115
+ type_name = rb_id2sym (rb_intern ("empty"));
116
+ break;
117
+ case XML_CTYPE_ANY:
118
+ type_name = rb_id2sym (rb_intern ("any"));
119
+ break;
120
+ case XML_CTYPE_MIXED:
121
+ type_name = rb_id2sym (rb_intern ("mixed"));
122
+ break;
123
+ case XML_CTYPE_NAME:
124
+ type_name = rb_id2sym (rb_intern ("name"));
125
+ break;
126
+ case XML_CTYPE_CHOICE:
127
+ type_name = rb_id2sym (rb_intern ("choice"));
128
+ break;
129
+ case XML_CTYPE_SEQ:
130
+ type_name = rb_id2sym (rb_intern ("seq"));
131
+ break;
132
+ }
133
+ VALUE quant_name;
134
+ switch (model->quant)
135
+ {
136
+ case XML_CQUANT_NONE:
137
+ quant_name = rb_id2sym (rb_intern ("none"));
138
+ break;
139
+ case XML_CQUANT_OPT:
140
+ quant_name = rb_id2sym (rb_intern ("opt"));
141
+ break;
142
+ case XML_CQUANT_REP:
143
+ quant_name = rb_id2sym (rb_intern ("rep"));
144
+ break;
145
+ case XML_CQUANT_PLUS:
146
+ quant_name = rb_id2sym (rb_intern ("plus"));
147
+ break;
148
+ }
149
+ VALUE ret = rb_ary_new_from_args (
150
+ 3, type_name, quant_name,
151
+ (model->name ? rb_utf8_str_new_cstr (model->name) : RUBY_Qnil));
152
+ VALUE children = RUBY_Qnil;
153
+ if (model->numchildren > 0)
154
+ {
155
+ children = rb_ary_new ();
156
+ for (unsigned int i = 0; i < model->numchildren; i++)
157
+ {
158
+ VALUE child = make_content_array (parser, model->children + i);
159
+ rb_ary_push (children, child);
160
+ }
161
+ }
162
+ rb_ary_push (ret, children);
163
+ return ret;
164
+ }
165
+
166
+ static inline VALUE
167
+ standalone_to_ruby (int standalone)
168
+ {
169
+ switch (standalone)
170
+ {
171
+ case -1:
172
+ return RUBY_Qnil;
173
+ case 0:
174
+ return RUBY_Qfalse;
175
+ case 1:
176
+ return RUBY_Qtrue;
177
+ default:
178
+ rb_raise (rbe_eError, "unreachable");
179
+ return RUBY_Qnil;
180
+ }
181
+ }
182
+
183
+ static void
184
+ on_start_element (void *userdata, const XML_Char *name, const XML_Char **atts)
185
+ {
186
+ VALUE recv = (VALUE)userdata;
187
+ struct rbe_parser *parser = rb_check_typeddata (recv, &rbx_cParser_type);
188
+ parser->last_attrs = atts;
189
+ VALUE attrhash = rb_hash_new ();
190
+ while (*atts)
191
+ {
192
+ const char *key = *atts++;
193
+ const char *val = *atts++;
194
+ rb_hash_aset (attrhash, freeze_object (rb_utf8_str_new_cstr (key)),
195
+ rb_utf8_str_new_cstr (val));
196
+ }
197
+ rb_funcall ((VALUE)recv, id_on_start_element, 2, rb_utf8_str_new_cstr (name),
198
+ attrhash);
199
+ }
200
+
201
+ static void
202
+ end_element_handler (void *userdata, const XML_Char *name)
203
+ {
204
+ VALUE recv = (VALUE)userdata;
205
+ rb_funcall ((VALUE)recv, id_on_end_element, 1, rb_utf8_str_new_cstr (name));
206
+ }
207
+
208
+ static void
209
+ character_data_handler (void *userdata, const XML_Char *s, int len)
210
+ {
211
+ VALUE recv = (VALUE)userdata;
212
+ rb_funcall ((VALUE)recv, id_on_character_data, 1, rb_utf8_str_new (s, len));
213
+ }
214
+
215
+ static void
216
+ on_processing_instruction (void *userdata, const XML_Char *target,
217
+ const XML_Char *data)
218
+ {
219
+ VALUE recv = (VALUE)userdata;
220
+ rb_funcall (recv, id_on_processing_instruction, 2,
221
+ rb_utf8_str_new_cstr (target), rb_utf8_str_new_cstr (data));
222
+ }
223
+
224
+ static void
225
+ default_handler (void *userdata, const XML_Char *s, int len)
226
+ {
227
+ VALUE recv = (VALUE)userdata;
228
+ rb_funcall ((VALUE)recv, id_on_default, 1, rb_utf8_str_new (s, len));
229
+ }
230
+
231
+ static void
232
+ default_expand_handler (void *userdata, const XML_Char *s, int len)
233
+ {
234
+ VALUE recv = (VALUE)userdata;
235
+ rb_funcall (recv, id_on_default_expand, 1, rb_utf8_str_new (s, len));
236
+ }
237
+
238
+ void
239
+ on_notation_decl (void *userdata, const XML_Char *notationName,
240
+ const XML_Char *base, const XML_Char *systemId,
241
+ const XML_Char *publicId)
242
+ {
243
+ VALUE recv = (VALUE)userdata;
244
+ rb_funcall ((VALUE)recv, id_on_notation_decl, 4,
245
+ rb_utf8_str_new_cstr (notationName),
246
+ (base ? rb_utf8_str_new_cstr (base) : RUBY_Qnil),
247
+ (systemId ? rb_utf8_str_new_cstr (systemId) : RUBY_Qnil),
248
+ (publicId ? rb_utf8_str_new_cstr (publicId) : RUBY_Qnil));
249
+ }
250
+
251
+ int
252
+ on_external_entity_ref (XML_Parser parser, const XML_Char *context,
253
+ const XML_Char *base, const XML_Char *systemId,
254
+ const XML_Char *publicId)
255
+ {
256
+ VALUE recv;
257
+
258
+ recv = (VALUE)XML_GetUserData (parser);
259
+ rb_funcall (recv, id_on_external_entity_ref, 4,
260
+ (context ? rb_utf8_str_new_cstr (context) : RUBY_Qnil),
261
+ (base ? rb_utf8_str_new_cstr (base) : RUBY_Qnil),
262
+ (systemId ? rb_utf8_str_new_cstr (systemId) : RUBY_Qnil),
263
+ (publicId ? rb_utf8_str_new_cstr (publicId) : RUBY_Qnil));
264
+ /* The error status in this handler should be returned
265
+ by the exception. */
266
+ return Qnil;
267
+ }
268
+
269
+ static void
270
+ comment_handler (void *userdata, const XML_Char *s)
271
+ {
272
+ VALUE recv = (VALUE)userdata;
273
+ rb_funcall ((VALUE)recv, id_on_comment, 1, rb_utf8_str_new_cstr (s));
274
+ }
275
+
276
+ static void
277
+ start_cdata_section_handler (void *userdata)
278
+ {
279
+ VALUE recv = (VALUE)userdata;
280
+ rb_funcall ((VALUE)recv, id_on_start_cdata_section, 0);
281
+ }
282
+
283
+ static void
284
+ end_cdata_section_handler (void *userdata)
285
+ {
286
+ VALUE recv = (VALUE)userdata;
287
+ rb_funcall ((VALUE)recv, id_on_end_cdata_section, 0);
288
+ }
289
+
290
+ static void
291
+ start_namespace_decl_handler (void *userdata, const XML_Char *prefix,
292
+ const XML_Char *uri)
293
+ {
294
+ VALUE recv = (VALUE)userdata;
295
+ rb_funcall ((VALUE)recv, id_on_start_namespace_decl, 2,
296
+ (prefix ? rb_utf8_str_new_cstr (prefix) : RUBY_Qnil),
297
+ (uri ? rb_utf8_str_new_cstr (uri) : RUBY_Qnil));
298
+ }
299
+
300
+ static void
301
+ end_namespace_decl_handler (void *userdata, const XML_Char *prefix)
302
+ {
303
+ VALUE recv = (VALUE)userdata;
304
+ rb_funcall ((VALUE)recv, id_on_end_namespace_decl, 1,
305
+ (prefix ? rb_utf8_str_new_cstr (prefix) : RUBY_Qnil));
306
+ }
307
+
308
+ static int
309
+ not_standalone_handler (void *userdata)
310
+ {
311
+ VALUE recv = (VALUE)userdata;
312
+ VALUE v = rb_funcall ((VALUE)recv, id_on_not_standalone, 0);
313
+ return RB_TEST (v) ? 1 : 0;
314
+ }
315
+
316
+ static void
317
+ on_start_doctype_decl (void *userdata, const XML_Char *doctypeName,
318
+ const XML_Char *sysid, const XML_Char *pubid,
319
+ int has_internal_subset)
320
+ {
321
+ VALUE recv = (VALUE)userdata;
322
+ rb_funcall ((VALUE)recv, id_on_start_doctype_decl, 4,
323
+ rb_utf8_str_new_cstr (doctypeName),
324
+ (sysid ? rb_utf8_str_new_cstr (sysid) : RUBY_Qnil),
325
+ (pubid ? rb_utf8_str_new_cstr (pubid) : RUBY_Qnil),
326
+ (has_internal_subset ? RUBY_Qtrue : RUBY_Qfalse));
327
+ }
328
+
329
+ static void
330
+ end_doctype_decl_handler (void *userdata)
331
+ {
332
+ VALUE recv = (VALUE)userdata;
333
+ rb_funcall ((VALUE)recv, id_on_end_doctype_decl, 0);
334
+ }
335
+
336
+ static void
337
+ element_decl_handler (void *userdata, const XML_Char *name, XML_Content *model)
338
+ {
339
+ VALUE recv = (VALUE)userdata;
340
+ struct rbe_parser *parser = rb_check_typeddata (recv, &rbx_cParser_type);
341
+ VALUE content = make_content_array (parser, model);
342
+ rb_funcall ((VALUE)recv, id_on_element_decl, 2, rb_utf8_str_new_cstr (name),
343
+ content);
344
+ }
345
+
346
+ static void
347
+ attlist_decl_handler (void *userdata, const XML_Char *elname,
348
+ const XML_Char *attname, const XML_Char *att_type,
349
+ const XML_Char *dflt, int isrequired)
350
+ {
351
+ VALUE recv = (VALUE)userdata;
352
+ rb_funcall ((VALUE)recv, id_on_attlist_decl, 5,
353
+ rb_utf8_str_new_cstr (elname), rb_utf8_str_new_cstr (attname),
354
+ rb_utf8_str_new_cstr (att_type),
355
+ (dflt ? rb_utf8_str_new_cstr (dflt) : RUBY_Qnil),
356
+ (isrequired ? RUBY_Qtrue : RUBY_Qfalse));
357
+ }
358
+
359
+ static void
360
+ xml_decl_handler (void *userdata, const XML_Char *version,
361
+ const XML_Char *encoding, int standalone)
362
+ {
363
+ VALUE recv = (VALUE)userdata;
364
+ rb_funcall ((VALUE)recv, id_on_xml_decl, 3,
365
+ (version ? rb_utf8_str_new_cstr (version) : RUBY_Qnil),
366
+ (encoding ? rb_utf8_str_new_cstr (encoding) : RUBY_Qnil),
367
+ standalone_to_ruby (standalone));
368
+ }
369
+
370
+ static void
371
+ on_entity_decl (void *userdata, const XML_Char *entityName,
372
+ int is_parameter_entity, const XML_Char *value,
373
+ int value_length, const XML_Char *base,
374
+ const XML_Char *systemId, const XML_Char *publicId,
375
+ const XML_Char *notationName)
376
+ {
377
+ VALUE recv = (VALUE)userdata;
378
+ rb_funcall (
379
+ (VALUE)recv, id_on_entity_decl, 7, rb_utf8_str_new_cstr (entityName),
380
+ (is_parameter_entity ? RUBY_Qtrue : RUBY_Qfalse),
381
+ rb_utf8_str_new (value, value_length),
382
+ (base ? rb_utf8_str_new_cstr (base) : RUBY_Qnil),
383
+ (systemId ? rb_utf8_str_new_cstr (systemId) : RUBY_Qnil),
384
+ (publicId ? rb_utf8_str_new_cstr (publicId) : RUBY_Qnil),
385
+ (notationName ? rb_utf8_str_new_cstr (notationName) : RUBY_Qnil));
386
+ }
387
+
388
+ static void
389
+ on_skipped_entity (void *userdata, const XML_Char *entityName,
390
+ int is_parameter_entity)
391
+ {
392
+ VALUE recv = (VALUE)userdata;
393
+ rb_funcall ((VALUE)recv, id_on_skipped_entity, 2,
394
+ rb_utf8_str_new_cstr (entityName),
395
+ is_parameter_entity ? RUBY_Qtrue : RUBY_Qfalse);
396
+ }
397
+
398
+ static VALUE
399
+ rbx_cParser_m_initialize (int argc, VALUE *argv, VALUE self)
400
+ {
401
+ VALUE kwargs;
402
+ rb_scan_args (argc, argv, ":", &kwargs);
403
+
404
+ ID kwargs_ids[] = { rb_intern ("encoding"), rb_intern ("nssep") };
405
+ VALUE kwargs_vals[sizeof (kwargs_ids) / sizeof (kwargs_ids[0])] = { 0 };
406
+ rb_get_kwargs (kwargs, kwargs_ids, 0, 2, kwargs_vals);
407
+ VALUE encoding = kwargs_vals[0], nssep = kwargs_vals[1];
408
+
409
+ const char *enc
410
+ = RB_NIL_OR_UNDEF_P (encoding) ? NULL : rb_string_value_cstr (&encoding);
411
+ const char *sep
412
+ = RB_NIL_OR_UNDEF_P (nssep) ? NULL : rb_string_value_cstr (&nssep);
413
+
414
+ struct rbe_parser *parser = rb_check_typeddata (self, &rbx_cParser_type);
415
+ *parser = (struct rbe_parser){
416
+ .parser
417
+ = sep == NULL ? XML_ParserCreate (enc) : XML_ParserCreateNS (enc, sep[0]),
418
+ .parent = RUBY_Qnil,
419
+ };
420
+ if (!parser->parser)
421
+ rb_raise (rbe_eError, "cannot create parser");
422
+
423
+ XML_SetUserData (parser->parser, (void *)self);
424
+ return self;
425
+ }
426
+
427
+ static VALUE
428
+ rbe_cParser_m_external_entity_parser (int argc, VALUE *argv, VALUE self)
429
+ {
430
+ struct rbe_parser *parser = rb_check_typeddata (self, &rbx_cParser_type);
431
+
432
+ VALUE context, kwargs;
433
+ rb_scan_args (argc, argv, "1:", &context, &kwargs);
434
+
435
+ const char *ctx = rb_string_value_cstr (&context);
436
+
437
+ ID kwargs_ids[] = { rb_intern ("encoding") };
438
+ VALUE kwargs_vals[sizeof (kwargs_ids) / sizeof (kwargs_ids[0])] = { 0 };
439
+ rb_get_kwargs (kwargs, kwargs_ids, 0, 1, kwargs_vals);
440
+ VALUE encoding = kwargs_vals[0];
441
+ const char *enc
442
+ = RB_NIL_OR_UNDEF_P (encoding) ? NULL : rb_string_value_cstr (&encoding);
443
+
444
+ VALUE eparser = rb_obj_alloc (rbe_cParser);
445
+ struct rbe_parser *ep = rb_check_typeddata (eparser, &rbx_cParser_type);
446
+ *ep = (struct rbe_parser){ .parser = XML_ExternalEntityParserCreate (
447
+ parser->parser, ctx, enc),
448
+ .parent = self };
449
+ if (!ep->parser)
450
+ rb_raise (rbe_eError, "cannot create external entity parser");
451
+ XML_SetUserData (ep->parser, (void *)eparser);
452
+ return eparser;
453
+ }
454
+
455
+ static VALUE
456
+ rbe_cParser_m_reset (int argc, VALUE *argv, VALUE obj)
457
+ {
458
+ struct rbe_parser *parser = rb_check_typeddata (obj, &rbx_cParser_type);
459
+
460
+ VALUE kwargs;
461
+ rb_scan_args (argc, argv, ":", &kwargs);
462
+ ID kwargs_ids[] = { rb_intern ("encoding") };
463
+ VALUE kwargs_vals[sizeof (kwargs_ids) / sizeof (kwargs_ids[0])] = { 0 };
464
+ rb_get_kwargs (kwargs, kwargs_ids, 0, 1, kwargs_vals);
465
+ VALUE encoding_obj = kwargs_vals[0];
466
+ const char *encoding = RB_NIL_OR_UNDEF_P (encoding_obj)
467
+ ? NULL
468
+ : rb_string_value_cstr (&encoding_obj);
469
+
470
+ XML_ParserReset (parser->parser, encoding);
471
+
472
+ XML_SetUserData (parser->parser, (void *)obj);
473
+ parser->default_current = false;
474
+ parser->last_attrs = NULL;
475
+
476
+ return obj;
477
+ }
478
+
479
+ static void
480
+ setup_event_handlers (struct rbe_parser *parser, VALUE obj)
481
+ {
482
+ XML_StartElementHandler start = NULL;
483
+ XML_EndElementHandler end = NULL;
484
+ XML_StartCdataSectionHandler start_c = NULL;
485
+ XML_EndCdataSectionHandler end_c = NULL;
486
+ XML_StartNamespaceDeclHandler start_ns = NULL;
487
+ XML_EndNamespaceDeclHandler end_ns = NULL;
488
+ XML_StartDoctypeDeclHandler start_doctype = NULL;
489
+ XML_EndDoctypeDeclHandler end_doctype = NULL;
490
+
491
+ if (rb_respond_to (obj, id_on_start_element))
492
+ start = on_start_element;
493
+ if (rb_respond_to (obj, id_on_end_element))
494
+ end = end_element_handler;
495
+ if (start || end)
496
+ XML_SetElementHandler (parser->parser, start, end);
497
+ if (rb_respond_to (obj, id_on_character_data))
498
+ XML_SetCharacterDataHandler (parser->parser, character_data_handler);
499
+ if (rb_respond_to (obj, id_on_processing_instruction))
500
+ XML_SetProcessingInstructionHandler (parser->parser,
501
+ on_processing_instruction);
502
+ if (rb_respond_to (obj, id_on_default_expand))
503
+ XML_SetDefaultHandlerExpand (parser->parser, default_expand_handler);
504
+ else if (rb_respond_to (obj, id_on_default))
505
+ XML_SetDefaultHandler (parser->parser, default_handler);
506
+ if (rb_respond_to (obj, id_on_notation_decl))
507
+ XML_SetNotationDeclHandler (parser->parser, on_notation_decl);
508
+ if (rb_respond_to (obj, id_on_external_entity_ref))
509
+ XML_SetExternalEntityRefHandler (parser->parser, on_external_entity_ref);
510
+ if (rb_respond_to (obj, id_on_comment))
511
+ XML_SetCommentHandler (parser->parser, comment_handler);
512
+
513
+ if (rb_respond_to (obj, id_on_start_cdata_section))
514
+ start_c = start_cdata_section_handler;
515
+ if (rb_respond_to (obj, id_on_end_cdata_section))
516
+ end_c = end_cdata_section_handler;
517
+ if (start_c || end_c)
518
+ XML_SetCdataSectionHandler (parser->parser, start_c, end_c);
519
+
520
+ if (rb_respond_to (obj, id_on_start_namespace_decl))
521
+ start_ns = start_namespace_decl_handler;
522
+ if (rb_respond_to (obj, id_on_end_namespace_decl))
523
+ end_ns = end_namespace_decl_handler;
524
+ if (start_ns || end_ns)
525
+ XML_SetNamespaceDeclHandler (parser->parser, start_ns, end_ns);
526
+ if (rb_respond_to (obj, id_on_not_standalone))
527
+ XML_SetNotStandaloneHandler (parser->parser, not_standalone_handler);
528
+ if (rb_respond_to (obj, id_on_start_doctype_decl))
529
+ start_doctype = on_start_doctype_decl;
530
+ if (rb_respond_to (obj, id_on_end_doctype_decl))
531
+ end_doctype = end_doctype_decl_handler;
532
+ if (start_doctype || end_doctype)
533
+ XML_SetDoctypeDeclHandler (parser->parser, start_doctype, end_doctype);
534
+ if (rb_respond_to (obj, id_on_element_decl))
535
+ XML_SetElementDeclHandler (parser->parser, element_decl_handler);
536
+ if (rb_respond_to (obj, id_on_attlist_decl))
537
+ XML_SetAttlistDeclHandler (parser->parser, attlist_decl_handler);
538
+ if (rb_respond_to (obj, id_on_xml_decl))
539
+ XML_SetXmlDeclHandler (parser->parser, xml_decl_handler);
540
+ if (rb_respond_to (obj, id_on_entity_decl))
541
+ XML_SetEntityDeclHandler (parser->parser, on_entity_decl);
542
+ if (rb_respond_to (obj, id_on_skipped_entity))
543
+ XML_SetSkippedEntityHandler (parser->parser, on_skipped_entity);
544
+ }
545
+
546
+ static VALUE
547
+ rbe_cParser_m_parse (int argc, VALUE *argv, VALUE obj)
548
+ {
549
+ enum XML_Status ret;
550
+
551
+ struct rbe_parser *parser = rb_check_typeddata (obj, &rbx_cParser_type);
552
+
553
+ VALUE str, kwargs;
554
+ rb_scan_args (argc, argv, "01:", &str, &kwargs);
555
+ ID kwargs_ids[] = { rb_intern ("final") };
556
+ VALUE kwargs_vals[sizeof (kwargs_ids) / sizeof (kwargs_ids[0])] = { 0 };
557
+ rb_get_kwargs (kwargs, kwargs_ids, 0, 1, kwargs_vals);
558
+ VALUE final_obj = kwargs_vals[0];
559
+ int final = RB_UNDEF_P (final_obj) ? RUBY_Qtrue : RB_TEST (final_obj);
560
+
561
+ setup_event_handlers (parser, obj);
562
+
563
+ if (!NIL_P (str))
564
+ {
565
+ ret = XML_Parse (parser->parser, RSTRING_PTR (str),
566
+ (int)RSTRING_LEN (str), final);
567
+ enum XML_Error err = XML_GetErrorCode (parser->parser);
568
+ if (final && err == XML_ERROR_UNKNOWN_ENCODING)
569
+ rb_raise (rbe_eError, "unknown encoding");
570
+ }
571
+ else
572
+ ret = XML_Parse (parser->parser, NULL, 0, final);
573
+
574
+ switch (ret)
575
+ {
576
+ case XML_STATUS_ERROR:
577
+ {
578
+ enum XML_Error err = XML_GetErrorCode (parser->parser);
579
+ const XML_LChar *message = XML_ErrorString (err);
580
+ rb_raise (rbe_eError, "fatal parsing error: %s", message);
581
+ return RUBY_Qnil;
582
+ }
583
+ case XML_STATUS_OK:
584
+ return RUBY_Qnil;
585
+
586
+ case XML_STATUS_SUSPENDED:
587
+ rb_raise (rbe_eError, "unexpected error");
588
+ return RUBY_Qnil;
589
+ }
590
+ }
591
+
592
+ static VALUE
593
+ rbe_cParser_m_default_current (VALUE obj)
594
+ {
595
+ struct rbe_parser *parser = rb_check_typeddata (obj, &rbx_cParser_type);
596
+ XML_DefaultCurrent (parser->parser);
597
+ return Qnil;
598
+ }
599
+
600
+ static VALUE
601
+ rbe_cParser_m_line (VALUE obj)
602
+ {
603
+ struct rbe_parser *parser = rb_check_typeddata (obj, &rbx_cParser_type);
604
+ XML_Size line = XML_GetCurrentLineNumber (parser->parser);
605
+ return rb_ulong2num_inline (line);
606
+ }
607
+
608
+ static VALUE
609
+ rbe_cParser_m_column (VALUE obj)
610
+ {
611
+ struct rbe_parser *parser = rb_check_typeddata (obj, &rbx_cParser_type);
612
+ XML_Size column = XML_GetCurrentColumnNumber (parser->parser);
613
+ return rb_ulong2num_inline (column);
614
+ }
615
+
616
+ static VALUE
617
+ rbe_cParser_m_byte_index (VALUE obj)
618
+ {
619
+ struct rbe_parser *parser = rb_check_typeddata (obj, &rbx_cParser_type);
620
+ XML_Index pos = XML_GetCurrentByteIndex (parser->parser);
621
+ return rb_long2num_inline (pos);
622
+ }
623
+
624
+ static VALUE
625
+ rbe_cParser_m_set_base (VALUE obj, VALUE base)
626
+ {
627
+ struct rbe_parser *parser = rb_check_typeddata (obj, &rbx_cParser_type);
628
+ enum XML_Status status
629
+ = XML_SetBase (parser->parser, rb_string_value_cstr (&base));
630
+ switch (status)
631
+ {
632
+ case XML_STATUS_ERROR:
633
+ rb_raise (rbe_eError, "out of memory");
634
+ return RUBY_Qnil;
635
+ case XML_STATUS_OK:
636
+ return RUBY_Qnil;
637
+ case XML_STATUS_SUSPENDED:
638
+ rb_raise (rbe_eError, "unexpected suspended error");
639
+ return RUBY_Qnil;
640
+ }
641
+ }
642
+
643
+ static VALUE
644
+ rbe_cParser_getBase (VALUE obj)
645
+ {
646
+ const XML_Char *ret;
647
+
648
+ struct rbe_parser *parser = rb_check_typeddata (obj, &rbx_cParser_type);
649
+ ret = XML_GetBase (parser->parser);
650
+ if (!ret)
651
+ return Qnil;
652
+
653
+ return rb_utf8_str_new_cstr (ret);
654
+ }
655
+
656
+ static VALUE
657
+ rbe_cParser_m_specified_attributes (VALUE obj)
658
+ {
659
+ const XML_Char **atts;
660
+ VALUE attrarray;
661
+
662
+ struct rbe_parser *parser = rb_check_typeddata (obj, &rbx_cParser_type);
663
+ atts = parser->last_attrs;
664
+ if (!atts)
665
+ return Qnil;
666
+ const int count = XML_GetSpecifiedAttributeCount (parser->parser);
667
+ attrarray = rb_ary_new_capa (count);
668
+ for (int index = 0; index < count; index += 2)
669
+ rb_ary_push (attrarray, rb_utf8_str_new_cstr (*(atts + index)));
670
+
671
+ return attrarray;
672
+ }
673
+
674
+ static VALUE
675
+ rbe_cParser_m_byte_count (VALUE obj)
676
+ {
677
+ struct rbe_parser *parser = rb_check_typeddata (obj, &rbx_cParser_type);
678
+ return rb_int2num_inline (XML_GetCurrentByteCount (parser->parser));
679
+ }
680
+
681
+ static VALUE
682
+ rbe_cParser_m_set_param_entity_parsing (VALUE obj, VALUE parsing)
683
+ {
684
+ struct rbe_parser *parser = rb_check_typeddata (obj, &rbx_cParser_type);
685
+
686
+ ID id = RB_SYM2ID (parsing);
687
+ enum XML_ParamEntityParsing p
688
+ = rb_intern ("never") == id ? XML_PARAM_ENTITY_PARSING_NEVER
689
+ : rb_intern ("unless_standalone") == id
690
+ ? XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE
691
+ : rb_intern ("always") == id
692
+ ? XML_PARAM_ENTITY_PARSING_ALWAYS
693
+ : (rb_raise (rb_eArgError, "unexpected parsing value"),
694
+ 0 /* dummy */);
695
+ if (XML_SetParamEntityParsing (parser->parser, p) == 0)
696
+ rb_raise (rbe_eError, "failed or no effect to set parsing control");
697
+ return RUBY_Qnil;
698
+ }
699
+
700
+ static VALUE
701
+ rbe_s_expat_version (VALUE obj)
702
+ {
703
+ (void)obj;
704
+ return rb_utf8_str_new_cstr (XML_ExpatVersion ());
705
+ }
706
+
707
+ static VALUE
708
+ rbe_cParser_m_set_return_ns_triplet (VALUE obj, VALUE do_nst)
709
+ {
710
+ int nst;
711
+
712
+ struct rbe_parser *parser = rb_check_typeddata (obj, &rbx_cParser_type);
713
+ switch (TYPE (do_nst))
714
+ {
715
+ case T_TRUE:
716
+ nst = 1;
717
+ break;
718
+ case T_FALSE:
719
+ nst = 0;
720
+ break;
721
+ default:
722
+ rb_raise (rb_eTypeError, "not valid value");
723
+ }
724
+ XML_SetReturnNSTriplet (parser->parser, nst);
725
+
726
+ return Qnil;
727
+ }
728
+
729
+ static VALUE
730
+ rbe_cParser_m_input_context (VALUE obj)
731
+ {
732
+ const char *buffer;
733
+ int offset;
734
+ int size;
735
+
736
+ struct rbe_parser *parser = rb_check_typeddata (obj, &rbx_cParser_type);
737
+ buffer = XML_GetInputContext (parser->parser, &offset, &size);
738
+ return buffer && size > 0
739
+ ? rb_ary_new_from_args (2, rb_utf8_str_new (buffer, size),
740
+ rb_int2num_inline (offset))
741
+ : Qnil;
742
+ }
743
+
744
+ static VALUE
745
+ rbe_cParser_m_id_attribute (VALUE obj)
746
+ {
747
+ int idattr;
748
+ const XML_Char **atts;
749
+
750
+ struct rbe_parser *parser = rb_check_typeddata (obj, &rbx_cParser_type);
751
+ atts = parser->last_attrs;
752
+ if (!atts)
753
+ return Qnil;
754
+ idattr = XML_GetIdAttributeIndex (parser->parser);
755
+ if (idattr < 0)
756
+ return Qnil;
757
+ return rb_utf8_str_new_cstr (atts[idattr]);
758
+ }
759
+
760
+ static VALUE
761
+ rbe_cParser_m_use_foreign_dtd (VALUE obj, VALUE useDTD)
762
+ {
763
+ struct rbe_parser *parser = rb_check_typeddata (obj, &rbx_cParser_type);
764
+ enum XML_Error ret = XML_UseForeignDTD (parser->parser, RB_TEST (useDTD));
765
+ switch (ret)
766
+ {
767
+ case XML_ERROR_NONE:
768
+ return RUBY_Qnil;
769
+ case XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING:
770
+ rb_raise (rbe_eError, "cannot change feature once parsing");
771
+ return RUBY_Qnil;
772
+ case XML_ERROR_FEATURE_REQUIRES_XML_DTD:
773
+ rb_raise (rbe_eError, "this feature requires XML DTD; XML_DTD was not "
774
+ "enabled when compiling Expat");
775
+ return RUBY_Qnil;
776
+ case XML_ERROR_INVALID_ARGUMENT:
777
+ rb_raise (rbe_eError, "parser is empty");
778
+ return RUBY_Qnil;
779
+ default:
780
+ rb_raise (rbe_eError, "unexpected error");
781
+ return RUBY_Qnil;
782
+ }
783
+ }
784
+
785
+ static VALUE
786
+ rbe_s_features (VALUE obj)
787
+ {
788
+ (void)obj;
789
+ const XML_Feature *list;
790
+ VALUE ret = rb_hash_new ();
791
+
792
+ list = XML_GetFeatureList ();
793
+ while (list && list->feature)
794
+ {
795
+ rb_hash_aset (ret, freeze_object (rb_utf8_str_new_cstr (list->name)),
796
+ INT2NUM (list->value));
797
+ list++;
798
+ }
799
+
800
+ return ret;
801
+ }
802
+
803
+ void
804
+ Init_rexpat ()
805
+ {
806
+ rbe_cParser = rb_define_class ("RExpat", rb_cObject);
807
+ rb_define_alloc_func (rbe_cParser, rbx_cParser_alloc);
808
+
809
+ rbe_eError = rb_define_class_under (rbe_cParser, "Error", rb_eStandardError);
810
+
811
+ rb_define_singleton_method (rbe_cParser, "expat_version",
812
+ rbe_s_expat_version, 0);
813
+ rb_define_method (rbe_cParser, "initialize", rbx_cParser_m_initialize, -1);
814
+ rb_define_method (rbe_cParser, "external_entity_parser",
815
+ rbe_cParser_m_external_entity_parser, -1);
816
+ rb_define_method (rbe_cParser, "parse", rbe_cParser_m_parse, -1);
817
+ rb_define_method (rbe_cParser, "default_current",
818
+ rbe_cParser_m_default_current, 0);
819
+ rb_define_method (rbe_cParser, "line", rbe_cParser_m_line, 0);
820
+ rb_define_method (rbe_cParser, "column", rbe_cParser_m_column, 0);
821
+ rb_define_method (rbe_cParser, "byte_index", rbe_cParser_m_byte_index, 0);
822
+ rb_define_method (rbe_cParser, "base=", rbe_cParser_m_set_base, 1);
823
+ rb_define_method (rbe_cParser, "getBase", rbe_cParser_getBase, 0);
824
+ rb_define_method (rbe_cParser, "specified_attributes",
825
+ rbe_cParser_m_specified_attributes, 0);
826
+ rb_define_method (rbe_cParser, "byte_count", rbe_cParser_m_byte_count, 0);
827
+ rb_define_method (rbe_cParser, "param_entity_parsing=",
828
+ rbe_cParser_m_set_param_entity_parsing, 1);
829
+ rb_define_method (rbe_cParser,
830
+ "return_ns_triplet=", rbe_cParser_m_set_return_ns_triplet,
831
+ 1);
832
+ rb_define_method (rbe_cParser, "input_context", rbe_cParser_m_input_context,
833
+ 0);
834
+ rb_define_method (rbe_cParser, "id_attribute", rbe_cParser_m_id_attribute,
835
+ 0);
836
+
837
+ rb_define_method (rbe_cParser, "reset", rbe_cParser_m_reset, -1);
838
+
839
+ rb_define_method (rbe_cParser,
840
+ "use_foreign_dtd=", rbe_cParser_m_use_foreign_dtd, 1);
841
+ rb_define_singleton_method (rbe_cParser, "features", rbe_s_features, 0);
842
+
843
+ id_on_start_element = rb_intern ("on_start_element");
844
+ id_on_end_element = rb_intern ("on_end_element");
845
+ id_on_character_data = rb_intern ("on_character_data");
846
+ id_on_processing_instruction = rb_intern ("on_processing_instruction");
847
+ id_on_default = rb_intern ("on_default");
848
+ id_on_notation_decl = rb_intern ("on_notation_decl");
849
+ id_on_external_entity_ref = rb_intern ("on_external_entity_ref");
850
+ id_on_default_expand = rb_intern ("on_default_expand");
851
+ id_on_comment = rb_intern ("on_comment");
852
+ id_on_start_cdata_section = rb_intern ("on_start_cdata_section");
853
+ id_on_end_cdata_section = rb_intern ("on_end_cdata_section");
854
+ id_on_start_namespace_decl = rb_intern ("on_start_namespace_decl");
855
+ id_on_end_namespace_decl = rb_intern ("on_end_namespace_decl");
856
+ id_on_not_standalone = rb_intern ("on_not_standalone");
857
+ id_on_start_doctype_decl = rb_intern ("on_start_doctype_decl");
858
+ id_on_end_doctype_decl = rb_intern ("on_end_doctype_decl");
859
+ id_on_element_decl = rb_intern ("on_element_decl");
860
+ id_on_attlist_decl = rb_intern ("on_attlist_decl");
861
+ id_on_xml_decl = rb_intern ("on_xml_decl");
862
+ id_on_entity_decl = rb_intern ("on_entity_decl");
863
+ id_on_skipped_entity = rb_intern ("on_skipped_entity");
864
+ }