rmultimarkdown 4.7.1.1 → 6.2.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +2 -2
  3. data/README.md +7 -9
  4. data/Rakefile +33 -18
  5. data/ext/Makefile +261 -0
  6. data/ext/extconf.rb +23 -3
  7. data/ext/mmd/aho-corasick.c +596 -0
  8. data/ext/mmd/aho-corasick.h +120 -0
  9. data/ext/mmd/beamer.c +344 -0
  10. data/ext/mmd/beamer.h +72 -0
  11. data/ext/mmd/char.c +156 -0
  12. data/ext/mmd/char.h +111 -0
  13. data/ext/mmd/char_lookup.c +212 -0
  14. data/ext/mmd/critic_markup.c +330 -0
  15. data/ext/mmd/critic_markup.h +94 -0
  16. data/ext/mmd/d_string.c +402 -0
  17. data/ext/mmd/epub.c +563 -0
  18. data/ext/mmd/epub.h +69 -0
  19. data/ext/mmd/fodt.c +2288 -0
  20. data/ext/mmd/fodt.h +81 -0
  21. data/ext/mmd/html.c +2460 -0
  22. data/ext/mmd/html.h +81 -0
  23. data/ext/mmd/i18n.h +170 -0
  24. data/ext/mmd/include/d_string.h +182 -0
  25. data/ext/mmd/include/libMultiMarkdown.h +548 -0
  26. data/ext/mmd/include/token.h +233 -0
  27. data/ext/mmd/latex.c +2435 -0
  28. data/ext/mmd/latex.h +83 -0
  29. data/ext/mmd/lexer.c +3001 -0
  30. data/ext/mmd/lexer.h +75 -0
  31. data/ext/mmd/memoir.c +138 -0
  32. data/ext/mmd/memoir.h +67 -0
  33. data/ext/mmd/miniz.c +7557 -0
  34. data/ext/mmd/miniz.h +1328 -0
  35. data/ext/mmd/mmd.c +2798 -0
  36. data/ext/mmd/mmd.h +120 -0
  37. data/ext/mmd/object_pool.c +141 -0
  38. data/ext/mmd/object_pool.h +101 -0
  39. data/ext/mmd/opendocument-content.c +2071 -0
  40. data/ext/mmd/opendocument-content.h +135 -0
  41. data/ext/mmd/opendocument.c +981 -0
  42. data/ext/mmd/opendocument.h +118 -0
  43. data/ext/mmd/parser.c +1760 -0
  44. data/ext/mmd/parser.h +39 -0
  45. data/{MultiMarkdown-4 → ext/mmd}/rng.c +90 -49
  46. data/ext/mmd/scanners.c +77512 -0
  47. data/ext/mmd/scanners.h +101 -0
  48. data/ext/mmd/stack.c +142 -0
  49. data/ext/mmd/stack.h +113 -0
  50. data/ext/mmd/textbundle.c +455 -0
  51. data/ext/mmd/textbundle.h +115 -0
  52. data/ext/mmd/token.c +773 -0
  53. data/ext/mmd/token_pairs.c +263 -0
  54. data/ext/mmd/token_pairs.h +123 -0
  55. data/ext/mmd/transclude.c +549 -0
  56. data/ext/mmd/transclude.h +87 -0
  57. data/ext/mmd/uthash.h +1074 -0
  58. data/ext/mmd/uuid.c +154 -0
  59. data/ext/mmd/uuid.h +77 -0
  60. data/ext/mmd/version.h +111 -0
  61. data/ext/mmd/writer.c +2652 -0
  62. data/ext/mmd/writer.h +260 -0
  63. data/ext/mmd/zip.c +210 -0
  64. data/ext/mmd/zip.h +120 -0
  65. data/ext/{multi_markdown.c → ruby_multi_markdown.c} +87 -18
  66. data/lib/multi_markdown.bundle +0 -0
  67. data/lib/multi_markdown.rb +5 -8
  68. data/lib/multi_markdown/version.rb +1 -1
  69. data/rmultimarkdown.gemspec +2 -2
  70. data/test/{extensions_test.rb.rb → extensions_test.rb} +10 -54
  71. data/test/multi_markdown_test.rb +13 -0
  72. metadata +67 -47
  73. data/MultiMarkdown-4/GLibFacade.c +0 -310
  74. data/MultiMarkdown-4/GLibFacade.h +0 -100
  75. data/MultiMarkdown-4/beamer.c +0 -182
  76. data/MultiMarkdown-4/beamer.h +0 -11
  77. data/MultiMarkdown-4/critic.c +0 -111
  78. data/MultiMarkdown-4/critic.h +0 -15
  79. data/MultiMarkdown-4/glib.h +0 -11
  80. data/MultiMarkdown-4/html.c +0 -1117
  81. data/MultiMarkdown-4/html.h +0 -14
  82. data/MultiMarkdown-4/latex.c +0 -1217
  83. data/MultiMarkdown-4/latex.h +0 -16
  84. data/MultiMarkdown-4/libMultiMarkdown.h +0 -177
  85. data/MultiMarkdown-4/lyx.c +0 -2265
  86. data/MultiMarkdown-4/lyx.h +0 -37
  87. data/MultiMarkdown-4/lyxbeamer.c +0 -265
  88. data/MultiMarkdown-4/lyxbeamer.h +0 -11
  89. data/MultiMarkdown-4/memoir.c +0 -80
  90. data/MultiMarkdown-4/memoir.h +0 -10
  91. data/MultiMarkdown-4/multimarkdown.c +0 -518
  92. data/MultiMarkdown-4/odf.c +0 -1222
  93. data/MultiMarkdown-4/odf.h +0 -18
  94. data/MultiMarkdown-4/opml.c +0 -189
  95. data/MultiMarkdown-4/opml.h +0 -15
  96. data/MultiMarkdown-4/parse_utilities.c +0 -884
  97. data/MultiMarkdown-4/parser.c +0 -16656
  98. data/MultiMarkdown-4/parser.h +0 -188
  99. data/MultiMarkdown-4/rtf.c +0 -665
  100. data/MultiMarkdown-4/rtf.h +0 -17
  101. data/MultiMarkdown-4/strtok.c +0 -56
  102. data/MultiMarkdown-4/strtok.h +0 -9
  103. data/MultiMarkdown-4/text.c +0 -53
  104. data/MultiMarkdown-4/text.h +0 -11
  105. data/MultiMarkdown-4/toc.c +0 -142
  106. data/MultiMarkdown-4/toc.h +0 -15
  107. data/MultiMarkdown-4/transclude.c +0 -307
  108. data/MultiMarkdown-4/transclude.h +0 -28
  109. data/MultiMarkdown-4/writer.c +0 -731
  110. data/MultiMarkdown-4/writer.h +0 -38
