apex-ruby 1.0.34 → 1.0.36

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 55f4be7471b1a5d14cc4f772e5a92fed588dfa22f746d2ebc8417fb9b13758d7
4
- data.tar.gz: 27aa2109e8bdad8f123f5cdfd6f7dee7a56947f4f47a06d653ca6fde9adf558c
3
+ metadata.gz: f0e8b147ca76e15cc47b7c82389cd32c925aabdb84580155ca98ea9cadaa4e94
4
+ data.tar.gz: 7b6c52d126e87e1d7adbd22a5eb4c0371617b056b6a8a8b019edea8f78328ee2
5
5
  SHA512:
6
- metadata.gz: e3d8fb224ae6eaebe856a9651319c806e3031d3720a7261c264063f8f0777dd522a8547175dfdd393bb83aafb1f5a7ad8b7ee7a49d18cdae9124f279051e7bf9
7
- data.tar.gz: a60e981f839d51dae0b93e4e088ba43fe575800b951e85d97b35379af434a93ef968d84ebcab74e137dd916023682ff06b1023c4a14574a0224a720490054c88
6
+ metadata.gz: a24d7c5056504ad6557c259f62f4f7aa7f83f4214675793415587ad040affe6095a830a60a65387b76d0d3138ebfc659a100406dda633f4f426fab67d1d0043b
7
+ data.tar.gz: 3e9a435b4f1d0e3539d90ed2448b646843d7b9a8b300baa032c72dc5ba12d1d9e4f881ac588e50102df688e51d7f994cdb0bfc88f355f0149baed824e8bb82f3
data/README.md CHANGED
@@ -6,7 +6,15 @@ Apex is a C library that supports CommonMark, GFM, MultiMarkdown, Kramdown, and
6
6
  Marked-specific extensions. This gem vendors the Apex sources (currently the **1.1.x** library)
7
7
  and exposes a small, kramdown-style Ruby API for converting Markdown to HTML.
8
8
 
9
- Query the underlying C library version with `Apex::Native.version`.
9
+ The gem has its own version number, separate from the Apex library it bundles. Each gem
10
+ release ships the latest Apex release, and `Apex::Native.version` reports which one you have:
11
+
12
+ ```ruby
13
+ require "apex"
14
+
15
+ Apex::VERSION # => "1.0.34" (the gem)
16
+ Apex::Native.version # => "1.1.36" (the bundled Apex library)
17
+ ```
10
18
 
11
19
  ## Requirements
12
20
 
@@ -21,8 +21,8 @@ typedef struct cmark_node cmark_node;
21
21
 
22
22
  #define APEX_VERSION_MAJOR 1
23
23
  #define APEX_VERSION_MINOR 1
24
- #define APEX_VERSION_PATCH 36
25
- #define APEX_VERSION_STRING "1.1.36"
24
+ #define APEX_VERSION_PATCH 38
25
+ #define APEX_VERSION_STRING "1.1.38"
26
26
 
27
27
  /**
28
28
  * Processor compatibility modes
@@ -364,6 +364,32 @@ static header_item *collect_headers(cmark_node *node, header_item **tail,
364
364
  return headers;
365
365
  }
366
366
 
367
+ typedef struct {
368
+ char *data;
369
+ size_t length;
370
+ size_t capacity;
371
+ bool failed;
372
+ } toc_buffer;
373
+
374
+ static void toc_buffer_append(toc_buffer *buf, const char *str) {
375
+ if (buf->failed || !str) return;
376
+ size_t len = strlen(str);
377
+ if (buf->length + len + 1 > buf->capacity) {
378
+ size_t capacity = buf->capacity ? buf->capacity : 4096;
379
+ while (buf->length + len + 1 > capacity) capacity *= 2;
380
+ char *grown = realloc(buf->data, capacity);
381
+ if (!grown) {
382
+ buf->failed = true;
383
+ return;
384
+ }
385
+ buf->data = grown;
386
+ buf->capacity = capacity;
387
+ }
388
+ memcpy(buf->data + buf->length, str, len);
389
+ buf->length += len;
390
+ buf->data[buf->length] = '\0';
391
+ }
392
+
367
393
  /**
368
394
  * Generate TOC HTML from headers
369
395
  * Produces valid ul > li > ul nesting (nested lists inside list items)
@@ -371,81 +397,64 @@ static header_item *collect_headers(cmark_node *node, header_item **tail,
371
397
  static char *generate_toc_html(header_item *headers, int min_level, int max_level) {
372
398
  if (!headers) return strdup("");
373
399
 
374
- size_t capacity = 4096;
375
- char *html = malloc(capacity);
376
- if (!html) return strdup("");
400
+ toc_buffer buf = {0};
401
+ /* depth: open <ul> elements. level_at[d] is the shallowest heading level
402
+ * listed at depth d. A deeper heading nests one level under the open <li>,
403
+ * however many heading levels it skips; headings at that same level stay
404
+ * siblings. */
405
+ int depth = 0;
406
+ int level_at[8] = {0};
407
+ bool li_open = false;
377
408
 
