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.
- checksums.yaml +4 -4
- data/LICENSE +2 -2
- data/README.md +7 -9
- data/Rakefile +33 -18
- data/ext/Makefile +261 -0
- data/ext/extconf.rb +23 -3
- data/ext/mmd/aho-corasick.c +596 -0
- data/ext/mmd/aho-corasick.h +120 -0
- data/ext/mmd/beamer.c +344 -0
- data/ext/mmd/beamer.h +72 -0
- data/ext/mmd/char.c +156 -0
- data/ext/mmd/char.h +111 -0
- data/ext/mmd/char_lookup.c +212 -0
- data/ext/mmd/critic_markup.c +330 -0
- data/ext/mmd/critic_markup.h +94 -0
- data/ext/mmd/d_string.c +402 -0
- data/ext/mmd/epub.c +563 -0
- data/ext/mmd/epub.h +69 -0
- data/ext/mmd/fodt.c +2288 -0
- data/ext/mmd/fodt.h +81 -0
- data/ext/mmd/html.c +2460 -0
- data/ext/mmd/html.h +81 -0
- data/ext/mmd/i18n.h +170 -0
- data/ext/mmd/include/d_string.h +182 -0
- data/ext/mmd/include/libMultiMarkdown.h +548 -0
- data/ext/mmd/include/token.h +233 -0
- data/ext/mmd/latex.c +2435 -0
- data/ext/mmd/latex.h +83 -0
- data/ext/mmd/lexer.c +3001 -0
- data/ext/mmd/lexer.h +75 -0
- data/ext/mmd/memoir.c +138 -0
- data/ext/mmd/memoir.h +67 -0
- data/ext/mmd/miniz.c +7557 -0
- data/ext/mmd/miniz.h +1328 -0
- data/ext/mmd/mmd.c +2798 -0
- data/ext/mmd/mmd.h +120 -0
- data/ext/mmd/object_pool.c +141 -0
- data/ext/mmd/object_pool.h +101 -0
- data/ext/mmd/opendocument-content.c +2071 -0
- data/ext/mmd/opendocument-content.h +135 -0
- data/ext/mmd/opendocument.c +981 -0
- data/ext/mmd/opendocument.h +118 -0
- data/ext/mmd/parser.c +1760 -0
- data/ext/mmd/parser.h +39 -0
- data/{MultiMarkdown-4 → ext/mmd}/rng.c +90 -49
- data/ext/mmd/scanners.c +77512 -0
- data/ext/mmd/scanners.h +101 -0
- data/ext/mmd/stack.c +142 -0
- data/ext/mmd/stack.h +113 -0
- data/ext/mmd/textbundle.c +455 -0
- data/ext/mmd/textbundle.h +115 -0
- data/ext/mmd/token.c +773 -0
- data/ext/mmd/token_pairs.c +263 -0
- data/ext/mmd/token_pairs.h +123 -0
- data/ext/mmd/transclude.c +549 -0
- data/ext/mmd/transclude.h +87 -0
- data/ext/mmd/uthash.h +1074 -0
- data/ext/mmd/uuid.c +154 -0
- data/ext/mmd/uuid.h +77 -0
- data/ext/mmd/version.h +111 -0
- data/ext/mmd/writer.c +2652 -0
- data/ext/mmd/writer.h +260 -0
- data/ext/mmd/zip.c +210 -0
- data/ext/mmd/zip.h +120 -0
- data/ext/{multi_markdown.c → ruby_multi_markdown.c} +87 -18
- data/lib/multi_markdown.bundle +0 -0
- data/lib/multi_markdown.rb +5 -8
- data/lib/multi_markdown/version.rb +1 -1
- data/rmultimarkdown.gemspec +2 -2
- data/test/{extensions_test.rb.rb → extensions_test.rb} +10 -54
- data/test/multi_markdown_test.rb +13 -0
- metadata +67 -47
- data/MultiMarkdown-4/GLibFacade.c +0 -310
- data/MultiMarkdown-4/GLibFacade.h +0 -100
- data/MultiMarkdown-4/beamer.c +0 -182
- data/MultiMarkdown-4/beamer.h +0 -11
- data/MultiMarkdown-4/critic.c +0 -111
- data/MultiMarkdown-4/critic.h +0 -15
- data/MultiMarkdown-4/glib.h +0 -11
- data/MultiMarkdown-4/html.c +0 -1117
- data/MultiMarkdown-4/html.h +0 -14
- data/MultiMarkdown-4/latex.c +0 -1217
- data/MultiMarkdown-4/latex.h +0 -16
- data/MultiMarkdown-4/libMultiMarkdown.h +0 -177
- data/MultiMarkdown-4/lyx.c +0 -2265
- data/MultiMarkdown-4/lyx.h +0 -37
- data/MultiMarkdown-4/lyxbeamer.c +0 -265
- data/MultiMarkdown-4/lyxbeamer.h +0 -11
- data/MultiMarkdown-4/memoir.c +0 -80
- data/MultiMarkdown-4/memoir.h +0 -10
- data/MultiMarkdown-4/multimarkdown.c +0 -518
- data/MultiMarkdown-4/odf.c +0 -1222
- data/MultiMarkdown-4/odf.h +0 -18
- data/MultiMarkdown-4/opml.c +0 -189
- data/MultiMarkdown-4/opml.h +0 -15
- data/MultiMarkdown-4/parse_utilities.c +0 -884
- data/MultiMarkdown-4/parser.c +0 -16656
- data/MultiMarkdown-4/parser.h +0 -188
- data/MultiMarkdown-4/rtf.c +0 -665
- data/MultiMarkdown-4/rtf.h +0 -17
- data/MultiMarkdown-4/strtok.c +0 -56
- data/MultiMarkdown-4/strtok.h +0 -9
- data/MultiMarkdown-4/text.c +0 -53
- data/MultiMarkdown-4/text.h +0 -11
- data/MultiMarkdown-4/toc.c +0 -142
- data/MultiMarkdown-4/toc.h +0 -15
- data/MultiMarkdown-4/transclude.c +0 -307
- data/MultiMarkdown-4/transclude.h +0 -28
- data/MultiMarkdown-4/writer.c +0 -731
- 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 ``
|
|
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
|
+
|