data/ext/mmd/uuid.c ADDED
@@ -0,0 +1,154 @@
1
+ /**
2
+
3
+ MultiMarkdown -- Lightweight markup processor to produce HTML, LaTeX, and more.
4
+
5
+ @file uuid.c
6
+
7
+ @brief
8
+
9
+
10
+ @author Fletcher T. Penney
11
+ @bug
12
+
13
+ **/
14
+
15
+ /*
16
+
17
+ Copyright © 2016 - 2017 Fletcher T. Penney.
18
+
19
+
20
+ The `MultiMarkdown 6` project is released under the MIT License..
21
+
22
+ GLibFacade.c and GLibFacade.h are from the MultiMarkdown v4 project:
23
+
24
+ https://github.com/fletcher/MultiMarkdown-4/
25
+
26
+ MMD 4 is released under both the MIT License and GPL.
27
+
28
+
29
+ CuTest is released under the zlib/libpng license. See CuTest.c for the
30
+ text of the license.
31
+
32
+ uthash library:
33
+ Copyright (c) 2005-2016, Troy D. Hanson
34
+
35
+ Licensed under BSD Revised license
36
+
37
+ miniz library:
38
+ Copyright 2013-2014 RAD Game Tools and Valve Software
39
+ Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
40
+
41
+ Licensed under the MIT license
42
+
43
+
44
+ ## The MIT License ##
45
+
46
+ Permission is hereby granted, free of charge, to any person obtaining
47
+ a copy of this software and associated documentation files (the
48
+ "Software"), to deal in the Software without restriction, including
49
+ without limitation the rights to use, copy, modify, merge, publish,
50
+ distribute, sublicense, and/or sell copies of the Software, and to
51
+ permit persons to whom the Software is furnished to do so, subject to
52
+ the following conditions:
53
+
54
+ The above copyright notice and this permission notice shall be
55
+ included in all copies or substantial portions of the Software.
56
+
57
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
58
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
59
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
60
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
61
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
62
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
63
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
64
+
65
+
66
+ */
67
+
68
+
69
+ #include <limits.h>
70
+ #include <stdlib.h>
71
+ #include <stdio.h>
72
+ #include <time.h>
73
+
74
+ #include "uuid.h"
75
+
76
+
77
+ #define SETBIT(a, n) (a[n/CHAR_BIT] |= (1<<(n % CHAR_BIT)))
78
+ #define CLEARBIT(a, n) (a[n/CHAR_BIT] &= ~(1<<(n % CHAR_BIT)))
79
+
80
+ char * uuid_string_from_bits(unsigned char * raw);
81
+
82
+ char * uuid_new(void) {
83
+ unsigned char raw[16];
84
+
85
+ // Get 128 bits of random goodness
86
+ for (int i = 0; i < 16; ++i) {
87
+ raw[i] = rand() % 256;
88
+ }
89
+
90
+ // Need to set certain bits for v4 compliance
91
+ CLEARBIT(raw, 52);
92
+ CLEARBIT(raw, 53);
93
+ SETBIT(raw, 54);
94
+ CLEARBIT(raw, 55);
95
+ CLEARBIT(raw, 70);
96
+ SETBIT(raw, 71);
97
+
98
+ return uuid_string_from_bits(raw);
99
+ }
100
+
101
+ char * uuid_string_from_bits(unsigned char * raw) {
102
+ char * result = malloc(37);
103
+
104
+ sprintf(result, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
105
+ raw[0], raw[1], raw[2], raw[3], raw[4], raw[5], raw[6], raw[7],
106
+ raw[8], raw[9], raw[10], raw[11], raw[12], raw[13], raw[14], raw[15] );
107
+
108
+ return result;
109
+ }
110
+
111
+
112
+
113
+ // http://stackoverflow.com/questions/322938/recommended-way-to-initialize-srand
114
+ // http://www.concentric.net/~Ttwang/tech/inthash.htm
115
+ unsigned long mix(unsigned long a, unsigned long b, unsigned long c) {
116
+ a = a - b;
117
+ a = a - c;
118
+ a = a ^ (c >> 13);
119
+ b = b - c;
120
+ b = b - a;
121
+ b = b ^ (a << 8);
122
+ c = c - a;
123
+ c = c - b;
124
+ c = c ^ (b >> 13);
125
+ a = a - b;
126
+ a = a - c;
127
+ a = a ^ (c >> 12);
128
+ b = b - c;
129
+ b = b - a;
130
+ b = b ^ (a << 16);
131
+ c = c - a;
132
+ c = c - b;
133
+ c = c ^ (b >> 5);
134
+ a = a - b;
135
+ a = a - c;
136
+ a = a ^ (c >> 3);
137
+ b = b - c;
138
+ b = b - a;
139
+ b = b ^ (a << 10);
140
+ c = c - a;
141
+ c = c - b;
142
+ c = c ^ (b >> 15);
143
+ return c;
144
+ }
145
+
146
+
147
+ void custom_seed_rand(void) {
148
+ // Seed random number generator
149
+ // This is not a "cryptographically secure" random seed,
150
+ // but good enough for an EPUB id....
151
+ unsigned long seed = mix(clock(), time(NULL), clock());
152
+ srand(seed);
153
+ }
154
+
data/ext/mmd/uuid.h ADDED
@@ -0,0 +1,77 @@
1
+ /**
2
+
3
+ MultiMarkdown -- Lightweight markup processor to produce HTML, LaTeX, and more.
4
+
5
+ @file uuid.h
6
+
7
+ @brief
8
+
9
+
10
+ @author Fletcher T. Penney
11
+ @bug
12
+
13
+ **/
14
+
15
+ /*
16
+
17
+ Copyright © 2016 - 2017 Fletcher T. Penney.
18
+
19
+
20
+ The `MultiMarkdown 6` project is released under the MIT License..
21
+
22
+ GLibFacade.c and GLibFacade.h are from the MultiMarkdown v4 project:
23
+
24
+ https://github.com/fletcher/MultiMarkdown-4/
25
+
26
+ MMD 4 is released under both the MIT License and GPL.
27
+
28
+
29
+ CuTest is released under the zlib/libpng license. See CuTest.c for the
30
+ text of the license.
31
+
32
+ uthash library:
33
+ Copyright (c) 2005-2016, Troy D. Hanson
34
+
35
+ Licensed under BSD Revised license
36
+
37
+ miniz library:
38
+ Copyright 2013-2014 RAD Game Tools and Valve Software
39
+ Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
40
+
41
+ Licensed under the MIT license
42
+
43
+
44
+ ## The MIT License ##
45
+
46
+ Permission is hereby granted, free of charge, to any person obtaining
47
+ a copy of this software and associated documentation files (the
48
+ "Software"), to deal in the Software without restriction, including
49
+ without limitation the rights to use, copy, modify, merge, publish,
50
+ distribute, sublicense, and/or sell copies of the Software, and to
51
+ permit persons to whom the Software is furnished to do so, subject to
52
+ the following conditions:
53
+
54
+ The above copyright notice and this permission notice shall be
55
+ included in all copies or substantial portions of the Software.
56
+
57
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
58
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
59
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
60
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
61
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
62
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
63
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
64
+
65
+
66
+ */
67
+
68
+
69
+ #ifndef UUID_MULTIMARKDOWN_H
70
+ #define UUID_MULTIMARKDOWN_H
71
+
72
+ char * uuid_new(void);
73
+ char * uuid_string_from_bits(unsigned char * raw);
74
+
75
+ void custom_seed_rand(void);
76
+
77
+ #endif
data/ext/mmd/version.h ADDED
@@ -0,0 +1,111 @@
1
+ /*
2
+
3
+ version.h -- MultiMarkdown
4
+
5
+ Copyright © 2016 - 2017 Fletcher T. Penney.
6
+
7
+
8
+
9
+
10
+ */
11
+
12
+ /**
13
+
14
+ @file
15
+
16
+ @brief Lightweight markup processor to produce HTML, LaTeX, and more. - project version header
17
+
18
+ **/
19
+
20
+
21
+ #ifndef FILE_MULTIMARKDOWN_H
22
+ #define FILE_MULTIMARKDOWN_H
23
+
24
+ #define MULTIMARKDOWN_NAME "MultiMarkdown"
25
+
26
+ #define MULTIMARKDOWN_VERSION "6.2.2"
27
+ #define MULTIMARKDOWN_COPYRIGHT "Copyright © 2016 - 2017 Fletcher T. Penney."
28
+
29
+ #define MULTIMARKDOWN_LICENSE "\tThe `MultiMarkdown 6` project is released under the MIT License..\n"\
30
+ " \n"\
31
+ " GLibFacade.c and GLibFacade.h are from the MultiMarkdown v4 project:\n"\
32
+ " \n"\
33
+ " https://github.com/fletcher/MultiMarkdown-4/\n"\
34
+ " \n"\
35
+ " MMD 4 is released under both the MIT License and GPL.\n"\
36
+ " \n"\
37
+ " \n"\
38
+ " CuTest is released under the zlib/libpng license. See CuTest.c for the\n"\
39
+ " text of the license.\n"\
40
+ " \n"\
41
+ " uthash library:\n"\
42
+ " Copyright (c) 2005-2016, Troy D. Hanson\n"\
43
+ " \n"\
44
+ " Licensed under Revised BSD license\n"\
45
+ " \n"\
46
+ " miniz library:\n"\
47
+ " Copyright 2013-2014 RAD Game Tools and Valve Software\n"\
48
+ " Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC\n"\
49
+ " \n"\
50
+ " Licensed under the MIT license\n"\
51
+ " \n"\
52
+ " argtable3 library:\n"\
53
+ " Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann\n"\
54
+ " <sheitmann@users.sourceforge.net>\n"\
55
+ " All rights reserved.\n"\
56
+ " \n"\
57
+ " Licensed under the Revised BSD License\n"\
58
+ " \n"\
59
+ " \n"\
60
+ " ## The MIT License ##\n"\
61
+ " \n"\
62
+ " Permission is hereby granted, free of charge, to any person obtaining\n"\
63
+ " a copy of this software and associated documentation files (the\n"\
64
+ " \"Software\"), to deal in the Software without restriction, including\n"\
65
+ " without limitation the rights to use, copy, modify, merge, publish,\n"\
66
+ " distribute, sublicense, and/or sell copies of the Software, and to\n"\
67
+ " permit persons to whom the Software is furnished to do so, subject to\n"\
68
+ " the following conditions:\n"\
69
+ " \n"\
70
+ " The above copyright notice and this permission notice shall be\n"\
71
+ " included in all copies or substantial portions of the Software.\n"\
72
+ " \n"\
73
+ " THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n"\
74
+ " EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n"\
75
+ " MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n"\
76
+ " IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n"\
77
+ " CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n"\
78
+ " TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n"\
79
+ " SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"\
80
+ " \n"\
81
+ " \n"\
82
+ " ## Revised BSD License ##\n"\
83
+ " \n"\
84
+ " Redistribution and use in source and binary forms, with or without\n"\
85
+ " modification, are permitted provided that the following conditions are\n"\
86
+ " met:\n"\
87
+ " * Redistributions of source code must retain the above copyright\n"\
88
+ " notice, this list of conditions and the following disclaimer.\n"\
89
+ " * Redistributions in binary form must reproduce the above\n"\
90
+ " copyright notice, this list of conditions and the following\n"\
91
+ " disclaimer in the documentation and/or other materials provided\n"\
92
+ " with the distribution.\n"\
93
+ " * Neither the name of the <organization> nor the\n"\
94
+ " names of its contributors may be used to endorse or promote\n"\
95
+ " products derived from this software without specific prior\n"\
96
+ " written permission.\n"\
97
+ " \n"\
98
+ " THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"\
99
+ " \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"\
100
+ " LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"\
101
+ " A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT\n"\
102
+ " HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n"\
103
+ " EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n"\
104
+ " PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES LOSS OF USE, DATA, OR\n"\
105
+ " PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n"\
106
+ " LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n"\
107
+ " NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n"\
108
+ " SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"\
109
+ " "
110
+
111
+ #endif
data/ext/mmd/writer.c ADDED
@@ -0,0 +1,2652 @@
1
+ /**
2
+
3
+ MultiMarkdown 6 -- Lightweight markup processor to produce HTML, LaTeX, and more.
4
+
5
+ @file writer.c
6
+
7
+ @brief Coordinate conversion of token tree to output formats.
8
+
9
+
10
+ @author Fletcher T. Penney
11
+ @bug
12
+
13
+ **/
14
+
15
+ /*
16
+
17
+ Copyright © 2016 - 2017 Fletcher T. Penney.
18
+
19
+
20
+ The `MultiMarkdown 6` project is released under the MIT License..
21
+
22
+ GLibFacade.c and GLibFacade.h are from the MultiMarkdown v4 project:
23
+
24
+ https://github.com/fletcher/MultiMarkdown-4/
25
+
26
+ MMD 4 is released under both the MIT License and GPL.
27
+
28
+
29
+ CuTest is released under the zlib/libpng license. See CuTest.c for the text
30
+ of the license.
31
+
32
+
33
+ ## The MIT License ##
34
+
35
+ Permission is hereby granted, free of charge, to any person obtaining a copy
36
+ of this software and associated documentation files (the "Software"), to deal
37
+ in the Software without restriction, including without limitation the rights
38
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
39
+ copies of the Software, and to permit persons to whom the Software is
40
+ furnished to do so, subject to the following conditions:
41
+
42
+ The above copyright notice and this permission notice shall be included in
43
+ all copies or substantial portions of the Software.
44
+
45
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
46
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
47
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
48
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
49
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
50
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
51
+ THE SOFTWARE.
52
+
53
+ */
54
+
55
+ #include <ctype.h>
56
+ #include <stdio.h>
57
+ #include <stdlib.h>
58
+ #include <string.h>
59
+
60
+ #include "libMultiMarkdown.h"
61
+
62
+ #include "aho-corasick.h"
63
+ #include "beamer.h"
64
+ #include "char.h"
65
+ #include "d_string.h"
66
+ #include "html.h"
67
+ #include "i18n.h"
68
+ #include "latex.h"
69
+ #include "memoir.h"
70
+ #include "mmd.h"
71
+ #include "opendocument-content.h"
72
+ #include "parser.h"
73
+ #include "scanners.h"
74
+ #include "token.h"
75
+ #include "uuid.h"
76
+ #include "writer.h"
77
+
78
+
79
+ void store_citation(scratch_pad * scratch, footnote * f);
80
+
81
+ void store_footnote(scratch_pad * scratch, footnote * f);
82
+
83
+ void store_glossary(scratch_pad * scratch, footnote * f);
84
+
85
+ void store_link(scratch_pad * scratch, link * l);
86
+
87
+ void store_metadata(scratch_pad * scratch, meta * m);
88
+
89
+ void store_abbreviation(scratch_pad * scratch, footnote * a);
90
+
91
+
92
+ /// strndup not available on all platforms
93
+ static char * my_strndup(const char * source, size_t n) {
94
+ if (source == NULL) {
95
+ return NULL;
96
+ }
97
+
98
+ size_t len = 0;
99
+ char * result;
100
+ const char * test = source;
101
+
102
+ // strlen is too slow is strlen(source) >> n
103
+ for (len = 0; len < n; ++len) {
104
+ if (test == '\0') {
105
+ break;
106
+ }
107
+
108
+ test++;
109
+ }
110
+
111
+ result = malloc(len + 1);
112
+
113
+ if (result) {
114
+ memcpy(result, source, len);
115
+ result[len] = '\0';
116
+ }
117
+
118
+ return result;
119
+ }
120
+
121
+
122
+ /// strdup() not available on all platforms
123
+ static char * my_strdup(const char * source) {
124
+ if (source == NULL) {
125
+ return NULL;
126
+ }
127
+
128
+ char * result = malloc(strlen(source) + 1);
129
+
130
+ if (result) {
131
+ strcpy(result, source);
132
+ }
133
+
134
+ return result;
135
+ }
136
+
137
+
138
+ /// Temporary storage while exporting parse tree to output format
139
+ scratch_pad * scratch_pad_new(mmd_engine * e, short format) {
140
+ scratch_pad * p = malloc(sizeof(scratch_pad));
141
+
142
+ if (p) {
143
+ p->padded = 2; // Prevent unnecessary leading space
144
+ p->list_is_tight = false; // Tight vs Loose list
145
+ p->skip_token = 0; // Skip over next n tokens
146
+ p->close_para = true;
147
+
148
+ p->extensions = e->extensions;
149
+ p->output_format = format;
150
+ p->quotes_lang = e->quotes_lang;
151
+ p->language = e->language;
152
+
153
+ p->header_stack = e->header_stack;
154
+
155
+ p->outline_stack = stack_new(0);
156
+
157
+ p->recurse_depth = 0;
158
+
159
+ p->base_header_level = 1;
160
+
161
+ p->odf_para_type = BLOCK_PARA;
162
+
163
+ if (e->extensions & EXT_RANDOM_FOOT) {
164
+ p->random_seed_base = rand() % 32000;
165
+ } else {
166
+ p->random_seed_base = 0;
167
+ }
168
+
169
+ // Store links in a hash for rapid retrieval when exporting
170
+ p->link_hash = NULL;
171
+ link * l;
172
+
173
+ for (int i = 0; i < e->link_stack->size; ++i) {
174
+ l = stack_peek_index(e->link_stack, i);
175
+
176
+ store_link(p, l);
177
+ }
178
+
179
+ // Store citations in a hash for rapid retrieval when exporting
180
+ footnote * f;
181
+
182
+ p->used_citations = stack_new(0);
183
+ p->inline_citations_to_free = stack_new(0);
184
+ p->citation_being_printed = 0;
185
+ p->bibtex_file = NULL;
186
+
187
+ p->citation_hash = NULL;
188
+
189
+ for (int i = 0; i < e->citation_stack->size; ++i) {
190
+ f = stack_peek_index(e->citation_stack, i);
191
+
192
+ store_citation(p, f);
193
+ }
194
+
195
+ // Store footnotes in a hash for rapid retrieval when exporting
196
+ p->used_footnotes = stack_new(0); // Store footnotes as we use them
197
+ p->inline_footnotes_to_free = stack_new(0); // Inline footnotes need to be freed
198
+ p->footnote_being_printed = 0;
199
+ p->footnote_para_counter = -1;
200
+
201
+ p->footnote_hash = NULL; // Store defined footnotes in a hash
202
+
203
+ for (int i = 0; i < e->footnote_stack->size; ++i) {
204
+ f = stack_peek_index(e->footnote_stack, i);
205
+
206
+ store_footnote(p, f);
207
+ }
208
+
209
+ // Store glossaries in a hash for rapid retrieval when exporting
210
+ p->used_glossaries = stack_new(0);
211
+ p->inline_glossaries_to_free = stack_new(0);
212
+ p->glossary_being_printed = 0;
213
+
214
+ p->glossary_hash = NULL;
215
+
216
+ for (int i = 0; i < e->glossary_stack->size; ++i) {
217
+ f = stack_peek_index(e->glossary_stack, i);
218
+
219
+ store_glossary(p, f);
220
+ }
221
+
222
+ // Store abbreviations in a hash for rapid retrieval when exporting
223
+ p->used_abbreviations = stack_new(0);
224
+ p->inline_abbreviations_to_free = stack_new(0);
225
+
226
+ p->abbreviation_hash = NULL;
227
+
228
+ for (int i = 0; i < e->abbreviation_stack->size; ++i) {
229
+ f = stack_peek_index(e->abbreviation_stack, i);
230
+
231
+ store_abbreviation(p, f);
232
+ }
233
+
234
+ // Store metadata in a hash for rapid retrieval when exporting
235
+ p->meta_hash = NULL;
236
+ meta * m;
237
+
238
+ for (int i = 0; i < e->metadata_stack->size; ++i) {
239
+ m = stack_peek_index(e->metadata_stack, i);
240
+
241
+ store_metadata(p, m);
242
+ }
243
+
244
+
245
+ // Store used assets in a hash
246
+ p->asset_hash = NULL;
247
+ p->store_assets = 0;
248
+ p->remember_assets = 0;
249
+ }
250
+
251
+ return p;
252
+ }
253
+
254
+
255
+ void scratch_pad_free(scratch_pad * scratch) {
256
+ // HASH_CLEAR(hh, scratch->link_hash);
257
+
258
+ stack_free(scratch->outline_stack);
259
+
260
+ link * l, * l_tmp;
261
+
262
+ // Free link hash
263
+ HASH_ITER(hh, scratch->link_hash, l, l_tmp) {
264
+ HASH_DEL(scratch->link_hash, l); // Remove item from hash
265
+ free(l); // "Shallow" free -- the pointers will be freed
266
+ // with the original later.
267
+ }
268
+
269
+ fn_holder * f, * f_tmp;
270
+
271
+
272
+ // Free footnote hash
273
+ HASH_ITER(hh, scratch->footnote_hash, f, f_tmp) {
274
+ HASH_DEL(scratch->footnote_hash, f); // Remove item from hash
275
+ free(f); // Free the fn_holder
276
+ }
277
+ stack_free(scratch->used_footnotes);
278
+
279
+ while (scratch->inline_footnotes_to_free->size) {
280
+ footnote_free(stack_pop(scratch->inline_footnotes_to_free));
281
+ }
282
+
283
+ stack_free(scratch->inline_footnotes_to_free);
284
+
285
+
286
+ // Free citation hash
287
+ HASH_ITER(hh, scratch->citation_hash, f, f_tmp) {
288
+ HASH_DEL(scratch->citation_hash, f); // Remove item from hash
289
+ free(f); // Free the fn_holder
290
+ }
291
+ stack_free(scratch->used_citations);
292
+
293
+ while (scratch->inline_citations_to_free->size) {
294
+ footnote_free(stack_pop(scratch->inline_citations_to_free));
295
+ }
296
+
297
+ stack_free(scratch->inline_citations_to_free);
298
+
299
+ free(scratch->bibtex_file);
300
+
301
+ // Free glossary hash
302
+ HASH_ITER(hh, scratch->glossary_hash, f, f_tmp) {
303
+ HASH_DEL(scratch->glossary_hash, f); // Remove item from hash
304
+ free(f); // Free the fn_holder
305
+ }
306
+ stack_free(scratch->used_glossaries);
307
+
308
+ while (scratch->inline_glossaries_to_free->size) {
309
+ footnote_free(stack_pop(scratch->inline_glossaries_to_free));
310
+ }
311
+
312
+ stack_free(scratch->inline_glossaries_to_free);
313
+
314
+
315
+ // Free abbreviation hash
316
+ HASH_ITER(hh, scratch->abbreviation_hash, f, f_tmp) {
317
+ HASH_DEL(scratch->abbreviation_hash, f); // Remove item from hash
318
+ free(f); // Free the fn_holder
319
+ }
320
+ stack_free(scratch->used_abbreviations);
321
+
322
+ while (scratch->inline_abbreviations_to_free->size) {
323
+ footnote_free(stack_pop(scratch->inline_abbreviations_to_free));
324
+ }
325
+
326
+ stack_free(scratch->inline_abbreviations_to_free);
327
+
328
+
329
+ // Free metadata hash
330
+ meta * m, * m_tmp;
331
+
332
+ HASH_ITER(hh, scratch->meta_hash, m, m_tmp) {
333
+ HASH_DEL(scratch->meta_hash, m); // Remove item from hash
334
+ // Don't free meta pointer since it is freed with the mmd_engine
335
+ //meta_free(m);
336
+ }
337
+
338
+ free(scratch);
339
+ }
340
+
341
+
342
+ /// Ensure at least num newlines at end of output buffer
343
+ void pad(DString * d, short num, scratch_pad * scratch) {
344
+ while (num > scratch->padded) {
345
+ d_string_append_c(d, '\n');
346
+ scratch->padded++;
347
+ }
348
+ }
349
+
350
+
351
+ void print_token_raw(DString * out, const char * source, token * t) {
352
+ if (t) {
353
+ switch (t->type) {
354
+ case EMPH_START:
355
+ case EMPH_STOP:
356
+ case STRONG_START:
357
+ case STRONG_STOP:
358
+ case TEXT_EMPTY:
359
+ break;
360
+
361
+ case PAIR_EMPH:
362
+ case PAIR_STRONG:
363
+ print_token_tree_raw(out, source, t->child);
364
+ break;
365
+
366
+ default:
367
+ d_string_append_c_array(out, &source[t->start], t->len);
368
+ break;
369
+ }
370
+ }
371
+ }
372
+
373
+
374
+ void print_token_tree_raw(DString * out, const char * source, token * t) {
375
+ while (t) {
376
+ print_token_raw(out, source, t);
377
+
378
+ t = t->next;
379
+ }
380
+ }
381
+
382
+
383
+ char * text_inside_pair(const char * source, token * pair) {
384
+ char * result = NULL;
385
+
386
+ if (source && pair) {
387
+ if (pair->child && pair->child->mate) {
388
+ // [foo], [^foo], [#foo] should give different strings -- use closer len
389
+ result = my_strndup(&source[pair->start + pair->child->mate->len], pair->len - (pair->child->mate->len * 2));
390
+ } else {
391
+ if (pair->child) {
392
+ result = my_strndup(&source[pair->start + pair->child->len], pair->len - (pair->child->len + 1));
393
+ }
394
+ }
395
+ }
396
+
397
+ return result;
398
+ }
399
+
400
+
401
+ char * label_from_string(const char * str) {
402
+ const char * next_char;
403
+ char * label = NULL;
404
+
405
+ DString * out = d_string_new("");
406
+
407
+ while (*str != '\0') {
408
+ next_char = str;
409
+ next_char++;
410
+
411
+ if ((*next_char & 0xC0) == 0x80) {
412
+ // Allow multibyte characters
413
+ d_string_append_c(out, *str);
414
+
415
+ while ((*next_char & 0xC0) == 0x80) {
416
+ str++;
417
+ d_string_append_c(out, *str);
418
+ next_char++;
419
+ }
420
+ } else if ((*str >= '0' && *str <= '9') || (*str >= 'A' && *str <= 'Z')
421
+ || (*str >= 'a' && *str <= 'z') || (*str == '.') || (*str == '_')
422
+ || (*str == '-') || (*str == ':')) {
423
+ // Allow 0-9, A-Z, a-z, ., _, -, :
424
+ d_string_append_c(out, tolower(*str));
425
+ }
426
+
427
+ str++;
428
+ }
429
+
430
+ label = out->str;
431
+ d_string_free(out, false);
432
+
433
+ return label;
434
+ }
435
+
436
+
437
+ char * label_from_token(const char * source, token * t) {
438
+ char * label = NULL;
439
+
440
+ DString * raw = d_string_new("");
441
+
442
+ d_string_append_c_array(raw, &source[t->start], t->len);
443
+
444
+ label = label_from_string(raw->str);
445
+
446
+ d_string_free(raw, true);
447
+
448
+ return label;
449
+ }
450
+
451
+
452
+ char * label_from_header(const char * source, token * t) {
453
+ char * result;
454
+ token * temp_token = manual_label_from_header(t, source);
455
+
456
+ if (temp_token) {
457
+ result = label_from_token(source, temp_token);
458
+ } else {
459
+ result = label_from_token(source, t);
460
+ }
461
+
462
+ return result;
463
+ }
464
+
465
+
466
+ /// Clean up whitespace in string for standardization
467
+ char * clean_string(const char * str, bool lowercase) {
468
+ if (str == NULL) {
469
+ return NULL;
470
+ }
471
+
472
+ DString * out = d_string_new("");
473
+ char * clean = NULL;
474
+ bool block_whitespace = true;
475
+
476
+ while (*str != '\0') {
477
+ switch (*str) {
478
+ case '\t':
479
+ case ' ':
480
+ case '\n':
481
+ case '\r':
482
+ if (!block_whitespace) {
483
+ d_string_append_c(out, ' ');
484
+ block_whitespace = true;
485
+ }
486
+
487
+ break;
488
+
489
+ default:
490
+ if (lowercase) {
491
+ d_string_append_c(out, tolower(*str));
492
+ } else {
493
+ d_string_append_c(out, *str);
494
+ }
495
+
496
+ block_whitespace = false;
497
+ break;
498
+ }
499
+
500
+ str++;
501
+ }
502
+
503
+ clean = out->str;
504
+
505
+ // Trim trailing whitespace/newlines
506
+ while (out->currentStringLength && char_is_whitespace_or_line_ending(clean[out->currentStringLength - 1])) {
507
+ out->currentStringLength--;
508
+ clean[out->currentStringLength] = '\0';
509
+ }
510
+
511
+ d_string_free(out, false);
512
+
513
+ // Trim trailing whitespace
514
+ return clean;
515
+ }
516
+
517
+
518
+ char * clean_string_from_range(const char * source, size_t start, size_t len, bool lowercase) {
519
+ char * clean = NULL;
520
+
521
+ DString * raw = d_string_new("");
522
+
523
+ d_string_append_c_array(raw, &source[start], len);
524
+
525
+ clean = clean_string(raw->str, lowercase);
526
+
527
+ d_string_free(raw, true);
528
+
529
+ return clean;
530
+ }
531
+
532
+
533
+ char * clean_string_from_token(const char * source, token * t, bool lowercase) {
534
+ return clean_string_from_range(source, t->start, t->len, lowercase);
535
+ }
536
+
537
+
538
+ char * clean_inside_pair(const char * source, token * t, bool lowercase) {
539
+ char * text = text_inside_pair(source, t);
540
+
541
+ char * clean = clean_string(text, lowercase);
542
+
543
+ free(text);
544
+
545
+ return clean;
546
+ }
547
+
548
+
549
+ attr * attr_new(char * key, char * value) {
550
+ attr * a = malloc(sizeof(attr));
551
+ size_t len = strlen(value);
552
+
553
+ // Strip quotes if present
554
+ if (value[0] == '"') {
555
+ value++;
556
+ len--;
557
+ }
558
+
559
+ if (value[len - 1] == '"') {
560
+ value[len - 1] = '\0';
561
+ }
562
+
563
+ if (a) {
564
+ a->key = key;
565
+ a->value = my_strdup(value);
566
+ a->next = NULL;
567
+ }
568
+
569
+ return a;
570
+ }
571
+
572
+
573
+ attr * parse_attributes(char * source) {
574
+ attr * attributes = NULL;
575
+ attr * a = NULL;
576
+ char * key = NULL;
577
+ char * value = NULL;
578
+ size_t scan_len;
579
+ size_t pos = 0;
580
+
581
+ while (source[pos] != '\0' && scan_attr(&source[pos])) {
582
+ pos += scan_spnl(&source[pos]);
583
+
584
+ // Get key
585
+ scan_len = scan_key(&source[pos]);
586
+ key = my_strndup(&source[pos], scan_len);
587
+
588
+ // Skip '='
589
+ pos += scan_len + 1;
590
+
591
+ // Get value
592
+ scan_len = scan_value(&source[pos]);
593
+ value = my_strndup(&source[pos], scan_len);
594
+
595
+ pos += scan_len;
596
+
597
+ if (a) {
598
+ a->next = attr_new(key, value);
599
+ a = a->next;
600
+ } else {
601
+ a = attr_new(key, value);
602
+ attributes = a;
603
+ }
604
+
605
+ free(value); // We stored a modified copy
606
+ }
607
+
608
+ return attributes;
609
+ }
610
+
611
+
612
+ link * link_new(const char * source, token * label, char * url, char * title, char * attributes) {
613
+ link * l = malloc(sizeof(link));
614
+
615
+ if (l) {
616
+ l->label = label;
617
+
618
+ if (label) {
619
+ l->clean_text = clean_inside_pair(source, label, true);
620
+ l->label_text = label_from_token(source, label);
621
+ } else {
622
+ l->clean_text = NULL;
623
+ l->label_text = NULL;
624
+ }
625
+
626
+ l->url = clean_string(url, false);
627
+ l->title = (title == NULL) ? NULL : my_strdup(title);
628
+ l->attributes = (attributes == NULL) ? NULL : parse_attributes(attributes);
629
+ }
630
+
631
+ return l;
632
+ }
633
+
634
+
635
+ /// Store shallow copies of links in the storage hash. The link
636
+ /// itself is new, but references the same data as the original.
637
+ /// This allows the copied link to simply be `free()`'d without
638
+ /// freeing the pointers.
639
+ link * link_shallow_copy(link * l) {
640
+ link * new = malloc(sizeof(link));
641
+
642
+ if (new) {
643
+ new->label = l->label;
644
+ new->clean_text = l->clean_text;
645
+ new->label_text = l->label_text;
646
+ new->url = l->url;
647
+ new->title = l->title;
648
+ new->attributes = l->attributes;
649
+ }
650
+
651
+ return new;
652
+ }
653
+
654
+
655
+ /// Copy stored links to a hash for quick searching during export.
656
+ /// Links are stored via a clean version of their text(from
657
+ /// `clean_string()`) and a label version (`label_from_string()`).
658
+ /// The first link for each string is stored.
659
+ void store_link(scratch_pad * scratch, link * l) {
660
+ link * temp_link;
661
+
662
+ // Add link via `clean_text`?
663
+ if (l->clean_text && l->clean_text[0] != '\0') {
664
+ HASH_FIND_STR(scratch->link_hash, l->clean_text, temp_link);
665
+
666
+ if (!temp_link) {
667
+ // Only add if another link is not found with clean_text
668
+ temp_link = link_shallow_copy(l);
669
+ HASH_ADD_KEYPTR(hh, scratch->link_hash, l->clean_text, strlen(l->clean_text), temp_link);
670
+ }
671
+ }
672
+
673
+ // Add link via `label_text`?
674
+ if (l->label_text && l->label_text[0] != '\0') {
675
+ HASH_FIND_STR(scratch->link_hash, l->label_text, temp_link);
676
+
677
+ if (!temp_link) {
678
+ // Only add if another link is not found with label_text
679
+ temp_link = link_shallow_copy(l);
680
+ HASH_ADD_KEYPTR(hh, scratch->link_hash, l->label_text, strlen(l->label_text), temp_link);
681
+ }
682
+ }
683
+ }
684
+
685
+ link * retrieve_link(scratch_pad * scratch, const char * key) {
686
+ link * l;
687
+
688
+ HASH_FIND_STR(scratch->link_hash, key, l);
689
+
690
+ if (l) {
691
+ return l;
692
+ }
693
+
694
+ char * clean = clean_string(key, true);
695
+
696
+ HASH_FIND_STR(scratch->link_hash, clean, l);
697
+
698
+ free(clean);
699
+
700
+ return l;
701
+ }
702
+
703
+
704
+ fn_holder * fn_holder_new(footnote * f) {
705
+ fn_holder * h = malloc(sizeof(fn_holder));
706
+
707
+ if (h) {
708
+ h->note = f;
709
+ }
710
+
711
+ return h;
712
+ }
713
+
714
+
715
+ void store_footnote(scratch_pad * scratch, footnote * f) {
716
+ fn_holder * temp_holder;
717
+
718
+ // Store by `clean_text`?
719
+ if (f->clean_text && f->clean_text[0] != '\0') {
720
+ HASH_FIND_STR(scratch->footnote_hash, f->clean_text, temp_holder);
721
+
722
+ if (!temp_holder) {
723
+ temp_holder = fn_holder_new(f);
724
+ HASH_ADD_KEYPTR(hh, scratch->footnote_hash, f->clean_text, strlen(f->clean_text), temp_holder);
725
+ }
726
+ }
727
+
728
+ // Store by `label_text`?
729
+ if (f->label_text && f->label_text[0] != '\0') {
730
+ HASH_FIND_STR(scratch->footnote_hash, f->label_text, temp_holder);
731
+
732
+ if (!temp_holder) {
733
+ temp_holder = fn_holder_new(f);
734
+ HASH_ADD_KEYPTR(hh, scratch->footnote_hash, f->label_text, strlen(f->label_text), temp_holder);
735
+ }
736
+ }
737
+ }
738
+
739
+
740
+ void store_citation(scratch_pad * scratch, footnote * f) {
741
+ fn_holder * temp_holder;
742
+
743
+ // Store by `clean_text`?
744
+ if (f->clean_text && f->clean_text[0] != '\0') {
745
+ HASH_FIND_STR(scratch->citation_hash, f->clean_text, temp_holder);
746
+
747
+ if (!temp_holder) {
748
+ temp_holder = fn_holder_new(f);
749
+ HASH_ADD_KEYPTR(hh, scratch->citation_hash, f->clean_text, strlen(f->clean_text), temp_holder);
750
+ }
751
+ }
752
+
753
+ // Store by `label_text`?
754
+ if (f->label_text && f->label_text[0] != '\0') {
755
+ HASH_FIND_STR(scratch->citation_hash, f->label_text, temp_holder);
756
+
757
+ if (!temp_holder) {
758
+ temp_holder = fn_holder_new(f);
759
+ HASH_ADD_KEYPTR(hh, scratch->citation_hash, f->label_text, strlen(f->label_text), temp_holder);
760
+ }
761
+ }
762
+ }
763
+
764
+
765
+ void store_glossary(scratch_pad * scratch, footnote * f) {
766
+ fn_holder * temp_holder;
767
+
768
+ // Store by `clean_text`?
769
+ if (f->clean_text && f->clean_text[0] != '\0') {
770
+ HASH_FIND_STR(scratch->glossary_hash, f->clean_text, temp_holder);
771
+
772
+ if (!temp_holder) {
773
+ temp_holder = fn_holder_new(f);
774
+ HASH_ADD_KEYPTR(hh, scratch->glossary_hash, f->clean_text, strlen(f->clean_text), temp_holder);
775
+ }
776
+ }
777
+
778
+ // Store by `label_text`?
779
+ if (f->label_text && f->label_text[0] != '\0') {
780
+ HASH_FIND_STR(scratch->glossary_hash, f->label_text, temp_holder);
781
+
782
+ if (!temp_holder) {
783
+ temp_holder = fn_holder_new(f);
784
+ HASH_ADD_KEYPTR(hh, scratch->glossary_hash, f->label_text, strlen(f->label_text), temp_holder);
785
+ }
786
+ }
787
+ }
788
+
789
+
790
+ void store_metadata(scratch_pad * scratch, meta * m) {
791
+ meta * temp;
792
+
793
+ // Store by `key`
794
+ if (m->key && m->key[0] != '\0') {
795
+ HASH_FIND_STR(scratch->meta_hash, m->key, temp);
796
+
797
+ if (!temp) {
798
+ HASH_ADD_KEYPTR(hh, scratch->meta_hash, m->key, strlen(m->key), m);
799
+ }
800
+ }
801
+ }
802
+
803
+
804
+ void store_abbreviation(scratch_pad * scratch, footnote * f) {
805
+ fn_holder * temp_holder;
806
+
807
+ // Store by `label_text`
808
+ if (f->label_text && f->label_text[0] != '\0') {
809
+ HASH_FIND_STR(scratch->abbreviation_hash, f->label_text, temp_holder);
810
+
811
+ if (!temp_holder) {
812
+ temp_holder = fn_holder_new(f);
813
+ HASH_ADD_KEYPTR(hh, scratch->abbreviation_hash, f->label_text, strlen(f->label_text), temp_holder);
814
+ }
815
+ }
816
+ }
817
+
818
+
819
+ void link_free(link * l) {
820
+ free(l->label_text);
821
+ free(l->clean_text);
822
+ free(l->url);
823
+ free(l->title);
824
+ // free(l->id);
825
+
826
+ attr * a = l->attributes;
827
+ attr * b;
828
+
829
+ while (a) {
830
+ b = a->next;
831
+ free(a->key);
832
+ free(a->value);
833
+ free(a);
834
+ a = b;
835
+ }
836
+
837
+ free(l);
838
+ }
839
+
840
+
841
+ void whitespace_accept(token ** remainder) {
842
+ while (token_chain_accept_multiple(remainder, 3, NON_INDENT_SPACE, INDENT_SPACE, INDENT_TAB));
843
+ }
844
+
845
+
846
+ /// Find link based on label
847
+ link * extract_link_from_stack(scratch_pad * scratch, const char * target) {
848
+ char * key = clean_string(target, true);
849
+
850
+ link * temp = NULL;
851
+
852
+ HASH_FIND_STR(scratch->link_hash, key, temp);
853
+
854
+ free(key);
855
+
856
+ if (temp) {
857
+ return temp;
858
+ }
859
+
860
+ key = label_from_string(target);
861
+
862
+ HASH_FIND_STR(scratch->link_hash, key, temp);
863
+
864
+ free(key);
865
+
866
+ return temp;
867
+ }
868
+
869
+
870
+ bool validate_url(const char * url) {
871
+ size_t len = scan_url(url);
872
+
873
+ return (len && len == strlen(url)) ? true : false;
874
+ }
875
+
876
+
877
+ char * destination_accept(const char * source, token ** remainder, bool validate) {
878
+ char * url = NULL;
879
+ char * clean = NULL;
880
+ token * t = NULL;
881
+ size_t start;
882
+ size_t scan_len;
883
+
884
+ if (*remainder == NULL) {
885
+ return url;
886
+ }
887
+
888
+ switch ((*remainder)->type) {
889
+ case PAIR_PAREN:
890
+ case PAIR_ANGLE:
891
+ case PAIR_QUOTE_SINGLE:
892
+ case PAIR_QUOTE_DOUBLE:
893
+ t = token_chain_accept_multiple(remainder, 2, PAIR_ANGLE, PAIR_PAREN);
894
+ url = text_inside_pair(source, t);
895
+ break;
896
+
897
+ default:
898
+ start = (*remainder)->start;
899
+
900
+ // Skip any whitespace
901
+ while (char_is_whitespace(source[start])) {
902
+ start++;
903
+ }
904
+
905
+ scan_len = scan_destination(&source[start]);
906
+
907
+ // Grab destination string
908
+ url = my_strndup(&source[start], scan_len);
909
+
910
+ // Advance remainder to end of destination
911
+ while ((*remainder)->next &&
912
+ (*remainder)->next->start < start + scan_len) {
913
+ *remainder = (*remainder)->next;
914
+ }
915
+
916
+ t = (*remainder); // We need to remember this for below
917
+ // Move remainder beyond destination
918
+ *remainder = (*remainder)->next;
919
+
920
+ // Is there a space in a URL concatenated with a title or attribute?
921
+ // e.g. [foo]: http://foo.bar/ class="foo"
922
+ // Since only one space between URL and class, they are joined.
923
+
924
+ if (t->type == TEXT_PLAIN) {
925
+ // Trim leading whitespace
926
+ token_trim_leading_whitespace(t, source);
927
+ token_split_on_char(t, source, ' ');
928
+ *remainder = t->next;
929
+ }
930
+
931
+ break;
932
+ }
933
+
934
+ // Is this a valid URL?
935
+ clean = clean_string(url, false);
936
+
937
+ if (validate && !validate_url(clean)) {
938
+ free(clean);
939
+ clean = NULL;
940
+ }
941
+
942
+ free(url);
943
+ return clean;
944
+ }
945
+
946
+
947
+ char * url_accept(const char * source, size_t start, size_t max_len, size_t * end_pos, bool validate) {
948
+ char * url = NULL;
949
+ char * clean = NULL;
950
+ size_t scan_len;
951
+
952
+ scan_len = scan_destination(&source[start]);
953
+
954
+ if (scan_len) {
955
+ if (scan_len > max_len) {
956
+ scan_len = max_len;
957
+ }
958
+
959
+ if (end_pos) {
960
+ *end_pos = start + scan_len;
961
+ }
962
+
963
+ // Is this <foo>?
964
+ if ((source[start] == '<') &&
965
+ (source[start + scan_len - 1] == '>')) {
966
+ // Strip '<' and '>'
967
+ start++;
968
+ scan_len -= 2;
969
+ }
970
+
971
+ url = my_strndup(&source[start], scan_len);
972
+
973
+ clean = clean_string(url, false);
974
+
975
+ if (validate && !validate_url(clean)) {
976
+ free(clean);
977
+ clean = NULL;
978
+ }
979
+
980
+ free(url);
981
+ }
982
+
983
+ return clean;
984
+ }
985
+
986
+
987
+ /// Extract url string from `(foo)` or `(<foo>)` or `(foo "bar")`
988
+ void extract_from_paren(token * paren, const char * source, char ** url, char ** title, char ** attributes) {
989
+ size_t scan_len;
990
+ size_t pos = paren->child->next->start;
991
+
992
+
993
+ size_t attr_len;
994
+
995
+ // Skip whitespace
996
+ while (char_is_whitespace(source[pos])) {
997
+ pos++;
998
+ }
999
+
1000
+ // Grab URL
1001
+ *url = url_accept(source, pos, paren->start + paren->len - 1 - pos, &pos, false);
1002
+
1003
+ // Skip whitespace
1004
+ while (char_is_whitespace(source[pos])) {
1005
+ pos++;
1006
+ }
1007
+
1008
+ // Grab title, if present
1009
+ scan_len = scan_title(&source[pos]);
1010
+
1011
+ if (scan_len) {
1012
+ *title = my_strndup(&source[pos + 1], scan_len - 2);
1013
+ pos += scan_len;
1014
+ }
1015
+
1016
+ // Skip whitespace
1017
+ while (char_is_whitespace(source[pos])) {
1018
+ pos++;
1019
+ }
1020
+
1021
+ // Grab attributes, if present
1022
+ attr_len = scan_attributes(&source[pos]);
1023
+
1024
+ if (attr_len) {
1025
+ *attributes = my_strndup(&source[pos], attr_len);
1026
+ }
1027
+ }
1028
+
1029
+
1030
+ /// Create a link from an explicit link `[foo](bar)`
1031
+ link * explicit_link(scratch_pad * scratch, token * bracket, token * paren, const char * source) {
1032
+ char * url_char = NULL;
1033
+ char * title_char = NULL;
1034
+ char * attr_char = NULL;
1035
+ link * l = NULL;
1036
+
1037
+ extract_from_paren(paren, source, &url_char, &title_char, &attr_char);
1038
+
1039
+ if (attr_char) {
1040
+ if (!(scratch->extensions & EXT_COMPATIBILITY)) {
1041
+ l = link_new(source, NULL, url_char, title_char, attr_char);
1042
+ }
1043
+ } else {
1044
+ l = link_new(source, NULL, url_char, title_char, attr_char);
1045
+ }
1046
+
1047
+ free(url_char);
1048
+ free(title_char);
1049
+ free(attr_char);
1050
+
1051
+ return l;
1052
+ }
1053
+
1054
+
1055
+ footnote * footnote_new(const char * source, token * label, token * content, bool lowercase) {
1056
+ footnote * f = malloc(sizeof(footnote));
1057
+
1058
+ if (f) {
1059
+ f->label = label;
1060
+ f->clean_text = (label == NULL) ? NULL : clean_inside_pair(source, label, lowercase);
1061
+ f->label_text = (label == NULL) ? NULL : label_from_token(source, label);
1062
+ f->free_para = false;
1063
+ f->count = -1;
1064
+
1065
+ if (content) {
1066
+ switch (content->type) {
1067
+ case BLOCK_PARA:
1068
+ f->content = content;
1069
+ break;
1070
+
1071
+ case TEXT_PLAIN:
1072
+ token_trim_leading_whitespace(content, source);
1073
+
1074
+ default:
1075
+ f->content = token_new_parent(content, BLOCK_PARA);
1076
+ f->free_para = true;
1077
+ break;
1078
+ }
1079
+ } else {
1080
+ f->content = NULL;
1081
+ }
1082
+ }
1083
+
1084
+ return f;
1085
+ }
1086
+
1087
+
1088
+ void footnote_free(footnote * f) {
1089
+ if (f) {
1090
+ if (f->free_para) {
1091
+ #ifdef kUseObjectPool
1092
+ // Nothing to do here
1093
+ #else
1094
+ free(f->content);
1095
+ #endif
1096
+ }
1097
+
1098
+ free(f->clean_text);
1099
+ free(f->label_text);
1100
+
1101
+ free(f);
1102
+ }
1103
+ }
1104
+
1105
+
1106
+ meta * meta_new(const char * source, size_t key_start, size_t len) {
1107
+ meta * m = malloc(sizeof(meta));
1108
+ char * key;
1109
+
1110
+ if (m) {
1111
+ key = my_strndup(&source[key_start], len);
1112
+ m->key = label_from_string(key);
1113
+ free(key);
1114
+ m->value = NULL;
1115
+ m->start = key_start;
1116
+ }
1117
+
1118
+ return m;
1119
+ }
1120
+
1121
+
1122
+ void meta_set_value(meta * m, const char * value) {
1123
+ if (value) {
1124
+ if (m->value) {
1125
+ free(m->value);
1126
+ }
1127
+
1128
+ m->value = clean_string(value, false);
1129
+ }
1130
+ }
1131
+
1132
+
1133
+ void meta_free(meta * m) {
1134
+ free(m->key);
1135
+ free(m->value);
1136
+
1137
+ free(m);
1138
+ }
1139
+
1140
+
1141
+ /// Find metadata based on key
1142
+ meta * extract_meta_from_stack(scratch_pad * scratch, const char * target) {
1143
+ char * key = clean_string(target, true);
1144
+
1145
+ meta * temp = NULL;
1146
+
1147
+ HASH_FIND_STR(scratch->meta_hash, key, temp);
1148
+
1149
+ free(key);
1150
+
1151
+ return temp;
1152
+ }
1153
+
1154
+
1155
+ char * extract_metadata(scratch_pad * scratch, const char * target) {
1156
+ char * clean = label_from_string(target);
1157
+
1158
+ meta * m = extract_meta_from_stack(scratch, clean);
1159
+ free(clean);
1160
+
1161
+ if (m) {
1162
+ return m->value;
1163
+ }
1164
+
1165
+ return NULL;
1166
+ }
1167
+
1168
+
1169
+ abbr * abbr_new(const char * source, token * label, token * content) {
1170
+ abbr * a = malloc(sizeof(abbr));
1171
+
1172
+ if (a) {
1173
+ a->abbr = text_inside_pair(source, label);
1174
+ a->abbr_len = strlen(a->abbr);
1175
+ a->expansion = clean_string_from_range(source, content->start, content->len, false);
1176
+ a->expansion_len = strlen(a->expansion);
1177
+ }
1178
+
1179
+ return a;
1180
+ }
1181
+
1182
+ void abbreviation_free(abbr * a) {
1183
+ if (a) {
1184
+ free(a->abbr);
1185
+ free(a->expansion);
1186
+ free(a);
1187
+ }
1188
+ }
1189
+
1190
+
1191
+ bool definition_extract(mmd_engine * e, token ** remainder) {
1192
+ char * source = e->dstr->str;
1193
+ token * label = NULL;
1194
+ token * title = NULL;
1195
+ char * url_char = NULL;
1196
+ char * title_char = NULL;
1197
+ char * attr_char = NULL;
1198
+ token * temp = NULL;
1199
+ size_t attr_len;
1200
+
1201
+ link * l = NULL;
1202
+ footnote * f = NULL;
1203
+
1204
+ // Store label
1205
+ label = *remainder;
1206
+
1207
+ *remainder = (*remainder)->next;
1208
+
1209
+ // Prepare for parsing
1210
+
1211
+ // Account for settings
1212
+
1213
+ switch (label->type) {
1214
+ case PAIR_BRACKET_CITATION:
1215
+ case PAIR_BRACKET_FOOTNOTE:
1216
+ case PAIR_BRACKET_GLOSSARY:
1217
+ if (e->extensions & EXT_NOTES) {
1218
+ if (!token_chain_accept(remainder, COLON)) {
1219
+ return false;
1220
+ }
1221
+
1222
+ title = *remainder; // Track first token of content in 'title'
1223
+
1224
+ // Store for later use
1225
+ switch (label->type) {
1226
+ case PAIR_BRACKET_CITATION:
1227
+ f = footnote_new(e->dstr->str, label, title, true);
1228
+ stack_push(e->citation_stack, f);
1229
+ break;
1230
+
1231
+ case PAIR_BRACKET_FOOTNOTE:
1232
+ f = footnote_new(e->dstr->str, label, title, true);
1233
+ stack_push(e->footnote_stack, f);
1234
+ break;
1235
+
1236
+ case PAIR_BRACKET_GLOSSARY:
1237
+ f = footnote_new(e->dstr->str, label, title, false);
1238
+ stack_push(e->glossary_stack, f);
1239
+ break;
1240
+ }
1241
+
1242
+ break;
1243
+ }
1244
+
1245
+ case PAIR_BRACKET:
1246
+
1247
+ // Reference Link Definition
1248
+
1249
+ if (!token_chain_accept(remainder, COLON)) {
1250
+ return false;
1251
+ }
1252
+
1253
+ // Skip space
1254
+ whitespace_accept(remainder);
1255
+
1256
+ // Grab destination
1257
+ url_char = destination_accept(e->dstr->str, remainder, false);
1258
+
1259
+ whitespace_accept(remainder);
1260
+
1261
+ // Grab title, if present
1262
+ temp = *remainder;
1263
+
1264
+ title = token_chain_accept_multiple(remainder, 2, PAIR_QUOTE_DOUBLE, PAIR_QUOTE_SINGLE);
1265
+
1266
+ if (!title) {
1267
+ // See if there's a title on next line
1268
+ whitespace_accept(remainder);
1269
+ token_chain_accept_multiple(remainder, 2, TEXT_NL, TEXT_LINEBREAK);
1270
+ whitespace_accept(remainder);
1271
+
1272
+ title = token_chain_accept_multiple(remainder, 2, PAIR_QUOTE_DOUBLE, PAIR_QUOTE_SINGLE);
1273
+
1274
+ if (!title) {
1275
+ *remainder = temp;
1276
+ }
1277
+ }
1278
+
1279
+ title_char = text_inside_pair(e->dstr->str, title);
1280
+
1281
+ // Get attributes
1282
+ if ((*remainder) && (((*remainder)->type != TEXT_NL) && ((*remainder)->type != TEXT_LINEBREAK))) {
1283
+ if (!(e->extensions & EXT_COMPATIBILITY)) {
1284
+ attr_len = scan_attributes(&source[(*remainder)->start]);
1285
+
1286
+ if (attr_len) {
1287
+ attr_char = my_strndup(&source[(*remainder)->start], attr_len);
1288
+
1289
+ // Skip forward
1290
+ attr_len += (*remainder)->start;
1291
+
1292
+ while ((*remainder) && (*remainder)->start < attr_len) {
1293
+ *remainder = (*remainder)->next;
1294
+ }
1295
+ }
1296
+
1297
+ l = link_new(e->dstr->str, label, url_char, title_char, attr_char);
1298
+ } else {
1299
+ // Not valid match
1300
+ }
1301
+ } else {
1302
+ l = link_new(e->dstr->str, label, url_char, title_char, attr_char);
1303
+ }
1304
+
1305
+ // Store link for later use
1306
+ if (l) {
1307
+ stack_push(e->link_stack, l);
1308
+ }
1309
+
1310
+ break;
1311
+
1312
+ case PAIR_BRACKET_VARIABLE:
1313
+ fprintf(stderr, "Process variable:\n");
1314
+ token_describe(label, e->dstr->str);
1315
+ break;
1316
+
1317
+ default:
1318
+ // Rest of block is not definitions (or has already been processed)
1319
+ return false;
1320
+ }
1321
+
1322
+ // Advance to next line
1323
+ token_skip_until_type_multiple(remainder, 2, TEXT_NL, TEXT_LINEBREAK);
1324
+
1325
+ if (*remainder) {
1326
+ *remainder = (*remainder)->next;
1327
+ }
1328
+
1329
+ // Clean up
1330
+ free(url_char);
1331
+ free(title_char);
1332
+ free(attr_char);
1333
+
1334
+ return true;
1335
+ }
1336
+
1337
+
1338
+ void process_definition_block(mmd_engine * e, token * block) {
1339
+ footnote * f;
1340
+
1341
+ token * label = block->child;
1342
+
1343
+ if (label->type == BLOCK_PARA) {
1344
+ label = label->child;
1345
+ }
1346
+
1347
+ switch (block->type) {
1348
+ case BLOCK_DEF_ABBREVIATION:
1349
+ case BLOCK_DEF_CITATION:
1350
+ case BLOCK_DEF_FOOTNOTE:
1351
+ case BLOCK_DEF_GLOSSARY:
1352
+ switch (block->type) {
1353
+ case BLOCK_DEF_ABBREVIATION:
1354
+ // Strip leading '>'' from term
1355
+ f = footnote_new(e->dstr->str, label, block->child, false);
1356
+
1357
+ if (f && f->clean_text) {
1358
+ memmove(f->clean_text, &(f->clean_text)[1], strlen(f->clean_text));
1359
+
1360
+ while (char_is_whitespace((f->clean_text)[0])) {
1361
+ memmove(f->clean_text, &(f->clean_text)[1], strlen(f->clean_text));
1362
+ }
1363
+ }
1364
+
1365
+ // Adjust the properties
1366
+ free(f->label_text);
1367
+ f->label_text = f->clean_text;
1368
+
1369
+ if (f->content->child &&
1370
+ f->content->child->next &&
1371
+ f->content->child->next->next) {
1372
+ f->clean_text = clean_string_from_range(e->dstr->str, f->content->child->next->next->start, block->start + block->len - f->content->child->next->next->start, false);
1373
+ } else {
1374
+ f->clean_text = NULL;
1375
+ }
1376
+
1377
+ stack_push(e->abbreviation_stack, f);
1378
+ break;
1379
+
1380
+ case BLOCK_DEF_CITATION:
1381
+ f = footnote_new(e->dstr->str, label, block->child, true);
1382
+ stack_push(e->citation_stack, f);
1383
+ break;
1384
+
1385
+ case BLOCK_DEF_FOOTNOTE:
1386
+ f = footnote_new(e->dstr->str, label, block->child, true);
1387
+ stack_push(e->footnote_stack, f);
1388
+ break;
1389
+
1390
+ case BLOCK_DEF_GLOSSARY:
1391
+ // Strip leading '?' from term
1392
+ f = footnote_new(e->dstr->str, label, block->child, false);
1393
+
1394
+ if (f && f->clean_text) {
1395
+ memmove(f->clean_text, &(f->clean_text)[1], strlen(f->clean_text));
1396
+ }
1397
+
1398
+ //if (f && f->label_text)
1399
+ // memmove(f->label_text, &(f->label_text)[1],strlen(f->label_text));
1400
+
1401
+ stack_push(e->glossary_stack, f);
1402
+ break;
1403
+ }
1404
+
1405
+ label->type = TEXT_EMPTY;
1406
+
1407
+ if (label->next) {
1408
+ label->next->type = TEXT_EMPTY;
1409
+ }
1410
+
1411
+ strip_leading_whitespace(label, e->dstr->str);
1412
+ break;
1413
+
1414
+ case BLOCK_DEF_LINK:
1415
+ definition_extract(e, &(label));
1416
+ break;
1417
+
1418
+ default:
1419
+ fprintf(stderr, "process %d\n", block->type);
1420
+ }
1421
+
1422
+ block->type = BLOCK_EMPTY;
1423
+ }
1424
+
1425
+
1426
+ void process_definition_stack(mmd_engine * e) {
1427
+ for (int i = 0; i < e->definition_stack->size; ++i) {
1428
+ process_definition_block(e, stack_peek_index(e->definition_stack, i));
1429
+ }
1430
+ }
1431
+
1432
+ token * manual_label_from_header(token * h, const char * source) {
1433
+ if (!h || !h->child) {
1434
+ return NULL;
1435
+ }
1436
+
1437
+ token * walker = h->child->tail;
1438
+ token * label = NULL;
1439
+ short count = 0;
1440
+
1441
+ while (walker) {
1442
+ switch (walker->type) {
1443
+ case MANUAL_LABEL:
1444
+ // Already identified
1445
+ label = walker;
1446
+ walker = NULL;
1447
+ break;
1448
+
1449
+ case INDENT_TAB:
1450
+ case INDENT_SPACE:
1451
+ case NON_INDENT_SPACE:
1452
+ case TEXT_NL:
1453
+ case TEXT_LINEBREAK:
1454
+ case TEXT_EMPTY:
1455
+ case MARKER_H1:
1456
+ case MARKER_H2:
1457
+ case MARKER_H3:
1458
+ case MARKER_H4:
1459
+ case MARKER_H5:
1460
+ case MARKER_H6:
1461
+ walker = walker->prev;
1462
+ break;
1463
+
1464
+ case TEXT_PLAIN:
1465
+ if (walker->len == 1) {
1466
+ if (source[walker->start] == ' ') {
1467
+ walker = walker->prev;
1468
+ break;
1469
+ }
1470
+ }
1471
+
1472
+ walker = NULL;
1473
+ break;
1474
+
1475
+ case PAIR_BRACKET:
1476
+ label = walker;
1477
+
1478
+ while (walker && walker->type == PAIR_BRACKET) {
1479
+ walker = walker->prev;
1480
+ count++;
1481
+ }
1482
+
1483
+ if (count % 2 == 0) {
1484
+ // Even count
1485
+ label = NULL;
1486
+ } else {
1487
+ // Odd count
1488
+ label->type = MANUAL_LABEL;
1489
+ }
1490
+
1491
+ default:
1492
+ walker = NULL;
1493
+ }
1494
+ }
1495
+
1496
+ return label;
1497
+ }
1498
+
1499
+
1500
+ void process_header_to_links(mmd_engine * e, token * h) {
1501
+ char * label;
1502
+
1503
+ // See if we have a manual label
1504
+ token * manual = manual_label_from_header(h, e->dstr->str);
1505
+
1506
+ if (manual) {
1507
+ label = label_from_token(e->dstr->str, manual);
1508
+ h = manual;
1509
+ } else {
1510
+ label = label_from_token(e->dstr->str, h);
1511
+ }
1512
+
1513
+ DString * url = d_string_new("#");
1514
+
1515
+ d_string_append(url, label);
1516
+
1517
+ link * l = link_new(e->dstr->str, h, url->str, NULL, NULL);
1518
+
1519
+ // Store link for later use
1520
+ stack_push(e->link_stack, l);
1521
+
1522
+ d_string_free(url, true);
1523
+ free(label);
1524
+ }
1525
+
1526
+
1527
+ void process_header_stack(mmd_engine * e) {
1528
+ // NTD in compatibility mode or if disabled
1529
+ if (e->extensions & EXT_NO_LABELS) {
1530
+ return;
1531
+ }
1532
+
1533
+ for (int i = 0; i < e->header_stack->size; ++i) {
1534
+ process_header_to_links(e, stack_peek_index(e->header_stack, i));
1535
+ }
1536
+ }
1537
+
1538
+
1539
+ void process_table_to_link(mmd_engine * e, token * t) {
1540
+ // Is there a caption
1541
+ if (table_has_caption(t)) {
1542
+ token * temp_token = t->next->child;
1543
+
1544
+ if (temp_token->next &&
1545
+ temp_token->next->type == PAIR_BRACKET) {
1546
+ temp_token = temp_token->next;
1547
+ }
1548
+
1549
+ char * label = label_from_token(e->dstr->str, temp_token);
1550
+
1551
+ DString * url = d_string_new("#");
1552
+ d_string_append(url, label);
1553
+
1554
+ link * l = link_new(e->dstr->str, temp_token, url->str, NULL, NULL);
1555
+
1556
+ stack_push(e->link_stack, l);
1557
+
1558
+ d_string_free(url, true);
1559
+ free(label);
1560
+ }
1561
+ }
1562
+
1563
+
1564
+ void process_table_stack(mmd_engine * e) {
1565
+ for (int i = 0; i < e->table_stack->size; ++i) {
1566
+ process_table_to_link(e, stack_peek_index(e->table_stack, i));
1567
+ }
1568
+ }
1569
+
1570
+
1571
+ /// Parse metadata
1572
+ void process_metadata_stack(mmd_engine * e, scratch_pad * scratch) {
1573
+ if ((scratch->extensions & EXT_NO_METADATA) ||
1574
+ (scratch->extensions & EXT_COMPATIBILITY)) {
1575
+ return;
1576
+ }
1577
+
1578
+ meta * m;
1579
+ short header_level = -10;
1580
+ char * temp_char = NULL;
1581
+
1582
+ for (int i = 0; i < e->metadata_stack->size; ++i) {
1583
+ // Check for certain metadata keys
1584
+ m = stack_peek_index(e->metadata_stack, i);
1585
+
1586
+ if (strcmp(m->key, "baseheaderlevel") == 0) {
1587
+ if (header_level == -10) {
1588
+ header_level = atoi(m->value);
1589
+ }
1590
+ } else if (strcmp(m->key, "epubheaderlevel") == 0) {
1591
+ if (scratch->output_format == FORMAT_EPUB) {
1592
+ header_level = atoi(m->value);
1593
+ }
1594
+ } else if (strcmp(m->key, "htmlheaderlevel") == 0) {
1595
+ if (scratch->output_format == FORMAT_HTML) {
1596
+ header_level = atoi(m->value);
1597
+ }
1598
+ } else if (strcmp(m->key, "xhtmlheaderlevel") == 0) {
1599
+ if (scratch->output_format == FORMAT_HTML) {
1600
+ header_level = atoi(m->value);
1601
+ }
1602
+ } else if (strcmp(m->key, "latexheaderlevel") == 0) {
1603
+ if ((scratch->output_format == FORMAT_LATEX) ||
1604
+ (scratch->output_format == FORMAT_BEAMER) ||
1605
+ (scratch->output_format == FORMAT_MEMOIR)) {
1606
+ header_level = atoi(m->value);
1607
+ }
1608
+ } else if (strcmp(m->key, "odfheaderlevel") == 0) {
1609
+ if ((scratch->output_format == FORMAT_ODT) ||
1610
+ (scratch->output_format == FORMAT_FODT)) {
1611
+ header_level = atoi(m->value);
1612
+ }
1613
+ } else if (strcmp(m->key, "language") == 0) {
1614
+ temp_char = label_from_string(m->value);
1615
+
1616
+ if (strcmp(temp_char, "de") == 0) {
1617
+ scratch->language = LC_DE;
1618
+ scratch->quotes_lang = GERMAN;
1619
+ } else if (strcmp(temp_char, "es") == 0) {
1620
+ scratch->language = LC_ES;
1621
+ scratch->quotes_lang = ENGLISH;
1622
+ } else if (strcmp(temp_char, "fr") == 0) {
1623
+ scratch->language = LC_FR;
1624
+ scratch->quotes_lang = FRENCH;
1625
+ } else if (strcmp(temp_char, "nl") == 0) {
1626
+ scratch->language = LC_NL;
1627
+ scratch->quotes_lang = DUTCH;
1628
+ } else if (strcmp(temp_char, "sv") == 0) {
1629
+ scratch->language = LC_SV;
1630
+ scratch->quotes_lang = SWEDISH;
1631
+ } else {
1632
+ scratch->language = LC_EN;
1633
+ scratch->quotes_lang = ENGLISH;
1634
+ }
1635
+
1636
+ free(temp_char);
1637
+ } else if (strcmp(m->key, "latexmode") == 0) {
1638
+ if (scratch->output_format == FORMAT_LATEX) {
1639
+ temp_char = label_from_string(m->value);
1640
+
1641
+ if (strcmp(temp_char, "beamer") == 0) {
1642
+ scratch->output_format = FORMAT_BEAMER;
1643
+ } else if (strcmp(temp_char, "memoir") == 0) {
1644
+ scratch->output_format = FORMAT_MEMOIR;
1645
+ }
1646
+
1647
+ free(temp_char);
1648
+ }
1649
+ } else if (strcmp(m->key, "quoteslanguage") == 0) {
1650
+ temp_char = label_from_string(m->value);
1651
+
1652
+ if ((strcmp(temp_char, "dutch") == 0) ||
1653
+ (strcmp(temp_char, "nl") == 0)) {
1654
+ scratch->quotes_lang = DUTCH;
1655
+ } else if ((strcmp(temp_char, "french") == 0) ||
1656
+ (strcmp(temp_char, "fr") == 0)) {
1657
+ scratch->quotes_lang = FRENCH;
1658
+ } else if ((strcmp(temp_char, "german") == 0) ||
1659
+ (strcmp(temp_char, "de") == 0)) {
1660
+ scratch->quotes_lang = GERMAN;
1661
+ } else if (strcmp(temp_char, "germanguillemets") == 0) {
1662
+ scratch->quotes_lang = GERMANGUILL;
1663
+ } else if ((strcmp(temp_char, "swedish") == 0) ||
1664
+ (strcmp(temp_char, "sv") == 0)) {
1665
+ scratch->quotes_lang = SWEDISH;
1666
+ } else {
1667
+ scratch->quotes_lang = ENGLISH;
1668
+ }
1669
+
1670
+ free(temp_char);
1671
+ } else if (strcmp(m->key, "bibtex") == 0) {
1672
+ scratch->bibtex_file = my_strdup(m->value);
1673
+
1674
+ // Trigger complete document unless explicitly denied
1675
+ if (!(scratch->extensions & EXT_SNIPPET)) {
1676
+ scratch->extensions |= EXT_COMPLETE;
1677
+ }
1678
+ } else {
1679
+ // Any other key triggers complete document
1680
+ if (!(scratch->extensions & EXT_SNIPPET)) {
1681
+ scratch->extensions |= EXT_COMPLETE;
1682
+ }
1683
+ }
1684
+
1685
+ }
1686
+
1687
+ if (header_level != -10) {
1688
+ scratch->base_header_level = header_level;
1689
+ }
1690
+ }
1691
+
1692
+
1693
+ void automatic_search_text(mmd_engine * e, token * t, trie * ac) {
1694
+ match * m = ac_trie_leftmost_longest_search(ac, e->dstr->str, t->start, t->len);
1695
+
1696
+ match * walker;
1697
+
1698
+ token * tok = t;
1699
+
1700
+ if (m) {
1701
+ walker = m->next;
1702
+
1703
+ while (walker) {
1704
+ token_split(tok, walker->start, walker->len, walker->match_type);
1705
+
1706
+ // Advance token to next token
1707
+ while (tok && (tok->start < walker->start + walker->len)) {
1708
+ tok = tok->next;
1709
+ }
1710
+
1711
+ // Advance to next match (if present)
1712
+ walker = walker->next;
1713
+ }
1714
+ }
1715
+
1716
+ match_free(m);
1717
+ }
1718
+
1719
+
1720
+ /// Determine which nodes to descend into to search for abbreviations
1721
+ void automatic_search(mmd_engine * e, token * t, trie * ac) {
1722
+ while (t) {
1723
+ switch (t->type) {
1724
+ case TEXT_PLAIN:
1725
+ automatic_search_text(e, t, ac);
1726
+ break;
1727
+
1728
+ case DOC_START_TOKEN:
1729
+ case BLOCK_BLOCKQUOTE:
1730
+ case BLOCK_DEFINITION:
1731
+ case BLOCK_DEFLIST:
1732
+ case BLOCK_LIST_BULLETED:
1733
+ case BLOCK_LIST_BULLETED_LOOSE:
1734
+ case BLOCK_LIST_ENUMERATED:
1735
+ case BLOCK_LIST_ENUMERATED_LOOSE:
1736
+ case BLOCK_LIST_ITEM_TIGHT:
1737
+ case BLOCK_LIST_ITEM:
1738
+ case BLOCK_PARA:
1739
+ case BLOCK_TABLE:
1740
+ case BLOCK_TABLE_HEADER:
1741
+ case BLOCK_TABLE_SECTION:
1742
+ case BLOCK_TERM:
1743
+ case LINE_LIST_BULLETED:
1744
+ case LINE_LIST_ENUMERATED:
1745
+ case PAIR_BRACKET:
1746
+ case PAIR_BRACKET_FOOTNOTE:
1747
+ case PAIR_BRACKET_GLOSSARY:
1748
+ case PAIR_BRACKET_IMAGE:
1749
+ case PAIR_QUOTE_DOUBLE:
1750
+ case PAIR_QUOTE_SINGLE:
1751
+ case PAIR_STAR:
1752
+ case PAIR_UL:
1753
+ case TABLE_CELL:
1754
+ case TABLE_ROW:
1755
+ automatic_search(e, t->child, ac);
1756
+ break;
1757
+
1758
+ // case PAIR_PAREN:
1759
+ default:
1760
+ break;
1761
+ }
1762
+
1763
+ t = t->next;
1764
+ }
1765
+ }
1766
+
1767
+
1768
+ void identify_global_search_terms(mmd_engine * e, scratch_pad * scratch) {
1769
+ // Only search if we have a target
1770
+ int count = e->abbreviation_stack->size + e->glossary_stack->size;
1771
+
1772
+ if (count == 0) {
1773
+ return;
1774
+ }
1775
+
1776
+ trie * ac = trie_new(0);
1777
+ footnote * f;
1778
+
1779
+ // Add abbreviations to search trie
1780
+ for (int i = 0; i < e->abbreviation_stack->size; ++i) {
1781
+ f = stack_peek_index(e->abbreviation_stack, i);
1782
+ trie_insert(ac, f->label_text, PAIR_BRACKET_ABBREVIATION);
1783
+ }
1784
+
1785
+ // Add glossary to search trie (without leading '?')
1786
+ for (int i = 0; i < e->glossary_stack->size; ++i) {
1787
+ f = stack_peek_index(e->glossary_stack, i);
1788
+ trie_insert(ac, f->clean_text, PAIR_BRACKET_GLOSSARY);
1789
+ }
1790
+
1791
+ ac_trie_prepare(ac);
1792
+ automatic_search(e, e->root, ac);
1793
+ trie_free(ac);
1794
+ }
1795
+
1796
+
1797
+ void mmd_engine_export_token_tree(DString * out, mmd_engine * e, short format) {
1798
+
1799
+ // Process potential reference definitions
1800
+ process_definition_stack(e);
1801
+
1802
+ // Process headers for potential cross-reference targets
1803
+ process_header_stack(e);
1804
+
1805
+ // Process tables for potential cross-reference targets
1806
+ process_table_stack(e);
1807
+
1808
+ // Create scratch pad
1809
+ scratch_pad * scratch = scratch_pad_new(e, format);
1810
+
1811
+ // Process metadata
1812
+ process_metadata_stack(e, scratch);
1813
+
1814
+ // Process abbreviations, glossary, etc.
1815
+ if (!(e->extensions & EXT_COMPATIBILITY)) {
1816
+ identify_global_search_terms(e, scratch);
1817
+ }
1818
+
1819
+
1820
+ switch (scratch->output_format) {
1821
+ case FORMAT_BEAMER:
1822
+ if (scratch->extensions & EXT_COMPLETE) {
1823
+ mmd_start_complete_latex(out, e->dstr->str, scratch);
1824
+ }
1825
+
1826
+ mmd_export_token_tree_beamer(out, e->dstr->str, e->root, scratch);
1827
+
1828
+ mmd_outline_add_beamer(out, NULL, scratch);
1829
+
1830
+ mmd_export_citation_list_beamer(out, e->dstr->str, scratch);
1831
+
1832
+ if (scratch->extensions & EXT_COMPLETE) {
1833
+ mmd_end_complete_beamer(out, e->dstr->str, scratch);
1834
+ }
1835
+
1836
+ break;
1837
+
1838
+ case FORMAT_EPUB:
1839
+ case FORMAT_TEXTBUNDLE:
1840
+ case FORMAT_TEXTBUNDLE_COMPRESSED:
1841
+ scratch->store_assets = true;
1842
+
1843
+ mmd_start_complete_html(out, e->dstr->str, scratch);
1844
+
1845
+ mmd_export_token_tree_html(out, e->dstr->str, e->root, scratch);
1846
+ mmd_export_footnote_list_html(out, e->dstr->str, scratch);
1847
+ mmd_export_glossary_list_html(out, e->dstr->str, scratch);
1848
+ mmd_export_citation_list_html(out, e->dstr->str, scratch);
1849
+
1850
+ mmd_end_complete_html(out, e->dstr->str, scratch);
1851
+
1852
+ break;
1853
+
1854
+ case FORMAT_HTML:
1855
+ if (scratch->extensions & EXT_COMPLETE) {
1856
+ mmd_start_complete_html(out, e->dstr->str, scratch);
1857
+ }
1858
+
1859
+ mmd_export_token_tree_html(out, e->dstr->str, e->root, scratch);
1860
+ mmd_export_footnote_list_html(out, e->dstr->str, scratch);
1861
+ mmd_export_glossary_list_html(out, e->dstr->str, scratch);
1862
+ mmd_export_citation_list_html(out, e->dstr->str, scratch);
1863
+
1864
+ if (scratch->extensions & EXT_COMPLETE) {
1865
+ mmd_end_complete_html(out, e->dstr->str, scratch);
1866
+ }
1867
+
1868
+ break;
1869
+
1870
+ case FORMAT_LATEX:
1871
+ if (scratch->extensions & EXT_COMPLETE) {
1872
+ mmd_start_complete_latex(out, e->dstr->str, scratch);
1873
+ }
1874
+
1875
+ mmd_export_token_tree_latex(out, e->dstr->str, e->root, scratch);
1876
+ mmd_export_citation_list_latex(out, e->dstr->str, scratch);
1877
+
1878
+ if (scratch->extensions & EXT_COMPLETE) {
1879
+ mmd_end_complete_latex(out, e->dstr->str, scratch);
1880
+ }
1881
+
1882
+ break;
1883
+
1884
+ case FORMAT_MEMOIR:
1885
+ if (scratch->extensions & EXT_COMPLETE) {
1886
+ mmd_start_complete_latex(out, e->dstr->str, scratch);
1887
+ }
1888
+
1889
+ mmd_export_token_tree_memoir(out, e->dstr->str, e->root, scratch);
1890
+ mmd_export_citation_list_latex(out, e->dstr->str, scratch);
1891
+
1892
+ if (scratch->extensions & EXT_COMPLETE) {
1893
+ mmd_end_complete_latex(out, e->dstr->str, scratch);
1894
+ }
1895
+
1896
+ break;
1897
+
1898
+ case FORMAT_ODT:
1899
+ scratch->store_assets = true;
1900
+
1901
+ case FORMAT_FODT:
1902
+ // mmd_start_complete_odf(out, e->dstr->str, scratch);
1903
+
1904
+ mmd_export_token_tree_opendocument(out, e->dstr->str, e->root, scratch);
1905
+
1906
+ // mmd_end_complete_odf(out, e->dstr->str, scratch);
1907
+ break;
1908
+ }
1909
+
1910
+ // Preserve asset_hash for possible use in export
1911
+ e->asset_hash = scratch->asset_hash;
1912
+
1913
+ scratch_pad_free(scratch);
1914
+ }
1915
+
1916
+
1917
+ void parse_brackets(const char * source, scratch_pad * scratch, token * bracket, link ** final_link, short * skip_token, bool * free_link) {
1918
+ link * temp_link = NULL;
1919
+ char * temp_char = NULL;
1920
+ short temp_short = 0;
1921
+
1922
+ // What is next?
1923
+ token * next = bracket->next;
1924
+
1925
+ if (next) {
1926
+ temp_short = 1;
1927
+ }
1928
+
1929
+ // Do not free this link after using it
1930
+ *free_link = false;
1931
+
1932
+ if (next && next->type == PAIR_PAREN) {
1933
+ // We have `[foo](bar)` or `![foo](bar)`
1934
+
1935
+ temp_link = explicit_link(scratch, bracket, next, source);
1936
+
1937
+ if (temp_link) {
1938
+ // Don't output brackets
1939
+ bracket->child->type = TEXT_EMPTY;
1940
+ bracket->child->mate->type = TEXT_EMPTY;
1941
+
1942
+ // This was an explicit link
1943
+ *final_link = temp_link;
1944
+
1945
+ // Skip over parentheses
1946
+ *skip_token = temp_short;
1947
+
1948
+ // Free this link
1949
+ *free_link = true;
1950
+ return;
1951
+ }
1952
+ }
1953
+
1954
+ if (next && next->type == PAIR_BRACKET) {
1955
+ // Is this a reference link? `[foo][bar]` or `![foo][bar]`
1956
+ temp_char = text_inside_pair(source, next);
1957
+
1958
+ if (temp_char[0] == '\0') {
1959
+ // Empty label, use first bracket (e.g. implicit link `[foo][]`)
1960
+ free(temp_char);
1961
+ temp_char = text_inside_pair(source, bracket);
1962
+ }
1963
+ } else {
1964
+ // This may be a simplified implicit link, e.g. `[foo]`
1965
+
1966
+ // But not if it's nested brackets, since it would not
1967
+ // end up being a valid reference
1968
+ token * walker = bracket->child;
1969
+
1970
+ while (walker) {
1971
+ switch (walker->type) {
1972
+ case PAIR_BRACKET:
1973
+ case PAIR_BRACKET_CITATION:
1974
+ case PAIR_BRACKET_FOOTNOTE:
1975
+ case PAIR_BRACKET_GLOSSARY:
1976
+ case PAIR_BRACKET_VARIABLE:
1977
+ case PAIR_BRACKET_ABBREVIATION:
1978
+ *final_link = NULL;
1979
+ return;
1980
+ }
1981
+
1982
+ walker = walker->next;
1983
+ }
1984
+
1985
+ temp_char = text_inside_pair(source, bracket);
1986
+ // Don't skip tokens
1987
+ temp_short = 0;
1988
+ }
1989
+
1990
+ temp_link = extract_link_from_stack(scratch, temp_char);
1991
+
1992
+ if (temp_char) {
1993
+ free(temp_char);
1994
+ }
1995
+
1996
+ if (temp_link) {
1997
+ // Don't output brackets
1998
+ if (bracket->child) {
1999
+ bracket->child->type = TEXT_EMPTY;
2000
+
2001
+ if (bracket->child->mate) {
2002
+ bracket->child->mate->type = TEXT_EMPTY;
2003
+ }
2004
+ }
2005
+
2006
+ *final_link = temp_link;
2007
+
2008
+ // Skip over second bracket if present
2009
+ *skip_token = temp_short;
2010
+ return;
2011
+ }
2012
+
2013
+ // No existing links, so nothing to do
2014
+ *final_link = NULL;
2015
+ }
2016
+
2017
+
2018
+ void mark_citation_as_used(scratch_pad * scratch, footnote * c) {
2019
+ if (c->count == -1) {
2020
+ // Add citation to used stack
2021
+ stack_push(scratch->used_citations, c);
2022
+
2023
+ // Update counter
2024
+ c->count = scratch->used_citations->size;
2025
+ }
2026
+ }
2027
+
2028
+
2029
+ void mark_footnote_as_used(scratch_pad * scratch, footnote * f) {
2030
+ if (f->count == -1) {
2031
+ // Add footnote to used stack
2032
+ stack_push(scratch->used_footnotes, f);
2033
+
2034
+ // Update counter
2035
+ f->count = scratch->used_footnotes->size;
2036
+ }
2037
+ }
2038
+
2039
+
2040
+ void mark_glossary_as_used(scratch_pad * scratch, footnote * c) {
2041
+ if (c->count == -1) {
2042
+ // Add glossary to used stack
2043
+ stack_push(scratch->used_glossaries, c);
2044
+
2045
+ // Update counter
2046
+ c->count = scratch->used_glossaries->size;
2047
+ }
2048
+ }
2049
+
2050
+
2051
+ void mark_abbreviation_as_used(scratch_pad * scratch, footnote * c) {
2052
+ if (c->count == -1) {
2053
+ // Add abbreviation to used stack
2054
+ stack_push(scratch->used_abbreviations, c);
2055
+
2056
+ // Update counter
2057
+ c->count = scratch->used_abbreviations->size;
2058
+ }
2059
+ }
2060
+
2061
+
2062
+ size_t extract_citation_from_stack(scratch_pad * scratch, const char * target) {
2063
+ char * key = clean_string(target, true);
2064
+
2065
+ fn_holder * h;
2066
+
2067
+ HASH_FIND_STR(scratch->citation_hash, key, h);
2068
+
2069
+ free(key);
2070
+
2071
+ if (h) {
2072
+ mark_citation_as_used(scratch, h->note);
2073
+ return h->note->count;
2074
+ }
2075
+
2076
+ key = label_from_string(target);
2077
+
2078
+ HASH_FIND_STR(scratch->citation_hash, key, h);
2079
+
2080
+ free(key);
2081
+
2082
+ if (h) {
2083
+ mark_citation_as_used(scratch, h->note);
2084
+ return h->note->count;
2085
+ }
2086
+
2087
+ // None found
2088
+ return -1;
2089
+ }
2090
+
2091
+
2092
+ size_t extract_footnote_from_stack(scratch_pad * scratch, const char * target) {
2093
+ char * key = clean_string(target, true);
2094
+
2095
+ fn_holder * h;
2096
+
2097
+ HASH_FIND_STR(scratch->footnote_hash, key, h);
2098
+
2099
+ free(key);
2100
+
2101
+ if (h) {
2102
+ mark_footnote_as_used(scratch, h->note);
2103
+ return h->note->count;
2104
+ }
2105
+
2106
+ key = label_from_string(target);
2107
+
2108
+ HASH_FIND_STR(scratch->footnote_hash, key, h);
2109
+
2110
+ free(key);
2111
+
2112
+ if (h) {
2113
+ mark_footnote_as_used(scratch, h->note);
2114
+ return h->note->count;
2115
+ }
2116
+
2117
+ // None found
2118
+ return -1;
2119
+ }
2120
+
2121
+
2122
+ size_t extract_abbreviation_from_stack(scratch_pad * scratch, const char * target) {
2123
+ char * key = clean_string(target, false);
2124
+
2125
+ fn_holder * h;
2126
+
2127
+ HASH_FIND_STR(scratch->abbreviation_hash, key, h);
2128
+
2129
+ free(key);
2130
+
2131
+ if (h) {
2132
+ mark_abbreviation_as_used(scratch, h->note);
2133
+ return h->note->count;
2134
+ }
2135
+
2136
+ key = label_from_string(target);
2137
+
2138
+ HASH_FIND_STR(scratch->abbreviation_hash, key, h);
2139
+
2140
+ free(key);
2141
+
2142
+ if (h) {
2143
+ mark_abbreviation_as_used(scratch, h->note);
2144
+ return h->note->count;
2145
+ }
2146
+
2147
+ // None found
2148
+ return -1;
2149
+ }
2150
+
2151
+
2152
+ size_t extract_glossary_from_stack(scratch_pad * scratch, const char * target) {
2153
+ char * key = clean_string(target, false);
2154
+
2155
+ fn_holder * h;
2156
+
2157
+ HASH_FIND_STR(scratch->glossary_hash, key, h);
2158
+
2159
+ free(key);
2160
+
2161
+ if (h) {
2162
+ mark_glossary_as_used(scratch, h->note);
2163
+ return h->note->count;
2164
+ }
2165
+
2166
+ key = label_from_string(target);
2167
+
2168
+ HASH_FIND_STR(scratch->glossary_hash, key, h);
2169
+
2170
+ free(key);
2171
+
2172
+ if (h) {
2173
+ mark_glossary_as_used(scratch, h->note);
2174
+ return h->note->count;
2175
+ }
2176
+
2177
+ // None found
2178
+ return -1;
2179
+ }
2180
+
2181
+
2182
+ void footnote_from_bracket(const char * source, scratch_pad * scratch, token * t, short * num) {
2183
+ // Get text inside bracket
2184
+ char * text = text_inside_pair(source, t);
2185
+ short footnote_id = extract_footnote_from_stack(scratch, text);
2186
+
2187
+ free(text);
2188
+
2189
+ if (footnote_id == -1) {
2190
+ // No match, this is an inline footnote -- create a new one
2191
+ t->child->type = TEXT_EMPTY;
2192
+ t->child->mate->type = TEXT_EMPTY;
2193
+
2194
+ // Create footnote
2195
+ footnote * temp = footnote_new(source, NULL, t->child, true);
2196
+
2197
+ // Store as used
2198
+ stack_push(scratch->used_footnotes, temp);
2199
+ *num = scratch->used_footnotes->size;
2200
+ temp->count = *num;
2201
+
2202
+ // We need to free this one later since it doesn't exist
2203
+ // in the engine's stack, on the scratch_pad stack
2204
+ stack_push(scratch->inline_footnotes_to_free, temp);
2205
+ } else {
2206
+ // Footnote in stack
2207
+ *num = footnote_id;
2208
+ }
2209
+ }
2210
+
2211
+
2212
+ void citation_from_bracket(const char * source, scratch_pad * scratch, token * t, short * num) {
2213
+ // Get text inside bracket
2214
+ char * text = text_inside_pair(source, t);
2215
+ short citation_id = extract_citation_from_stack(scratch, text);
2216
+
2217
+ free(text);
2218
+
2219
+ if (citation_id == -1) {
2220
+ // No match, this is an inline citation -- create a new one
2221
+
2222
+ t->child->type = TEXT_EMPTY;
2223
+ t->child->mate->type = TEXT_EMPTY;
2224
+
2225
+ // *UNLESS* we are using BibTeX, in which case we leave them alone
2226
+ if (scratch->bibtex_file) {
2227
+ *num = -1;
2228
+ return;
2229
+ }
2230
+
2231
+ // Create citation
2232
+ footnote * temp = footnote_new(source, t, t->child, true);
2233
+
2234
+ // Store as used
2235
+ stack_push(scratch->used_citations, temp);
2236
+ *num = scratch->used_citations->size;
2237
+ temp->count = *num;
2238
+
2239
+ // We need to free this one later since it doesn't exist
2240
+ // in the engine's stack, on the scratch_pad stack
2241
+ stack_push(scratch->inline_citations_to_free, temp);
2242
+ } else {
2243
+ // Citation in stack
2244
+ *num = citation_id;
2245
+ }
2246
+ }
2247
+
2248
+
2249
+ void glossary_from_bracket(const char * source, scratch_pad * scratch, token * t, short * num) {
2250
+ // Get text inside bracket
2251
+ char * text;
2252
+
2253
+ if (t->child) {
2254
+ text = text_inside_pair(source, t);
2255
+ memmove(text, &text[1], strlen(text));
2256
+ } else {
2257
+ text = malloc(t->len + 1);
2258
+ memcpy(text, &source[t->start], t->len);
2259
+ text[t->len] = '\0';
2260
+ }
2261
+
2262
+ short glossary_id = extract_glossary_from_stack(scratch, text);
2263
+
2264
+ free(text);
2265
+
2266
+ if (glossary_id == -1) {
2267
+ // No match, this is an inline glossary -- create a new glossary entry
2268
+ if (t->child) {
2269
+ t->child->type = TEXT_EMPTY;
2270
+ t->child->mate->type = TEXT_EMPTY;
2271
+ }
2272
+
2273
+ // Create glossary
2274
+ token * label = t->child;
2275
+
2276
+ while (label && label->type != PAIR_PAREN) {
2277
+ label = label->next;
2278
+ }
2279
+
2280
+ if (label) {
2281
+ footnote * temp = footnote_new(source, label, label->next, false);
2282
+
2283
+ // Store as used
2284
+ stack_push(scratch->used_glossaries, temp);
2285
+ *num = scratch->used_glossaries->size;
2286
+ temp->count = *num;
2287
+
2288
+ // We need to free this one later since it doesn't exist
2289
+ // in the engine's stack, on the scratch_pad stack
2290
+ stack_push(scratch->inline_glossaries_to_free, temp);
2291
+ } else {
2292
+ // Improperly formatted glossary
2293
+ *num = -1;
2294
+ }
2295
+ } else {
2296
+ // Glossary in stack
2297
+ *num = glossary_id;
2298
+ }
2299
+ }
2300
+
2301
+
2302
+ void abbreviation_from_bracket(const char * source, scratch_pad * scratch, token * t, short * num) {
2303
+ // Get text inside bracket
2304
+ char * text;
2305
+
2306
+ if (t->child) {
2307
+ text = text_inside_pair(source, t);
2308
+ } else {
2309
+ text = malloc(t->len + 2);
2310
+ text[0] = '>';
2311
+ memcpy(&text[1], &source[t->start], t->len);
2312
+ text[t->len + 1] = '\0';
2313
+ }
2314
+
2315
+ short abbr_id = extract_abbreviation_from_stack(scratch, &text[1]);
2316
+
2317
+ free(text);
2318
+
2319
+ if (abbr_id == -1) {
2320
+ // No match, this is an inline glossary -- create a new glossary entry
2321
+ if (t->child) {
2322
+ t->child->type = TEXT_EMPTY;
2323
+ t->child->mate->type = TEXT_EMPTY;
2324
+ }
2325
+
2326
+ // Create glossary
2327
+ token * label = t->child;
2328
+
2329
+ while (label && label->type != PAIR_PAREN) {
2330
+ label = label->next;
2331
+ }
2332
+
2333
+ if (label) {
2334
+ footnote * temp = footnote_new(source, label, label->next, false);
2335
+
2336
+ // Adjust the properties
2337
+ free(temp->label_text);
2338
+ temp->label_text = temp->clean_text;
2339
+
2340
+ if (temp->content && temp->content->child) {
2341
+ temp->clean_text = clean_string_from_range(source, temp->content->child->start, t->start + t->len - t->child->mate->len - temp->content->child->start, false);
2342
+ }
2343
+
2344
+ // Store as used
2345
+ stack_push(scratch->used_abbreviations, temp);
2346
+ *num = scratch->used_abbreviations->size;
2347
+ temp->count = *num;
2348
+
2349
+ // We need to free this one later since it doesn't exist
2350
+ // in the engine's stack, on the scratch_pad stack
2351
+ stack_push(scratch->inline_abbreviations_to_free, temp);
2352
+ } else {
2353
+ // Improperly formatted glossary
2354
+ *num = -1;
2355
+ }
2356
+ } else {
2357
+ // Glossary in stack
2358
+ *num = abbr_id;
2359
+ }
2360
+ }
2361
+
2362
+
2363
+ void read_table_column_alignments(const char * source, token * table, scratch_pad * scratch) {
2364
+ token * walker = table->child->child;
2365
+
2366
+ scratch->table_alignment[0] = '\0';
2367
+ scratch->table_column_count = 0;
2368
+
2369
+ if (walker == NULL) {
2370
+ return;
2371
+ }
2372
+
2373
+ // Find the separator line
2374
+ while (walker->next) {
2375
+ walker = walker->next;
2376
+ }
2377
+
2378
+ walker->type = TEXT_EMPTY;
2379
+
2380
+ // Iterate through cells to create alignment string
2381
+ short counter = 0;
2382
+ short align = 0;
2383
+
2384
+ walker = walker->child;
2385
+
2386
+ while (walker) {
2387
+ switch (walker->type) {
2388
+ case TABLE_CELL:
2389
+ align = scan_alignment_string(&source[walker->start]);
2390
+
2391
+ switch (align) {
2392
+ case ALIGN_LEFT:
2393
+ scratch->table_alignment[counter] = 'l';
2394
+ break;
2395
+
2396
+ case ALIGN_RIGHT:
2397
+ scratch->table_alignment[counter] = 'r';
2398
+ break;
2399
+
2400
+ case ALIGN_CENTER:
2401
+ scratch->table_alignment[counter] = 'c';
2402
+ break;
2403
+
2404
+ case ALIGN_LEFT | ALIGN_WRAP:
2405
+ scratch->table_alignment[counter] = 'L';
2406
+ break;
2407
+
2408
+ case ALIGN_RIGHT | ALIGN_WRAP:
2409
+ scratch->table_alignment[counter] = 'R';
2410
+ break;
2411
+
2412
+ case ALIGN_CENTER | ALIGN_WRAP:
2413
+ scratch->table_alignment[counter] = 'C';
2414
+ break;
2415
+
2416
+ case ALIGN_WRAP:
2417
+ scratch->table_alignment[counter] = 'N';
2418
+ break;
2419
+
2420
+ default:
2421
+ scratch->table_alignment[counter] = 'n';
2422
+ }
2423
+
2424
+ counter++;
2425
+ break;
2426
+ }
2427
+
2428
+ walker = walker->next;
2429
+ }
2430
+
2431
+ scratch->table_alignment[counter] = '\0';
2432
+ scratch->table_column_count = counter;
2433
+ }
2434
+
2435
+
2436
+ void strip_leading_whitespace(token * chain, const char * source) {
2437
+ while (chain) {
2438
+ switch (chain->type) {
2439
+ case INDENT_TAB:
2440
+ case INDENT_SPACE:
2441
+ case NON_INDENT_SPACE:
2442
+ chain->type = TEXT_EMPTY;
2443
+
2444
+ case TEXT_EMPTY:
2445
+ chain = chain->next;
2446
+ break;
2447
+
2448
+ case TEXT_PLAIN:
2449
+ token_trim_leading_whitespace(chain, source);
2450
+
2451
+ default:
2452
+ return;
2453
+ }
2454
+
2455
+ if (chain) {
2456
+ chain = chain->next;
2457
+ }
2458
+ }
2459
+ }
2460
+
2461
+
2462
+ bool table_has_caption(token * t) {
2463
+
2464
+ if (t->next && t->next->type == BLOCK_PARA) {
2465
+ t = t->next->child;
2466
+
2467
+ if (t->type == PAIR_BRACKET) {
2468
+ t = t->next;
2469
+
2470
+ if (t && t->next &&
2471
+ t->next->type == PAIR_BRACKET) {
2472
+ t = t->next;
2473
+ }
2474
+
2475
+ if (t && t->next &&
2476
+ ((t->next->type == TEXT_NL) ||
2477
+ (t->next->type == TEXT_LINEBREAK))) {
2478
+ t = t->next;
2479
+ }
2480
+
2481
+ if (t && t->next == NULL) {
2482
+ return true;
2483
+ }
2484
+ }
2485
+ }
2486
+
2487
+ return false;
2488
+ }
2489
+
2490
+
2491
+ /// Grab the first "word" after the end of the fence marker:
2492
+ /// ````perl
2493
+ /// or
2494
+ /// ```` perl
2495
+ char * get_fence_language_specifier(token * fence, const char * source) {
2496
+ char * result = NULL;
2497
+ size_t start = fence->start + fence->len;
2498
+ size_t len = 0;
2499
+
2500
+ while (char_is_whitespace(source[start])) {
2501
+ start++;
2502
+ }
2503
+
2504
+ while (!char_is_whitespace_or_line_ending(source[start + len])) {
2505
+ len++;
2506
+ }
2507
+
2508
+ if (len) {
2509
+ result = my_strndup(&source[start], len);
2510
+ }
2511
+
2512
+ return result;
2513
+ }
2514
+
2515
+
2516
+ short raw_level_for_header(token * header) {
2517
+ switch (header->type) {
2518
+ case BLOCK_H1:
2519
+ case BLOCK_SETEXT_1:
2520
+ return 1;
2521
+
2522
+ case BLOCK_H2:
2523
+ case BLOCK_SETEXT_2:
2524
+ return 2;
2525
+
2526
+ case BLOCK_H3:
2527
+ return 3;
2528
+
2529
+ case BLOCK_H4:
2530
+ return 4;
2531
+
2532
+ case BLOCK_H5:
2533
+ return 5;
2534
+
2535
+ case BLOCK_H6:
2536
+ return 6;
2537
+ }
2538
+
2539
+ return 0;
2540
+ }
2541
+
2542
+
2543
+ asset * asset_new(char * url, scratch_pad * scratch) {
2544
+ asset * a = malloc(sizeof(asset));
2545
+
2546
+ if (a) {
2547
+ a->url = my_strdup(url);
2548
+
2549
+ // Create a unique local asset path
2550
+ a->asset_path = uuid_new();
2551
+ }
2552
+
2553
+ return a;
2554
+ }
2555
+
2556
+
2557
+ void asset_free(asset * a) {
2558
+ if (a) {
2559
+ free(a->url);
2560
+ free(a->asset_path);
2561
+ }
2562
+
2563
+ free(a);
2564
+ }
2565
+
2566
+
2567
+ asset * extract_asset(scratch_pad * scratch, char * url) {
2568
+ asset * a;
2569
+
2570
+ HASH_FIND_STR(scratch->asset_hash, url, a);
2571
+
2572
+ return a;
2573
+ }
2574
+
2575
+
2576
+ void store_asset(scratch_pad * scratch, char * url) {
2577
+ asset * a = extract_asset(scratch, url);
2578
+
2579
+ // Only store if this url has not already been stored
2580
+ if (!a) {
2581
+ // Asset not found - create new one
2582
+ a = asset_new(url, scratch);
2583
+ HASH_ADD_KEYPTR(hh, scratch->asset_hash, a->url, strlen(a->url), a);
2584
+ }
2585
+ }
2586
+
2587
+
2588
+ bool raw_filter_text_matches(char * pattern, short format) {
2589
+ if (!pattern) {
2590
+ return false;
2591
+ }
2592
+
2593
+ if (strcmp("*", pattern) == 0) {
2594
+ return true;
2595
+ } else if (strcmp("{=*}", pattern) == 0) {
2596
+ return true;
2597
+ } else {
2598
+ switch (format) {
2599
+ case FORMAT_HTML:
2600
+ if (strstr(pattern, "html")) {
2601
+ return true;
2602
+ }
2603
+
2604
+ break;
2605
+
2606
+ case FORMAT_ODT:
2607
+ case FORMAT_FODT:
2608
+ if (strstr(pattern, "odt")) {
2609
+ return true;
2610
+ }
2611
+
2612
+ break;
2613
+
2614
+ case FORMAT_EPUB:
2615
+ if (strstr(pattern, "epub")) {
2616
+ return true;
2617
+ }
2618
+
2619
+ break;
2620
+
2621
+ case FORMAT_MEMOIR:
2622
+ case FORMAT_BEAMER:
2623
+ case FORMAT_LATEX:
2624
+ if (strstr(pattern, "latex")) {
2625
+ return true;
2626
+ }
2627
+
2628
+ break;
2629
+ }
2630
+ }
2631
+
2632
+ return false;
2633
+ }
2634
+
2635
+
2636
+ /// Determine whether raw filter matches specified format
2637
+ bool raw_filter_matches(token * t, const char * source, short format) {
2638
+ bool result = false;
2639
+
2640
+ if (t->type != PAIR_RAW_FILTER) {
2641
+ return result;
2642
+ }
2643
+
2644
+ char * pattern = my_strndup(&source[t->child->start + 2], t->child->mate->start - t->child->start - 2);
2645
+
2646
+ result = raw_filter_text_matches(pattern, format);
2647
+
2648
+ free(pattern);
2649
+
2650
+ return result;
2651
+ }
2652
+