378
- char *write = html;
379
- size_t remaining = capacity;
380
- int current_level = 0;
381
- int last_level = 0; /* normalized level of last item added (0 = none yet) */
382
-
383
- #define APPEND(str) do { \
384
- size_t len = strlen(str); \
385
- if (len < remaining) { \
386
- memcpy(write, str, len); \
387
- write += len; \
388
- remaining -= len; \
389
- } \
390
- } while(0)
391
-
392
- APPEND("<nav class=\"toc\">\n");
409
+ toc_buffer_append(&buf, "<nav class=\"toc\">\n");
393
410
 
394
411
  for (header_item *h = headers; h; h = h->next) {
395
- /* Skip headers outside min/max range */
396
412
  if (h->level < min_level || h->level > max_level) continue;
397
413
  int target_level = h->level - min_level + 1;
398
414
  if (target_level < 1) target_level = 1;
399
415
 
400
- /* Going up: close </ul></li> for each level (nested ul inside parent li) */
401
- while (current_level > target_level) {
402
- APPEND("</ul>\n</li>\n");
403
- current_level--;
404
- }
405
-
406
- /* Going down: open one <ul> inside the previous li before adding child.
407
- * At root (current_level 0): only open one ul - never ul > ul.
408
- * When going deeper: open exactly one ul per step - never ul > ul. */
409
- while (current_level < target_level) {
410
- if (current_level == 0) {
411
- APPEND("<ul>\n");
412
- current_level = 1;
413
- break;
414
- }
415
- if (last_level > 0) {
416
- APPEND("<ul>\n");
417
- current_level++;
418
- break;
419
- } else {
420
- break; /* No parent li - add as direct child of root ul */
416
+ if (depth == 0) {
417
+ toc_buffer_append(&buf, "<ul>\n");
418
+ depth = 1;
419
+ level_at[depth] = target_level;
420
+ } else if (target_level > level_at[depth] && li_open && depth < 7) {
421
+ toc_buffer_append(&buf, "<ul>\n");
422
+ depth++;
423
+ level_at[depth] = target_level;
424
+ li_open = false;
425
+ } else {
426
+ while (depth > 1 && target_level <= level_at[depth - 1]) {
427
+ if (li_open) toc_buffer_append(&buf, "</li>\n");
428
+ toc_buffer_append(&buf, "</ul>\n");
429
+ depth--;
430
+ li_open = true;
421
431
  }
432
+ if (li_open) toc_buffer_append(&buf, "</li>\n");
433
+ if (target_level < level_at[depth]) level_at[depth] = target_level;
422
434
  }
423
435
 
424
- /* Close previous li when adding sibling (same or shallower level) */
425
- if (last_level > 0 && target_level <= last_level) {
426
- APPEND("</li>\n");
427
- }
428
-
429
- /* Add list item (leave open - may contain nested ul) */
430
- char item[1024];
431
- snprintf(item, sizeof(item), "<li><a href=\"#%s\">%s</a>",
432
- h->id, h->text);
433
- APPEND(item);
434
- last_level = target_level;
436
+ toc_buffer_append(&buf, "<li><a href=\"#");
437
+ toc_buffer_append(&buf, h->id);
438
+ toc_buffer_append(&buf, "\">");
439
+ toc_buffer_append(&buf, h->text);
440
+ toc_buffer_append(&buf, "</a>");
441
+ li_open = true;
435
442
  }
436
443
 
437
- /* Close remaining: </li></ul> for each open level */
438
- while (current_level > 0) {
439
- APPEND("</li>\n</ul>\n");
440
- current_level--;
444
+ while (depth > 0) {
445
+ if (li_open) toc_buffer_append(&buf, "</li>\n");
446
+ toc_buffer_append(&buf, "</ul>\n");
447
+ depth--;
448
+ li_open = depth > 0;
441
449
  }
442
450
 
443
- APPEND("</nav>\n");
451
+ toc_buffer_append(&buf, "</nav>\n");
444
452
 
445
- #undef APPEND
446
-
447
- *write = '\0';
448
- return html;
453
+ if (buf.failed || !buf.data) {
454
+ free(buf.data);
455
+ return strdup("");
456
+ }
457
+ return buf.data;
449
458
  }
450
459
 
451
460
  void apex_toc_entries_free(apex_toc_entry *entries, size_t count) {
@@ -2473,6 +2473,41 @@ char *apex_adjust_quote_language(const char *html, const char *quotes_language)
2473
2473
  return output;
2474
2474
  }
2475
2475
 
2476
+ typedef struct {
2477
+ char *data;
2478
+ size_t len;
2479
+ size_t cap;
2480
+ } aria_buf;
2481
+
2482
+ static bool aria_buf_append(aria_buf *b, const char *s, size_t n) {
2483
+ if (b->len + n + 1 > b->cap) {
2484
+ size_t new_cap = b->cap ? b->cap : 64;
2485
+ while (b->len + n + 1 > new_cap) new_cap *= 2;
2486
+ char *grown = realloc(b->data, new_cap);
2487
+ if (!grown) return false;
2488
+ b->data = grown;
2489
+ b->cap = new_cap;
2490
+ }
2491
+ memcpy(b->data + b->len, s, n);
2492
+ b->len += n;
2493
+ b->data[b->len] = '\0';
2494
+ return true;
2495
+ }
2496
+
2497
+ /* strstr limited to [start, end) */
2498
+ static const char *aria_find_in(const char *start, const char *end, const char *needle) {
2499
+ size_t n = strlen(needle);
2500
+ for (const char *p = start; p && p + n <= end; p++) {
2501
+ if (*p == *needle && memcmp(p, needle, n) == 0) return p;
2502
+ }
2503
+ return NULL;
2504
+ }
2505
+
2506
+ static bool aria_has_table_figure_class(const char *start, const char *end) {
2507
+ return aria_find_in(start, end, "class=\"table-figure\"") ||
2508
+ aria_find_in(start, end, "class='table-figure'");
2509
+ }
2510
+
2476
2511
  /**
2477
2512
  * Apply ARIA labels and accessibility attributes to HTML output
2478
2513
  * @param html The HTML output
@@ -2512,11 +2547,7 @@ char *apex_apply_aria_labels(const char *html, cmark_node *document) {
2512
2547
  const char *figure_start_pos = NULL;
2513
2548
  while (before_cap >= html && before_cap > search - 200) {
2514
2549
  if (*before_cap == '<' && strncmp(before_cap, "<figure", 7) == 0) {
2515
- const char *class_check = strstr(before_cap, "class=\"table-figure\"");
2516
- if (!class_check) {
2517
- class_check = strstr(before_cap, "class='table-figure'");
2518
- }
2519
- if (class_check && class_check < cap_tag_start) {
2550
+ if (aria_has_table_figure_class(before_cap, cap_tag_start)) {
2520
2551
  in_table_figure = true;
2521
2552
  figure_start_pos = before_cap;
2522
2553
  break;
@@ -2527,13 +2558,13 @@ char *apex_apply_aria_labels(const char *html, cmark_node *document) {
2527
2558
 
2528
2559
  if (in_table_figure) {
2529
2560
  /* Check if ID already exists */
2530
- const char *id_attr = strstr(cap_tag_start, "id=\"");
2561
+ const char *id_attr = aria_find_in(cap_tag_start, cap_tag_end, "id=\"");
2531
2562
  if (!id_attr) {
2532
- id_attr = strstr(cap_tag_start, "id='");
2563
+ id_attr = aria_find_in(cap_tag_start, cap_tag_end, "id='");
2533
2564
  }
2534
2565
 
2535
2566
  char *caption_id = NULL;
2536
- if (id_attr && id_attr < cap_tag_end) {
2567
+ if (id_attr) {
2537
2568
  /* Extract existing ID */
2538
2569
  const char *id_start = id_attr + 4;
2539
2570
  const char *id_end = strchr(id_start, '"');
@@ -2573,41 +2604,26 @@ char *apex_apply_aria_labels(const char *html, cmark_node *document) {
2573
2604
  search++;
2574
2605
  }
2575
2606
 
2576
- /* Allocate buffer with extra space for ARIA attributes */
2577
- size_t capacity = html_len + 2048 + (caption_list ? strlen(caption_list->caption_id) * 10 : 0);
2578
- char *output = malloc(capacity + 1);
2579
- if (!output) {
2580
- /* Free caption list */
2581
- while (caption_list) {
2582
- caption_info *next = caption_list->next;
2583
- free(caption_list->caption_id);
2584
- free(caption_list);
2585
- caption_list = next;
2586
- }
2587
- return strdup(html);
2588
- }
2607
+ aria_buf out = {0};
2608
+ out.cap = html_len + html_len / 8 + 256;
2609
+ out.data = malloc(out.cap);
2610
+ if (!out.data) goto oom;
2611
+ out.data[0] = '\0';
2589
2612
 
2590
2613
  const char *read = html;
2591
- char *write = output;
2592
- size_t remaining = capacity;
2593
2614
 
2594
- /* Helper macro to append strings safely */
2615
+ /* Arguments are evaluated before appending so the read position always advances */
2616
+ #define APPEND_N(s, n) do { \
2617
+ if (!aria_buf_append(&out, (s), (n))) goto oom; \
2618
+ } while (0)
2595
2619
  #define APPEND_SAFE(str) do { \
2596
- size_t len = strlen(str); \
2597
- if (len <= remaining) { \
2598
- memcpy(write, str, len); \
2599
- write += len; \
2600
- remaining -= len; \
2601
- } \
2602
- } while(0)
2603
-
2604
- /* Helper macro to copy characters safely */
2620
+ const char *str_ = (str); \
2621
+ APPEND_N(str_, strlen(str_)); \
2622
+ } while (0)
2605
2623
  #define COPY_CHAR(c) do { \
2606
- if (remaining > 0) { \
2607
- *write++ = (c); \
2608
- remaining--; \
2609
- } \
2610
- } while(0)
2624
+ char c_ = (c); \
2625
+ APPEND_N(&c_, 1); \
2626
+ } while (0)
2611
2627
 
