rmultimarkdown 4.7.1.1 → 6.2.2.1

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.
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
+