diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-05-05 13:30:00 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-05-05 13:30:00 +0300 |
commit | 14e64fde73a25d5758fe70d5425b70399c3705db (patch) | |
tree | f3765626b7a2bdc1e7324a1d308f5b27e22b93cc /src/gempub.c | |
parent | 93f7e8712057bc04bd6123a8aa8db0e1e3bdcec8 (diff) |
Gempub: Parse links on index page for navigation
Diffstat (limited to 'src/gempub.c')
-rw-r--r-- | src/gempub.c | 84 |
1 files changed, 80 insertions, 4 deletions
diff --git a/src/gempub.c b/src/gempub.c index c9b1c242..e1f246ac 100644 --- a/src/gempub.c +++ b/src/gempub.c | |||
@@ -25,31 +25,92 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
25 | #include "lang.h" | 25 | #include "lang.h" |
26 | #include "defs.h" | 26 | #include "defs.h" |
27 | #include "gmdocument.h" | 27 | #include "gmdocument.h" |
28 | #include "gmrequest.h" | ||
28 | #include "ui/util.h" | 29 | #include "ui/util.h" |
30 | #include "app.h" | ||
29 | 31 | ||
30 | #include <the_Foundation/archive.h> | 32 | #include <the_Foundation/archive.h> |
31 | #include <the_Foundation/file.h> | 33 | #include <the_Foundation/file.h> |
32 | #include <the_Foundation/path.h> | 34 | #include <the_Foundation/path.h> |
35 | #include <the_Foundation/regexp.h> | ||
33 | 36 | ||
34 | const char *mimeType_Gempub = "application/gpub+zip"; | 37 | const char *mimeType_Gempub = "application/gpub+zip"; |
35 | 38 | ||
39 | /*----------------------------------------------------------------------------------------------*/ | ||
40 | |||
41 | iDeclareType(GempubNavLink) | ||
42 | |||
43 | struct Impl_GempubNavLink { | ||
44 | iString url; | ||
45 | iString label; | ||
46 | }; | ||
47 | |||
48 | static void init_GempubNavLink(iGempubNavLink *d) { | ||
49 | init_String(&d->url); | ||
50 | init_String(&d->label); | ||
51 | } | ||
52 | |||
53 | static void deinit_GempubNavLink(iGempubNavLink *d) { | ||
54 | deinit_String(&d->url); | ||
55 | deinit_String(&d->label); | ||
56 | } | ||
57 | |||
58 | iDefineTypeConstruction(GempubNavLink) | ||
59 | |||
60 | /*----------------------------------------------------------------------------------------------*/ | ||
61 | |||
36 | struct Impl_Gempub { | 62 | struct Impl_Gempub { |
37 | iArchive *arch; | 63 | iArchive *arch; |
38 | iString baseUrl; | 64 | iString baseUrl; |
39 | iString props[max_GempubProperty]; | 65 | iString props[max_GempubProperty]; |
66 | iArray *navLinks; /* from index page */ | ||
40 | }; | 67 | }; |
41 | 68 | ||
42 | iDefineTypeConstruction(Gempub) | 69 | iDefineTypeConstruction(Gempub) |
43 | 70 | ||
71 | static void parseNavigationLinks_Gempub_(const iGempub *d) { | ||
72 | if (!isEmpty_Array(d->navLinks)) { | ||
73 | return; | ||
74 | } | ||
75 | iGmRequest *index = iClob(new_GmRequest(certs_App())); | ||
76 | setUrl_GmRequest(index, indexPageUrl_Gempub(d)); | ||
77 | submit_GmRequest(index); /* this is just a local file read */ | ||
78 | iAssert(isFinished_GmRequest(index)); | ||
79 | iRangecc src = iNullRange; | ||
80 | iRegExp *linkPattern = iClob(newGemtextLink_RegExp()); | ||
81 | while (nextSplit_Rangecc(range_Block(body_GmRequest(index)), "\n", &src)) { | ||
82 | iRangecc line = src; | ||
83 | trim_Rangecc(&line); | ||
84 | iRegExpMatch m; | ||
85 | init_RegExpMatch(&m); | ||
86 | if (matchRange_RegExp(linkPattern, line, &m)) { | ||
87 | iBeginCollect(); | ||
88 | iGempubNavLink link; | ||
89 | init_GempubNavLink(&link); | ||
90 | const iRangecc url = capturedRange_RegExpMatch(&m, 1); | ||
91 | set_String(&link.url, absoluteUrl_String(url_GmRequest(index), collectNewRange_String(url))); | ||
92 | setRange_String(&link.label, capturedRange_RegExpMatch(&m, 2)); | ||
93 | trim_String(&link.label); | ||
94 | pushBack_Array(d->navLinks, &link); | ||
95 | iEndCollect(); | ||
96 | } | ||
97 | } | ||
98 | } | ||
99 | |||
44 | void init_Gempub(iGempub *d) { | 100 | void init_Gempub(iGempub *d) { |
45 | d->arch = NULL; | 101 | d->arch = NULL; |
46 | init_String(&d->baseUrl); | 102 | init_String(&d->baseUrl); |
47 | iForIndices(i, d->props) { | 103 | iForIndices(i, d->props) { |
48 | init_String(&d->props[i]); | 104 | init_String(&d->props[i]); |
49 | } | 105 | } |
106 | d->navLinks = new_Array(sizeof(iGempubNavLink)); | ||
50 | } | 107 | } |
51 | 108 | ||
52 | void deinit_Gempub(iGempub *d) { | 109 | void deinit_Gempub(iGempub *d) { |
110 | iForEach(Array, n, d->navLinks) { | ||
111 | deinit_GempubNavLink(n.value); | ||
112 | } | ||
113 | delete_Array(d->navLinks); | ||
53 | iForIndices(i, d->props) { | 114 | iForIndices(i, d->props) { |
54 | deinit_String(&d->props[i]); | 115 | deinit_String(&d->props[i]); |
55 | } | 116 | } |
@@ -140,12 +201,27 @@ void close_Gempub(iGempub *d) { | |||
140 | } | 201 | } |
141 | } | 202 | } |
142 | 203 | ||
204 | void setBaseUrl_Gempub(iGempub *d, const iString *url) { | ||
205 | set_String(&d->baseUrl, url); | ||
206 | } | ||
207 | |||
143 | iBool isOpen_Gempub(const iGempub *d) { | 208 | iBool isOpen_Gempub(const iGempub *d) { |
144 | return d->arch != NULL; | 209 | return d->arch != NULL; |
145 | } | 210 | } |
146 | 211 | ||
147 | void setBaseUrl_Gempub(iGempub *d, const iString *url) { | 212 | const iString *indexPageUrl_Gempub(const iGempub *d) { |
148 | set_String(&d->baseUrl, url); | 213 | iAssert(!isEmpty_String(&d->baseUrl)); |
214 | iString *dir = collect_String(copy_String(&d->baseUrl)); | ||
215 | appendCStr_String(dir, "/"); | ||
216 | return absoluteUrl_String(dir, &d->props[index_GempubProperty]); | ||
217 | } | ||
218 | |||
219 | const iString *navStartLinkUrl_Gempub(const iGempub *d) { | ||
220 | parseNavigationLinks_Gempub_(d); | ||
221 | if (isEmpty_Array(d->navLinks)) { | ||
222 | return NULL; /* has no navigation structure */ | ||
223 | } | ||
224 | return &((const iGempubNavLink *) constFront_Array(d->navLinks))->url; | ||
149 | } | 225 | } |
150 | 226 | ||
151 | static iBool hasProperty_Gempub_(const iGempub *d, enum iGempubProperty prop) { | 227 | static iBool hasProperty_Gempub_(const iGempub *d, enum iGempubProperty prop) { |
@@ -176,7 +252,7 @@ iString *coverPageSource_Gempub(const iGempub *d) { | |||
176 | appendProperty_Gempub_(d, "${gempub.meta.author}:", author_GempubProperty, out); | 252 | appendProperty_Gempub_(d, "${gempub.meta.author}:", author_GempubProperty, out); |
177 | if (!isRemote_Gempub_(d)) { | 253 | if (!isRemote_Gempub_(d)) { |
178 | appendFormat_String(out, "\n=> %s " book_Icon " ${gempub.cover.view}\n", | 254 | appendFormat_String(out, "\n=> %s " book_Icon " ${gempub.cover.view}\n", |
179 | cstrCollect_String(concat_Path(baseUrl, &d->props[index_GempubProperty]))); | 255 | cstr_String(indexPageUrl_Gempub(d))); |
180 | if (hasProperty_Gempub_(d, cover_GempubProperty)) { | 256 | if (hasProperty_Gempub_(d, cover_GempubProperty)) { |
181 | appendFormat_String(out, "\n=> %s ${gempub.cover.image}\n", | 257 | appendFormat_String(out, "\n=> %s ${gempub.cover.image}\n", |
182 | cstrCollect_String(concat_Path(baseUrl, &d->props[cover_GempubProperty]))); | 258 | cstrCollect_String(concat_Path(baseUrl, &d->props[cover_GempubProperty]))); |