2612
2628
  while (*read) {
2613
2629
  /* Check for <nav class="toc"> */
@@ -2620,29 +2636,16 @@ char *apex_apply_aria_labels(const char *html, cmark_node *document) {
2620
2636
  }
2621
2637
 
2622
2638
  /* Check if this is a TOC nav element */
2623
- const char *class_attr = strstr(tag_start, "class=\"toc\"");
2624
- if (!class_attr) {
2625
- class_attr = strstr(tag_start, "class='toc'");
2626
- }
2627
-
2628
- if (class_attr && class_attr < tag_end) {
2629
- /* Check if aria-label already exists */
2630
- const char *aria_label = strstr(tag_start, "aria-label=");
2631
- if (!aria_label || aria_label > tag_end) {
2632
- /* Copy up to just before closing >, add aria-label, then close */
2633
- size_t prefix_len = tag_end - tag_start;
2634
- if (prefix_len <= remaining) {
2635
- memcpy(write, tag_start, prefix_len);
2636
- write += prefix_len;
2637
- remaining -= prefix_len;
2638
- }
2639
+ bool is_toc = aria_find_in(tag_start, tag_end, "class=\"toc\"") ||
2640
+ aria_find_in(tag_start, tag_end, "class='toc'");
2639
2641
 
2640
- /* Add aria-label before closing > */
2641
- APPEND_SAFE(" aria-label=\"Table of contents\"");
2642
- COPY_CHAR('>');
2643
- read = tag_end + 1;
2644
- continue;
2645
- }
2642
+ if (is_toc && !aria_find_in(tag_start, tag_end, "aria-label=")) {
2643
+ /* Copy up to just before closing >, add aria-label, then close */
2644
+ APPEND_N(tag_start, (size_t)(tag_end - tag_start));
2645
+ APPEND_SAFE(" aria-label=\"Table of contents\"");
2646
+ COPY_CHAR('>');
2647
+ read = tag_end + 1;
2648
+ continue;
2646
2649
  }
2647
2650
  }
2648
2651
 
@@ -2655,18 +2658,9 @@ char *apex_apply_aria_labels(const char *html, cmark_node *document) {
2655
2658
  continue;
2656
2659
  }
2657
2660
 
2658
- /* Check if role already exists */
2659
- const char *role_attr = strstr(tag_start, "role=");
2660
- if (!role_attr || role_attr > tag_end) {
2661
+ if (!aria_find_in(tag_start, tag_end, "role=")) {
2661
2662
  /* Copy up to just before closing >, add role, then close */
2662
- size_t prefix_len = tag_end - tag_start;
2663
- if (prefix_len <= remaining) {
2664
- memcpy(write, tag_start, prefix_len);
2665
- write += prefix_len;
2666
- remaining -= prefix_len;
2667
- }
2668
-
2669
- /* Add role="figure" before closing > */
2663
+ APPEND_N(tag_start, (size_t)(tag_end - tag_start));
2670
2664
  APPEND_SAFE(" role=\"figure\"");
2671
2665
  COPY_CHAR('>');
2672
2666
  read = tag_end + 1;
@@ -2683,85 +2677,44 @@ char *apex_apply_aria_labels(const char *html, cmark_node *document) {
2683
2677
  continue;
2684
2678
  }
2685
2679
 
2686
- /* Check if role already exists */
2687
- const char *role_attr = strstr(tag_start, "role=");
2688
- bool needs_role = (!role_attr || role_attr > tag_end);
2689
-
2690
- /* Check if aria-describedby already exists */
2691
- const char *aria_desc = strstr(tag_start, "aria-describedby=");
2692
- bool has_aria_desc = (aria_desc && aria_desc < tag_end);
2693
-
2694
- /* Check if we're in a table-figure context and look for figcaption */
2695
- bool in_table_figure = false;
2696
- const char *before_table = read - 1;
2697
- while (before_table >= html && before_table > read - 500) {
2698
- if (*before_table == '<' && strncmp(before_table, "<figure", 7) == 0) {
2699
- const char *class_check = strstr(before_table, "class=\"table-figure\"");
2700
- if (!class_check) {
2701
- class_check = strstr(before_table, "class='table-figure'");
2702
- }
2703
- if (class_check && class_check < tag_start) {
2704
- in_table_figure = true;
2680
+ bool needs_role = !aria_find_in(tag_start, tag_end, "role=");
2681
+ bool has_aria_desc = aria_find_in(tag_start, tag_end, "aria-describedby=") != NULL;
2682
+
2683
+ /* Find the enclosing table-figure, if any */
2684
+ const char *this_figure_start = NULL;
2685
+ const char *find_fig = read - 1;
2686
+ while (find_fig >= html && find_fig > read - 500) {
2687
+ if (*find_fig == '<' && strncmp(find_fig, "<figure", 7) == 0) {
2688
+ if (aria_has_table_figure_class(find_fig, tag_start)) {
2689
+ this_figure_start = find_fig;
2705
2690
  break;
2706
2691
  }
2707
2692
  }
2708
- before_table--;
2693
+ find_fig--;
2709
2694
  }
2710
2695
 
2711
- /* Find figcaption ID for this table by checking caption_list */
2712
- char *caption_id = NULL;
2713
- if (in_table_figure && !has_aria_desc) {
2714
- /* Find the figure_start for this table */
2715
- const char *this_figure_start = NULL;
2716
- const char *find_fig = read - 1;
2717
- while (find_fig >= html && find_fig > read - 500) {
2718
- if (*find_fig == '<' && strncmp(find_fig, "<figure", 7) == 0) {
2719
- const char *class_check = strstr(find_fig, "class=\"table-figure\"");
2720
- if (!class_check) {
2721
- class_check = strstr(find_fig, "class='table-figure'");
2722
- }
2723
- if (class_check && class_check < tag_start) {
2724
- this_figure_start = find_fig;
2725
- break;
2726
- }
2727
- }
2728
- find_fig--;
2729
- }
2730
-
2731
- /* Look for a caption in this figure (either before or after table) */
2732
- if (this_figure_start) {
2733
- for (caption_info *cap = caption_list; cap; cap = cap->next) {
2734
- if (cap->figure_start == this_figure_start) {
2735
- /* Found a caption in the same figure - use it regardless of position */
2736
- caption_id = strdup(cap->caption_id);
2737
- break;
2738
- }
2696
+ /* Look for a caption in this figure (either before or after table) */
2697
+ const char *caption_id = NULL;
2698
+ if (this_figure_start && !has_aria_desc) {
2699
+ for (caption_info *cap = caption_list; cap; cap = cap->next) {
2700
+ if (cap->figure_start == this_figure_start) {
2701
+ caption_id = cap->caption_id;
2702
+ break;
2739
2703
  }
2740
2704
  }
2741
2705
  }
2742
2706
 
2743
2707
  if (needs_role || caption_id) {
2744
2708
  /* Copy up to just before closing >, add attributes, then close */
2745
- size_t prefix_len = tag_end - tag_start;
2746
- if (prefix_len <= remaining) {
2747
- memcpy(write, tag_start, prefix_len);
2748
- write += prefix_len;
2749
- remaining -= prefix_len;
2750
- }
2751
-
2752
- /* Add role="table" if needed */
2709
+ APPEND_N(tag_start, (size_t)(tag_end - tag_start));
2753
2710
  if (needs_role) {
2754
2711
  APPEND_SAFE(" role=\"table\"");
2755
2712
  }
2756
-
2757
- /* Add aria-describedby if we found a caption ID */
2758
2713
  if (caption_id) {
2759
- char aria_desc_str[256];
2760
- snprintf(aria_desc_str, sizeof(aria_desc_str), " aria-describedby=\"%s\"", caption_id);
2761
- APPEND_SAFE(aria_desc_str);
2762
- free(caption_id);
2714
+ APPEND_SAFE(" aria-describedby=\"");
2715
+ APPEND_SAFE(caption_id);
2716
+ COPY_CHAR('"');
2763
2717
  }
2764
-
2765
2718
  COPY_CHAR('>');
2766
2719
  read = tag_end + 1;
2767
2720
  continue;
@@ -2786,30 +2739,17 @@ char *apex_apply_aria_labels(const char *html, cmark_node *document) {
2786
2739
  }
2787
2740
  }
2788
2741
 
2789
- if (this_caption) {
2790
- /* Check if ID already exists in original HTML */
2791
- const char *id_attr = strstr(tag_start, "id=\"");
2792
- if (!id_attr) {
2793
- id_attr = strstr(tag_start, "id='");
2794
- }
2795
-
2796
- if (!id_attr || id_attr > tag_end) {
2797
- /* No ID in original, add the one we generated/collected */
2798
- size_t prefix_len = tag_end - tag_start;
2799
- if (prefix_len <= remaining) {
2800
- memcpy(write, tag_start, prefix_len);
2801
- write += prefix_len;
2802
- remaining -= prefix_len;
2803
- }
2804
-
2805
- /* Add id attribute */
2806
- char id_attr_str[128];
2807
- snprintf(id_attr_str, sizeof(id_attr_str), " id=\"%s\"", this_caption->caption_id);
2808
- APPEND_SAFE(id_attr_str);
2809
- COPY_CHAR('>');
2810
- read = tag_end + 1;
2811
- continue;
2812
- }
2742
+ if (this_caption &&
2743
+ !aria_find_in(tag_start, tag_end, "id=\"") &&
2744
+ !aria_find_in(tag_start, tag_end, "id='")) {
2745
+ /* No ID in original, add the one we generated/collected */
2746
+ APPEND_N(tag_start, (size_t)(tag_end - tag_start));
2747
+ APPEND_SAFE(" id=\"");
2748
+ APPEND_SAFE(this_caption->caption_id);
2749
+ COPY_CHAR('"');
2750
+ COPY_CHAR('>');
2751
+ read = tag_end + 1;
2752
+ continue;
2813
2753
  }
2814
2754
  }
2815
2755
 
@@ -2817,20 +2757,27 @@ char *apex_apply_aria_labels(const char *html, cmark_node *document) {
2817
2757
  COPY_CHAR(*read++);
2818
2758
  }
2819
2759
 
2760
+ #undef APPEND_N
2820
2761
  #undef APPEND_SAFE
2821
2762
  #undef COPY_CHAR
2822
2763
 
2823
- *write = '\0';
2824
-
2825
- /* Free caption list */
2826
2764
  while (caption_list) {
2827
2765
  caption_info *next = caption_list->next;
2828
2766
  free(caption_list->caption_id);
2829
2767
  free(caption_list);
2830
2768
  caption_list = next;
2831
2769
  }
2770
+ return out.data;
2832
2771
 
2833
- return output;
2772
+ oom:
2773
+ free(out.data);
2774
+ while (caption_list) {
2775
+ caption_info *next = caption_list->next;
2776
+ free(caption_list->caption_id);
2777
+ free(caption_list);
2778
+ caption_list = next;
2779
+ }
2780
+ return strdup(html);
2834
2781
  }
2835
2782
 
2836
2783
  /* Helper: trim leading/trailing ASCII whitespace from an attribute value */
data/lib/apex/version.rb CHANGED
@@ -6,7 +6,7 @@ module Apex
6
6
  # via Apex::Native.version.
7
7
  module Version
8
8
  # Current gem version.
9
- VERSION = "1.0.34"
9
+ VERSION = "1.0.36"
10
10
  end
11
11
 
12
12
  # Shortcut to gem version string.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apex-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.34
4
+ version: 1.0.36
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra