summaryrefslogtreecommitdiff
path: root/src/gempub.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gempub.c')
-rw-r--r--src/gempub.c88
1 files changed, 84 insertions, 4 deletions
diff --git a/src/gempub.c b/src/gempub.c
index c9b1c242..0f0345dc 100644
--- a/src/gempub.c
+++ b/src/gempub.c
@@ -25,31 +25,96 @@ 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
34const char *mimeType_Gempub = "application/gpub+zip"; 37const char *mimeType_Gempub = "application/gpub+zip";
35 38
39/*----------------------------------------------------------------------------------------------*/
40
41iDeclareType(GempubNavLink)
42
43struct Impl_GempubNavLink {
44 iString url;
45 iString label;
46};
47
48static void init_GempubNavLink(iGempubNavLink *d) {
49 init_String(&d->url);
50 init_String(&d->label);
51}
52
53static void deinit_GempubNavLink(iGempubNavLink *d) {
54 deinit_String(&d->url);
55 deinit_String(&d->label);
56}
57
58iDefineTypeConstruction(GempubNavLink)
59
60/*----------------------------------------------------------------------------------------------*/
61
36struct Impl_Gempub { 62struct 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
42iDefineTypeConstruction(Gempub) 69iDefineTypeConstruction(Gempub)
43 70
71static 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 const iRangecc url = capturedRange_RegExpMatch(&m, 1);
89 iUrl parts;
90 init_Url(&parts, collectNewRange_String(url));
91 if (isEmpty_Range(&parts.scheme)) {
92 iGempubNavLink link;
93 init_GempubNavLink(&link);
94 set_String(&link.url, absoluteUrl_String(url_GmRequest(index), collectNewRange_String(url)));
95 setRange_String(&link.label, capturedRange_RegExpMatch(&m, 2));
96 trim_String(&link.label);
97 pushBack_Array(d->navLinks, &link);
98 }
99 iEndCollect();
100 }
101 }
102}
103
44void init_Gempub(iGempub *d) { 104void init_Gempub(iGempub *d) {
45 d->arch = NULL; 105 d->arch = NULL;
46 init_String(&d->baseUrl); 106 init_String(&d->baseUrl);
47 iForIndices(i, d->props) { 107 iForIndices(i, d->props) {
48 init_String(&d->props[i]); 108 init_String(&d->props[i]);
49 } 109 }
110 d->navLinks = new_Array(sizeof(iGempubNavLink));
50} 111}
51 112
52void deinit_Gempub(iGempub *d) { 113void deinit_Gempub(iGempub *d) {
114 iForEach(Array, n, d->navLinks) {
115 deinit_GempubNavLink(n.value);
116 }
117 delete_Array(d->navLinks);
53 iForIndices(i, d->props) { 118 iForIndices(i, d->props) {
54 deinit_String(&d->props[i]); 119 deinit_String(&d->props[i]);
55 } 120 }
@@ -140,12 +205,27 @@ void close_Gempub(iGempub *d) {
140 } 205 }
141} 206}
142 207
208void setBaseUrl_Gempub(iGempub *d, const iString *url) {
209 set_String(&d->baseUrl, url);
210}
211
143iBool isOpen_Gempub(const iGempub *d) { 212iBool isOpen_Gempub(const iGempub *d) {
144 return d->arch != NULL; 213 return d->arch != NULL;
145} 214}
146 215
147void setBaseUrl_Gempub(iGempub *d, const iString *url) { 216const iString *indexPageUrl_Gempub(const iGempub *d) {
148 set_String(&d->baseUrl, url); 217 iAssert(!isEmpty_String(&d->baseUrl));
218 iString *dir = collect_String(copy_String(&d->baseUrl));
219 appendCStr_String(dir, "/");
220 return absoluteUrl_String(dir, &d->props[index_GempubProperty]);
221}
222
223const iString *navStartLinkUrl_Gempub(const iGempub *d) {
224 parseNavigationLinks_Gempub_(d);
225 if (isEmpty_Array(d->navLinks)) {
226 return NULL; /* has no navigation structure */
227 }
228 return &((const iGempubNavLink *) constFront_Array(d->navLinks))->url;
149} 229}
150 230
151static iBool hasProperty_Gempub_(const iGempub *d, enum iGempubProperty prop) { 231static iBool hasProperty_Gempub_(const iGempub *d, enum iGempubProperty prop) {
@@ -176,7 +256,7 @@ iString *coverPageSource_Gempub(const iGempub *d) {
176 appendProperty_Gempub_(d, "${gempub.meta.author}:", author_GempubProperty, out); 256 appendProperty_Gempub_(d, "${gempub.meta.author}:", author_GempubProperty, out);
177 if (!isRemote_Gempub_(d)) { 257 if (!isRemote_Gempub_(d)) {
178 appendFormat_String(out, "\n=> %s " book_Icon " ${gempub.cover.view}\n", 258 appendFormat_String(out, "\n=> %s " book_Icon " ${gempub.cover.view}\n",
179 cstrCollect_String(concat_Path(baseUrl, &d->props[index_GempubProperty]))); 259 cstr_String(indexPageUrl_Gempub(d)));
180 if (hasProperty_Gempub_(d, cover_GempubProperty)) { 260 if (hasProperty_Gempub_(d, cover_GempubProperty)) {
181 appendFormat_String(out, "\n=> %s ${gempub.cover.image}\n", 261 appendFormat_String(out, "\n=> %s ${gempub.cover.image}\n",
182 cstrCollect_String(concat_Path(baseUrl, &d->props[cover_GempubProperty]))); 262 cstrCollect_String(concat_Path(baseUrl, &d->props[cover_GempubProperty])));