Coverage Report

Created: 2026-08-25 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ebook-tools-0.2.2/src/libepub/opf.c
Line
Count
Source
1
#include "epublib.h"
2
3
1.73k
struct opf *_opf_parse(struct epub *epub, char *opfStr) {
4
1.73k
  struct opf *opf;
5
1.73k
  xmlTextReaderPtr reader;
6
1.73k
  int ret;
7
8
1.73k
  _epub_print_debug(epub, DEBUG_INFO, "building opf struct");
9
  
10
1.73k
  opf = malloc(sizeof(struct opf));
11
1.73k
  if (!opf) {
12
0
    _epub_err_set_oom(&epub->error);
13
0
    return NULL;
14
0
  }
15
1.73k
  memset(opf, 0, sizeof(struct opf));
16
1.73k
  opf->epub = epub;
17
  
18
1.73k
  reader = xmlReaderForMemory(opfStr, strlen(opfStr), 
19
1.73k
                              "OPF", NULL, 0);
20
1.73k
   if (reader != NULL) {
21
1.73k
    ret = xmlTextReaderRead(reader);
22
10.9k
    while (ret == 1) {
23
9.17k
      const xmlChar *name = xmlTextReaderConstLocalName(reader);
24
9.17k
      if (xmlStrcmp(name, (xmlChar *)"metadata") == 0)
25
1.18k
        _opf_parse_metadata(opf, reader);
26
7.98k
      else 
27
7.98k
      if (xmlStrcmp(name, (xmlChar *)"manifest") == 0)
28
564
        _opf_parse_manifest(opf, reader);
29
7.41k
      else 
30
7.41k
      if (xmlStrcmp(name, (xmlChar *)"spine") == 0)
31
571
        _opf_parse_spine(opf, reader);
32
6.84k
      else 
33
6.84k
      if (xmlStrcmp(name, (xmlChar *)"guide") == 0)
34
530
        _opf_parse_guide(opf, reader);
35
6.31k
      else 
36
6.31k
      if (xmlStrcmp(name, (xmlChar *)"tours") == 0)
37
0
        _opf_parse_tours(opf, reader);
38
      
39
9.17k
     ret = xmlTextReaderRead(reader);
40
9.17k
    }
41
42
1.73k
    xmlFreeTextReader(reader);
43
1.73k
    if (ret != 0) {
44
1.21k
      _epub_print_debug(opf->epub, DEBUG_ERROR, "failed to parse OPF");
45
1.21k
      return NULL;
46
1.21k
    } else if(!opf->spine) {
47
3
    _epub_print_debug(opf->epub, DEBUG_ERROR, "Ilegal OPF no spine found");
48
3
    return NULL;
49
3
  }
50
1.73k
   } else {
51
0
     _epub_print_debug(opf->epub, DEBUG_ERROR, "unable to open OPF");
52
0
     return NULL;
53
0
   }
54
55
516
   return opf;
56
1.73k
}
57
58
xmlChar *_get_possible_namespace(xmlTextReaderPtr reader, 
59
                                 const xmlChar * localName, 
60
                                 const xmlChar * namespace) 
61
4.70k
{
62
4.70k
  xmlChar *tmp, *ns;
63
4.70k
  ns = xmlTextReaderLookupNamespace(reader, namespace);
64
4.70k
  tmp = xmlTextReaderGetAttributeNs(reader, localName, ns);
65
66
4.70k
  if (ns)
67
4.41k
    free(ns);
68
  
69
4.70k
  if (! tmp)
70
1.76k
    return xmlTextReaderGetAttribute(reader, localName);
71
  
72
2.94k
  return tmp;
73
4.70k
}
74
75
1.18k
void _opf_init_metadata(struct opf *opf) {
76
1.18k
  struct metadata *meta = malloc(sizeof(struct metadata));
77
78
1.18k
  meta->id = NewListAlloc(LIST, NULL, NULL, NULL);  
79
1.18k
  meta->title = NewListAlloc(LIST, NULL, NULL, (NodeCompareFunc)StringCompare);
80
1.18k
  meta->creator = NewListAlloc(LIST, NULL, NULL, NULL);
81
1.18k
  meta->contrib = NewListAlloc(LIST, NULL, NULL, NULL);
82
1.18k
  meta->subject = NewListAlloc(LIST, NULL, NULL, (NodeCompareFunc)StringCompare);
83
1.18k
  meta->publisher = NewListAlloc(LIST, NULL, NULL, (NodeCompareFunc)StringCompare);
84
1.18k
  meta->description = NewListAlloc(LIST, NULL, NULL, (NodeCompareFunc)StringCompare);
85
1.18k
  meta->date = NewListAlloc(LIST, NULL, NULL, NULL);
86
1.18k
  meta->type = NewListAlloc(LIST, NULL, NULL, (NodeCompareFunc)StringCompare);
87
1.18k
  meta->format = NewListAlloc(LIST, NULL, NULL, (NodeCompareFunc)StringCompare);
88
1.18k
  meta->source = NewListAlloc(LIST, NULL, NULL, (NodeCompareFunc)StringCompare);
89
1.18k
  meta->lang = NewListAlloc(LIST, NULL, NULL, (NodeCompareFunc)StringCompare);
90
1.18k
  meta->relation = NewListAlloc(LIST, NULL, NULL, (NodeCompareFunc)StringCompare);
91
1.18k
  meta->coverage = NewListAlloc(LIST, NULL, NULL, (NodeCompareFunc)StringCompare);
92
1.18k
  meta->rights = NewListAlloc(LIST, NULL, NULL, (NodeCompareFunc)StringCompare);
93
1.18k
  meta->meta = NewListAlloc(LIST, NULL, NULL, NULL);
94
95
1.18k
  opf->metadata = meta;
96
1.18k
}
97
98
508
void _opf_free_metadata(struct metadata *meta) {
99
508
  FreeList(meta->id, (ListFreeFunc)_list_free_id);
100
508
  FreeList(meta->title, free);
101
508
  FreeList(meta->creator, (ListFreeFunc)_list_free_creator);
102
508
  FreeList(meta->contrib, (ListFreeFunc)_list_free_creator);
103
508
  FreeList(meta->subject, free);
104
508
  FreeList(meta->publisher, free);
105
508
  FreeList(meta->description, free);
106
508
  FreeList(meta->date, (ListFreeFunc)_list_free_date);
107
508
  FreeList(meta->type, free);
108
508
  FreeList(meta->format, free);
109
508
  FreeList(meta->source, free);
110
508
  FreeList(meta->lang, free);
111
508
  FreeList(meta->relation, free);
112
508
  FreeList(meta->coverage, free);
113
508
  FreeList(meta->rights, free);
114
508
  FreeList(meta->meta, (ListFreeFunc)_list_free_meta);
115
508
  free(meta);
116
508
}
117
118
1.18k
void _opf_parse_metadata(struct opf *opf, xmlTextReaderPtr reader) {
119
1.18k
  int ret;
120
1.18k
  struct metadata *meta;
121
1.18k
  const xmlChar *local;
122
1.18k
  xmlChar *string;
123
  
124
1.18k
  _epub_print_debug(opf->epub, DEBUG_INFO, "parsing metadata");
125
  
126
  // must have title, identifier and language
127
1.18k
  _opf_init_metadata(opf);
128
1.18k
  meta = opf->metadata;
129
  
130
1.18k
  ret = xmlTextReaderRead(reader);
131
62.1k
  while (ret == 1 && 
132
61.6k
         xmlStrcasecmp(xmlTextReaderConstLocalName(reader),(xmlChar *)"metadata")) {
133
134
    // ignore non starting tags
135
60.9k
    if (xmlTextReaderNodeType(reader) != 1) {
136
44.4k
      ret = xmlTextReaderRead(reader);
137
44.4k
      continue;
138
44.4k
    }
139
    
140
16.5k
    local = xmlTextReaderConstLocalName(reader);
141
16.5k
    string = (xmlChar *)xmlTextReaderReadString(reader);
142
143
16.5k
    if (xmlStrcasecmp(local, (xmlChar *)"identifier") == 0) {
144
666
      struct id *new = malloc(sizeof(struct id));
145
666
      new->string = string;
146
666
      new->scheme = _get_possible_namespace(reader, (xmlChar *)"scheme",
147
666
                                                (xmlChar *)"opf");
148
666
      new->id = xmlTextReaderGetAttribute(reader, (xmlChar *)"id");
149
      
150
666
      AddNode(meta->id, NewListNode(meta->id, new));
151
666
      _epub_print_debug(opf->epub, DEBUG_INFO, "identifier %s(%s) is: %s", 
152
666
                        new->id, new->scheme, new->string);
153
15.8k
    } else if (xmlStrcasecmp(local, (xmlChar *)"title") == 0) {
154
838
      AddNode(meta->title, NewListNode(meta->title, string));
155
838
      _epub_print_debug(opf->epub, DEBUG_INFO, "title is %s", string);
156
        
157
15.0k
    } else if (xmlStrcasecmp(local, (xmlChar *)"creator") == 0) {
158
926
      struct creator *new = malloc(sizeof(struct creator));
159
926
      new->name = string;
160
926
      new->fileAs = 
161
926
        _get_possible_namespace(reader, (xmlChar *)"file-as",
162
926
                                    (xmlChar *)"opf");
163
926
      new->role = 
164
926
        _get_possible_namespace(reader, (xmlChar *)"role",
165
926
                                    (xmlChar *)"opf");
166
926
      AddNode(meta->creator, NewListNode(meta->creator, new));       
167
926
      _epub_print_debug(opf->epub, DEBUG_INFO, "creator - %s: %s (%s)", 
168
926
                        new->role, new->name, new->fileAs);
169
        
170
14.0k
    } else if (xmlStrcasecmp(local, (xmlChar *)"contributor") == 0) {
171
652
      struct creator *new = malloc(sizeof(struct creator));
172
652
      new->name = string;
173
652
      new->fileAs = 
174
652
        _get_possible_namespace(reader, (xmlChar *)"file-as",
175
652
                                    (xmlChar *)"opf");
176
652
      new->role = 
177
652
        _get_possible_namespace(reader, (xmlChar *)"role",
178
652
                                    (xmlChar *)"opf");
179
652
      AddNode(meta->contrib, NewListNode(meta->contrib, new));     
180
652
      _epub_print_debug(opf->epub, DEBUG_INFO, "contributor - %s: %s (%s)", 
181
652
                        new->role, new->name, new->fileAs);
182
      
183
13.4k
    } else if (xmlStrcasecmp(local, (xmlChar *)"meta") == 0) {
184
2.42k
      struct meta *new = malloc(sizeof(struct meta));
185
2.42k
      new->name = xmlTextReaderGetAttribute(reader, (xmlChar *)"name");
186
2.42k
      new->content = xmlTextReaderGetAttribute(reader, (xmlChar *)"content");
187
      
188
2.42k
      AddNode(meta->meta, NewListNode(meta->meta, new));
189
2.42k
      if (string)
190
0
        free(string);
191
2.42k
      _epub_print_debug(opf->epub, DEBUG_INFO, "meta is %s: %s", 
192
2.42k
                        new->name, new->content); 
193
11.0k
    } else if (xmlStrcasecmp(local, (xmlChar *)"date") == 0) {
194
885
      struct date *new = malloc(sizeof(struct date));
195
885
      new->date = string;
196
885
      new->event = _get_possible_namespace(reader, (xmlChar *)"event",
197
885
                                               (xmlChar *)"opf");
198
885
      AddNode(meta->date, NewListNode(meta->date, new));
199
885
      _epub_print_debug(opf->epub, DEBUG_INFO, "date of %s: %s", 
200
885
                        new->event, new->date); 
201
        
202
10.1k
    } else if (xmlStrcasecmp(local, (xmlChar *)"subject") == 0) {
203
741
      AddNode(meta->subject, NewListNode(meta->subject, string));
204
741
      _epub_print_debug(opf->epub, DEBUG_INFO, "subject is %s", string);
205
        
206
9.39k
    } else if (xmlStrcasecmp(local, (xmlChar *)"publisher") == 0) {
207
804
      AddNode(meta->publisher, NewListNode(meta->publisher, string)); 
208
804
      _epub_print_debug(opf->epub, DEBUG_INFO, "publisher is %s", string); 
209
        
210
8.58k
    } else if (xmlStrcasecmp(local, (xmlChar *)"description") == 0) {
211
1.05k
      AddNode(meta->description, NewListNode(meta->description, string));
212
1.05k
      _epub_print_debug(opf->epub, DEBUG_INFO, "description is %s", string);
213
        
214
7.53k
    } else if (xmlStrcasecmp(local, (xmlChar *)"type") == 0) {
215
892
      AddNode(meta->type, NewListNode(meta->type, string));       
216
892
      _epub_print_debug(opf->epub, DEBUG_INFO, "type is %s", string);
217
        
218
6.64k
    } else if (xmlStrcasecmp(local, (xmlChar *)"format") == 0) {
219
889
      AddNode(meta->format, NewListNode(meta->format, string));
220
889
      _epub_print_debug(opf->epub, DEBUG_INFO, "format is %s", string); 
221
222
5.75k
    } else if (xmlStrcasecmp(local, (xmlChar *)"source") == 0) {
223
1.06k
      AddNode(meta->source, NewListNode(meta->source, string));
224
1.06k
      _epub_print_debug(opf->epub, DEBUG_INFO, "source is %s", string); 
225
226
4.68k
    } else if (xmlStrcasecmp(local, (xmlChar *)"language") == 0) {
227
812
      AddNode(meta->lang, NewListNode(meta->lang, string));
228
812
      _epub_print_debug(opf->epub, DEBUG_INFO, "language is %s", string); 
229
      
230
3.87k
    } else if (xmlStrcasecmp(local, (xmlChar *)"relation") == 0) {
231
893
      AddNode(meta->relation, NewListNode(meta->relation, string));
232
893
      _epub_print_debug(opf->epub, DEBUG_INFO, "relation is %s", string); 
233
234
2.98k
    } else if (xmlStrcasecmp(local, (xmlChar *)"coverage") == 0) {
235
663
      AddNode(meta->coverage, NewListNode(meta->coverage, string));
236
663
      _epub_print_debug(opf->epub, DEBUG_INFO, "coverage is %s", string); 
237
2.32k
    } else if (xmlStrcasecmp(local, (xmlChar *)"rights") == 0) {
238
1.00k
      AddNode(meta->rights, NewListNode(meta->rights, string));
239
1.00k
      _epub_print_debug(opf->epub, DEBUG_INFO, "rights is %s", string);
240
1.32k
    } else if (string) {
241
1.21k
      if (xmlStrcasecmp(local, (xmlChar *)"dc-metadata") != 0 &&
242
1.21k
          xmlStrcasecmp(local, (xmlChar *)"x-metadata") != 0)
243
1.21k
        _epub_print_debug(opf->epub, DEBUG_INFO,
244
1.21k
                          "unsupported local %s: %s", local, string); 
245
1.21k
      free(string);
246
1.21k
    }
247
248
16.5k
    ret = xmlTextReaderRead(reader);
249
16.5k
  }
250
1.18k
}
251
252
167
struct toc *_opf_init_toc() {
253
  
254
167
  struct toc *toc = malloc(sizeof(struct toc));
255
167
  memset(toc, 0, sizeof(struct toc));
256
257
167
  toc->playOrder = NewListAlloc(LIST, NULL, NULL, 
258
167
                                (NodeCompareFunc)_list_cmp_toc_by_playorder);
259
260
167
  return toc;
261
167
}
262
263
64
struct tocCategory *_opf_init_toc_category() {
264
64
  struct tocCategory *tc = malloc(sizeof(struct tocCategory));
265
64
  memset(tc, 0, sizeof(struct tocCategory));
266
267
64
  tc->info = NewListAlloc(LIST, NULL, NULL, NULL); //tocLabel
268
64
  tc->label = NewListAlloc(LIST, NULL, NULL, NULL); //tocLabel
269
64
  tc->items = NewListAlloc(LIST, NULL, NULL, NULL); //tocItem
270
271
64
  return tc;
272
64
}
273
274
51
void _opf_free_toc_category(struct tocCategory *tc) {
275
51
  if (tc->id)
276
0
    free(tc->id);
277
51
  if (tc->class)
278
0
    free(tc->class);
279
  
280
51
  FreeList(tc->info, (ListFreeFunc)_list_free_toc_label);
281
51
  FreeList(tc->label, (ListFreeFunc)_list_free_toc_label);
282
51
  FreeList(tc->items, (ListFreeFunc)_list_free_toc_item);
283
  
284
51
  free(tc);
285
51
}
286
287
139
void _opf_free_toc(struct toc *toc) {
288
  
289
139
  if (toc->navMap)
290
51
    _opf_free_toc_category(toc->navMap);
291
139
  if (toc->navList)
292
0
    _opf_free_toc_category(toc->navList);
293
139
  if (toc->pageList)
294
0
    _opf_free_toc_category(toc->pageList);
295
296
  // all items are already free, lets free only the struct
297
139
  FreeList(toc->playOrder, NULL);
298
299
139
  free(toc);
300
301
139
}
302
303
// Parse a navLabel or navInfo returns NULL on failure and the label on success 
304
15
struct tocLabel *_opf_parse_navlabel(struct opf *opf, xmlTextReaderPtr reader) {
305
15
  int ret;
306
  
307
15
  struct tocLabel *new = malloc(sizeof(struct tocLabel));
308
15
  memset(new, 0, sizeof(struct tocLabel));
309
310
15
  new->lang = xmlTextReaderGetAttribute(reader, (xmlChar *)"lang");
311
15
  new->dir = xmlTextReaderGetAttribute(reader, (xmlChar *)"dir");
312
313
15
  ret = xmlTextReaderRead(reader);
314
87
  while (ret == 1 && 
315
84
         xmlStrcasecmp(xmlTextReaderConstName(reader),(xmlChar *)"navLabel") &&
316
72
         xmlStrcasecmp(xmlTextReaderConstName(reader),(xmlChar *)"navInfo")) {
317
72
    if (! xmlStrcasecmp(xmlTextReaderConstName(reader),(xmlChar *)"text") &&
318
30
        xmlTextReaderNodeType(reader) == 1) {
319
15
      new->text = (xmlChar *)xmlTextReaderReadString(reader);
320
15
    }
321
72
    ret = xmlTextReaderRead(reader);
322
72
  }
323
324
15
  if (ret != 1) {
325
3
    free(new);
326
3
    return NULL;
327
3
  }
328
12
  _epub_print_debug(opf->epub, DEBUG_INFO, 
329
12
                    "parsing label/info %s(%s/%s)",
330
12
                    new->text, new->lang, new->dir);
331
12
  return new;
332
15
}
333
334
29
struct tocItem *_opf_init_toc_item(int depth) {
335
29
  struct tocItem *item = malloc(sizeof(struct tocItem));
336
29
  memset(item, 0, sizeof(struct tocItem));
337
338
29
  item->depth = depth;
339
29
  item->playOrder = -1;
340
29
  item->value = -1;
341
342
29
  return item;
343
29
}
344
345
29
int _get_attribute_as_positive_int(xmlTextReaderPtr reader, const xmlChar *name) {
346
29
  xmlChar *str = xmlTextReaderGetAttribute(reader, name);
347
29
  int ret = -1;
348
349
29
  if (str) { 
350
20
    ret = atoi((char *)str);
351
20
    free(str);
352
20
  } 
353
354
29
  return ret;
355
29
}
356
357
64
void _opf_parse_navmap(struct opf *opf, xmlTextReaderPtr reader) {
358
64
  int ret;
359
64
  int depth = 0;
360
361
64
  struct tocCategory *tc = _opf_init_toc_category();
362
64
  struct tocItem *item = NULL;
363
364
64
  _epub_print_debug(opf->epub, DEBUG_INFO, "parsing nav map");
365
366
64
  tc->id = xmlTextReaderGetAttribute(reader, (xmlChar *)"id");
367
368
64
  ret = xmlTextReaderRead(reader);
369
222
  while (ret == 1 && 
370
168
         xmlStrcasecmp(xmlTextReaderConstName(reader),(xmlChar *)"navMap")) {
371
372
158
    if (! xmlStrcasecmp(xmlTextReaderConstName(reader),(xmlChar *)"navPoint")) {
373
41
      if (xmlTextReaderNodeType(reader) == 1) {
374
375
29
        if (item) {
376
0
          _epub_print_debug(opf->epub, DEBUG_INFO, 
377
0
                            "adding nav point item->%s %s (d:%d,p:%d)", 
378
0
                            item->id, item->src, item->depth, item->playOrder);
379
0
          AddNode(tc->items, NewListNode(tc->items, item));
380
0
          AddNode(opf->toc->playOrder, NewListNode(opf->toc->playOrder, item));
381
0
          item = NULL;
382
0
        }
383
384
29
        depth++;
385
29
        item = _opf_init_toc_item(depth);
386
29
        item->id = xmlTextReaderGetAttribute(reader, (xmlChar *)"id");
387
29
        item->class = xmlTextReaderGetAttribute(reader, (xmlChar *)"class");
388
        
389
29
        item->playOrder = _get_attribute_as_positive_int(reader, (xmlChar *)"playOrder");
390
29
        if (item->playOrder == -1) {
391
9
          _epub_print_debug(opf->epub, DEBUG_WARNING, 
392
9
                            "- missing play order in nav point element");
393
9
        }
394
     
395
29
      } else if (xmlTextReaderNodeType(reader) == 15) {
396
12
        if (item) {
397
12
          _epub_print_debug(opf->epub, DEBUG_INFO, 
398
12
                            "adding nav point item->%s %s (d:%d,p:%d)", 
399
12
                            item->id, item->src, item->depth, item->playOrder);
400
12
          AddNode(tc->items, NewListNode(tc->items, item)); 
401
12
          AddNode(opf->toc->playOrder, NewListNode(opf->toc->playOrder, item));
402
12
          item = NULL;
403
12
        }
404
12
        depth--;
405
12
      }
406
41
    }
407
  
408
    // ignore non starting tags
409
158
    if (xmlTextReaderNodeType(reader) != 1) {
410
96
      ret = xmlTextReaderRead(reader);
411
96
      continue;
412
96
    }
413
    
414
62
    if (! xmlStrcasecmp(xmlTextReaderConstName(reader),(xmlChar *)"navLabel")) {
415
15
      if (item) {
416
15
        if (! item->label)
417
15
          item->label = NewListAlloc(LIST, NULL, NULL, NULL); //tocLabel
418
15
        AddNode(item->label, NewListNode(item->label, 
419
15
                                         _opf_parse_navlabel(opf, reader)));
420
15
      } else { // Not inside navpoint
421
0
        AddNode(tc->label, NewListNode(tc->label, 
422
0
                                       _opf_parse_navlabel(opf, reader)));
423
0
      }
424
47
    } else if (! xmlStrcasecmp(xmlTextReaderConstName(reader),(xmlChar *)"navInfo")) {
425
0
        AddNode(tc->info, NewListNode(tc->info, 
426
0
                                      _opf_parse_navlabel(opf, reader)));
427
0
        if (item)
428
0
          _epub_print_debug(opf->epub, DEBUG_WARNING, 
429
0
                            "nav info inside nav point element");
430
0
    } else 
431
47
      if (! xmlStrcasecmp(xmlTextReaderConstName(reader),(xmlChar *)"content")) {
432
10
        if (item)
433
10
          item->src = xmlTextReaderGetAttribute(reader, (xmlChar *)"src");
434
0
        else
435
0
          _epub_print_debug(opf->epub, DEBUG_WARNING, 
436
0
                            "content not inside nav point element");  
437
10
      }
438
    
439
62
    ret = xmlTextReaderRead(reader);
440
62
  }
441
442
64
  opf->toc->navMap = tc;
443
64
  _epub_print_debug(opf->epub, DEBUG_INFO, "finished parsing nav map");
444
64
}
445
446
0
void _opf_parse_navlist(struct opf *opf, xmlTextReaderPtr reader) {
447
0
  int ret;
448
449
0
  struct tocCategory *tc = _opf_init_toc_category();
450
0
  struct tocItem *item = NULL;
451
452
0
  tc->id = xmlTextReaderGetAttribute(reader, (xmlChar *)"id");
453
0
  tc->class = xmlTextReaderGetAttribute(reader, (xmlChar *)"class");
454
    
455
0
  _epub_print_debug(opf->epub, DEBUG_INFO, "parsing nav list");
456
457
0
  ret = xmlTextReaderRead(reader);
458
0
  while (ret == 1 && 
459
0
         xmlStrcasecmp(xmlTextReaderConstName(reader),(xmlChar *)"navList")) {
460
461
0
    if (! xmlStrcasecmp(xmlTextReaderConstName(reader),(xmlChar *)"navTarget")) {
462
0
      if (xmlTextReaderNodeType(reader) == 1) {
463
0
        item = _opf_init_toc_item(1);
464
0
        item->id = xmlTextReaderGetAttribute(reader, (xmlChar *)"id");
465
0
        item->class = xmlTextReaderGetAttribute(reader, (xmlChar *)"class");
466
        
467
0
        item->playOrder = _get_attribute_as_positive_int(reader, (xmlChar *)"playOrder");
468
0
        if (item->playOrder == -1) {
469
0
          _epub_print_debug(opf->epub, DEBUG_WARNING, 
470
0
                            "- missing play order in nav target element");
471
0
        }
472
0
        item->value = _get_attribute_as_positive_int(reader, (xmlChar *)"value"); 
473
0
      } else if (xmlTextReaderNodeType(reader) == 15) {
474
0
        if (item) {
475
0
          _epub_print_debug(opf->epub, DEBUG_INFO, 
476
0
                            "adding nav target item->%s %s (d:%d,p:%d)", 
477
0
                            item->id, item->src, item->depth, item->playOrder);
478
0
          AddNode(tc->items, NewListNode(tc->items, item)); 
479
0
          AddNode(opf->toc->playOrder, NewListNode(opf->toc->playOrder, item));
480
0
          item = NULL;
481
0
        } else {
482
0
          _epub_print_debug(opf->epub, DEBUG_ERROR, "empty item in nav list"); 
483
0
        }
484
0
      }
485
0
    }
486
  
487
    
488
    // ignore non starting tags
489
0
    if (xmlTextReaderNodeType(reader) != 1) {
490
0
      ret = xmlTextReaderRead(reader);
491
0
      continue;
492
0
    }
493
0
    if (! xmlStrcasecmp(xmlTextReaderConstName(reader),(xmlChar *)"navLabel")) {
494
0
      if (item) {
495
0
        if (! item->label)
496
0
          item->label = NewListAlloc(LIST, NULL, NULL, NULL); //tocLabel
497
0
        AddNode(item->label, NewListNode(item->label, 
498
0
                                         _opf_parse_navlabel(opf, reader)));
499
0
      } else { // Not inside navpoint
500
0
        AddNode(tc->label, NewListNode(tc->label, 
501
0
                                       _opf_parse_navlabel(opf, reader)));
502
0
      }
503
0
    } else if (! xmlStrcasecmp(xmlTextReaderConstName(reader),(xmlChar *)"navInfo")) {
504
0
      AddNode(tc->info, NewListNode(tc->info, 
505
0
                                    _opf_parse_navlabel(opf, reader)));
506
0
      if (item)
507
0
        _epub_print_debug(opf->epub, DEBUG_WARNING, 
508
0
                          "nav info inside nav target element");
509
0
    } else 
510
0
      if (! xmlStrcasecmp(xmlTextReaderConstName(reader),(xmlChar *)"content")) {
511
0
        if (item)
512
0
          item->src = xmlTextReaderGetAttribute(reader, (xmlChar *)"src");
513
0
        else
514
0
          _epub_print_debug(opf->epub, DEBUG_WARNING, 
515
0
                            "content not inside nav target element");  
516
0
      }
517
518
0
    ret = xmlTextReaderRead(reader);
519
0
  }
520
  
521
0
  opf->toc->navList = tc;
522
0
  _epub_print_debug(opf->epub, DEBUG_INFO, "finished parsing nav list");
523
  
524
0
}
525
526
0
void _opf_parse_pagelist(struct opf *opf, xmlTextReaderPtr reader) {
527
0
  int ret;
528
0
  struct tocCategory *tc = _opf_init_toc_category();
529
0
  struct tocItem *item = NULL;
530
  
531
0
  tc->id = xmlTextReaderGetAttribute(reader, (xmlChar *)"id");
532
0
  tc->class = xmlTextReaderGetAttribute(reader, (xmlChar *)"class");
533
  
534
0
  _epub_print_debug(opf->epub, DEBUG_INFO, "parsing page list");
535
  
536
0
  ret = xmlTextReaderRead(reader);
537
0
  while (ret == 1 && 
538
0
         xmlStrcasecmp(xmlTextReaderConstName(reader),(xmlChar *)"pageList")) {
539
0
    if (! xmlStrcasecmp(xmlTextReaderConstName(reader),(xmlChar *)"pageTarget")) {
540
0
      if (xmlTextReaderNodeType(reader) == 1) {
541
0
        item = _opf_init_toc_item(1);
542
0
        item->id = xmlTextReaderGetAttribute(reader, (xmlChar *)"id");
543
0
        item->class = xmlTextReaderGetAttribute(reader, (xmlChar *)"class");
544
0
        item->type  = xmlTextReaderGetAttribute(reader, (xmlChar *)"type");
545
        
546
0
        item->playOrder = _get_attribute_as_positive_int(reader, (xmlChar *)"playOrder");
547
0
        if (item->playOrder == -1) {
548
0
          _epub_print_debug(opf->epub, DEBUG_WARNING, 
549
0
                            "- missing play order in page target element");
550
0
        }
551
0
        item->value = _get_attribute_as_positive_int(reader, (xmlChar *)"value"); 
552
0
      } else if (xmlTextReaderNodeType(reader) == 15) {
553
0
        if (item) {
554
0
          _epub_print_debug(opf->epub, DEBUG_INFO, 
555
0
                            "adding page target item->%s %s (d:%d,p:%d)", 
556
0
                            item->id, item->src, item->depth, item->playOrder);
557
0
          AddNode(tc->items, NewListNode(tc->items, item)); 
558
0
          AddNode(opf->toc->playOrder, NewListNode(opf->toc->playOrder, item));
559
0
          item = NULL;
560
0
        } else {
561
0
          _epub_print_debug(opf->epub, DEBUG_ERROR, "empty item in nav list"); 
562
0
        }
563
0
      }
564
0
    }
565
566
     // ignore non starting tags
567
0
    if (xmlTextReaderNodeType(reader) != 1) {
568
0
      ret = xmlTextReaderRead(reader);
569
0
      continue;
570
0
    }
571
572
0
    if (! xmlStrcasecmp(xmlTextReaderConstName(reader),(xmlChar *)"navLabel")) {
573
0
      if (item) {
574
0
        if (! item->label)
575
0
          item->label = NewListAlloc(LIST, NULL, NULL, NULL); //tocLabel
576
0
        AddNode(item->label, NewListNode(item->label, 
577
0
                                         _opf_parse_navlabel(opf, reader)));
578
0
      } else { // Not inside navpoint
579
0
        AddNode(tc->label, NewListNode(tc->label, 
580
0
                                       _opf_parse_navlabel(opf, reader)));
581
0
      }
582
0
    } else if (! xmlStrcasecmp(xmlTextReaderConstName(reader),(xmlChar *)"navInfo")) {
583
0
      AddNode(tc->info, NewListNode(tc->info, 
584
0
                                    _opf_parse_navlabel(opf, reader)));
585
0
      if (item)
586
0
        _epub_print_debug(opf->epub, DEBUG_WARNING, 
587
0
                          "nav info inside page target element");
588
0
    } else 
589
0
      if (! xmlStrcasecmp(xmlTextReaderConstName(reader),(xmlChar *)"content")) {
590
0
        if (item)
591
0
          item->src = xmlTextReaderGetAttribute(reader, (xmlChar *)"src");
592
0
        else
593
0
          _epub_print_debug(opf->epub, DEBUG_WARNING, 
594
0
                            "content not inside nav target element");  
595
0
      }
596
597
0
    ret = xmlTextReaderRead(reader);
598
0
  }
599
  
600
0
  opf->toc->navList = tc;
601
0
  _epub_print_debug(opf->epub, DEBUG_INFO, "finished parsing page list");
602
    
603
0
}
604
605
167
void _opf_parse_toc(struct opf *opf, char *tocStr, int size) {
606
607
167
  xmlTextReaderPtr reader;
608
167
  int ret;
609
610
167
  _epub_print_debug(opf->epub, DEBUG_INFO, "building toc");
611
  
612
167
  opf->toc = _opf_init_toc();
613
  
614
167
  _epub_print_debug(opf->epub, DEBUG_INFO, "parsing toc");
615
  
616
167
  reader = xmlReaderForMemory(tocStr, size, "TOC", NULL, 0);
617
  
618
167
  if (reader != NULL) {
619
167
    ret = xmlTextReaderRead(reader);
620
    
621
2.03k
    while (ret == 1) {
622
      
623
1.87k
      const xmlChar *name = xmlTextReaderConstName(reader);
624
1.87k
      if (xmlStrcasecmp(name, (xmlChar *)"navList") == 0)
625
0
        _opf_parse_navlist(opf, reader);
626
1.87k
      else 
627
1.87k
      if (xmlStrcasecmp(name, (xmlChar *)"navMap") == 0)
628
64
        _opf_parse_navmap(opf, reader);
629
1.80k
      else 
630
1.80k
      if (xmlStrcasecmp(name, (xmlChar *)"pageList") == 0)
631
0
        _opf_parse_pagelist(opf, reader);
632
633
1.87k
      ret = xmlTextReaderRead(reader);
634
1.87k
    }
635
636
167
    xmlFreeTextReader(reader);
637
167
    if (ret != 0) {
638
157
      _epub_print_debug(opf->epub, DEBUG_ERROR, "failed to parse toc");
639
157
    }
640
167
  } else {
641
0
    _epub_print_debug(opf->epub, DEBUG_ERROR, "unable to open toc reader");
642
0
  }
643
644
167
  SortList(opf->toc->playOrder);
645
167
  _epub_print_debug(opf->epub, DEBUG_INFO, "finished parsing toc");
646
167
}      
647
648
571
void _opf_parse_spine(struct opf *opf, xmlTextReaderPtr reader) {
649
571
  int ret;
650
571
  xmlChar *linear;
651
652
571
  _epub_print_debug(opf->epub, DEBUG_INFO, "parsing spine");
653
  
654
571
  opf->spine = NewListAlloc(LIST, NULL, NULL, NULL); 
655
571
  opf->tocName = xmlTextReaderGetAttribute(reader, (xmlChar *)"toc");
656
  
657
571
  if (opf->tocName) { 
658
541
    char *tocStr = NULL;
659
541
    struct manifest *item;
660
541
    int size;
661
662
541
    _epub_print_debug(opf->epub, DEBUG_INFO, "toc is %s", opf->tocName);
663
    
664
541
    item = _opf_manifest_get_by_id(opf, opf->tocName);
665
541
  if (item != NULL) {
666
476
    size = _ocf_get_data_file(opf->epub->ocf, (char *)item->href, &tocStr);
667
    
668
476
    if (size <= 0) {
669
309
      _epub_print_debug(opf->epub, DEBUG_ERROR, "Faulty toc file %s",
670
309
                opf->tocName);
671
309
    } else {
672
167
      _opf_parse_toc(opf, tocStr, size);
673
167
      free(tocStr);
674
167
    }
675
476
  } else {
676
65
    _epub_print_debug(opf->epub, DEBUG_ERROR, "Toc not in manifest (-) %s",
677
65
              opf->tocName);
678
65
  }
679
541
  } else {
680
30
    _epub_print_debug(opf->epub, DEBUG_WARNING, "toc not found (-)"); 
681
30
    opf->toc = NULL;
682
30
  }
683
684
  
685
571
  ret = xmlTextReaderRead(reader);
686
3.31k
  while (ret == 1 && 
687
3.29k
         xmlStrcasecmp(xmlTextReaderConstLocalName(reader), (xmlChar *)"spine")) {
688
2.74k
    struct spine *item;
689
  
690
    // ignore non starting tags
691
2.74k
    if (xmlTextReaderNodeType(reader) != 1) {
692
1.64k
      ret = xmlTextReaderRead(reader);
693
1.64k
      continue;
694
1.64k
    }
695
696
1.10k
    item = malloc(sizeof(struct spine));
697
1.10k
  memset(item, 0, sizeof(struct spine));
698
699
1.10k
    item->idref = xmlTextReaderGetAttribute(reader, (xmlChar *)"idref");
700
1.10k
    linear = xmlTextReaderGetAttribute(reader, (xmlChar *)"linear");
701
1.10k
    if (linear && xmlStrcasecmp(linear, (xmlChar *)"no") == 0) {
702
0
      item->linear = 0;
703
1.10k
    } else {
704
1.10k
        item->linear = 1;
705
1.10k
        opf->linearCount++;
706
1.10k
    }
707
708
1.10k
    if(linear)
709
0
        free(linear);
710
    
711
1.10k
     AddNode(opf->spine, NewListNode(opf->spine, item));
712
     
713
    // decide what to do with non linear items
714
1.10k
    _epub_print_debug(opf->epub, DEBUG_INFO, "found item %s", item->idref);
715
    
716
1.10k
    ret = xmlTextReaderRead(reader);
717
1.10k
  }
718
571
}
719
720
564
void _opf_parse_manifest(struct opf *opf, xmlTextReaderPtr reader) {
721
564
  int ret;
722
  
723
564
  _epub_print_debug(opf->epub, DEBUG_INFO, "parsing manifest");
724
725
564
  opf->manifest = NewListAlloc(LIST, NULL, NULL, 
726
564
                               (NodeCompareFunc)_list_cmp_manifest_by_id );
727
728
564
  ret = xmlTextReaderRead(reader);
729
730
7.35k
  while (ret == 1 && 
731
7.29k
         xmlStrcasecmp(xmlTextReaderConstLocalName(reader),(xmlChar *)"manifest")) {
732
6.78k
    struct manifest *item;
733
734
    // ignore non starting tags
735
6.78k
    if (xmlTextReaderNodeType(reader) != 1) {
736
3.64k
      ret = xmlTextReaderRead(reader);
737
3.64k
      continue;
738
3.64k
    }
739
740
3.14k
    item = malloc(sizeof(struct manifest));
741
742
3.14k
    item->id = xmlTextReaderGetAttribute(reader, (xmlChar *)"id");
743
3.14k
    item->href = xmlTextReaderGetAttribute(reader, (xmlChar *)"href");
744
3.14k
    item->type = xmlTextReaderGetAttribute(reader, (xmlChar *)"media-type");
745
3.14k
    item->fallback = xmlTextReaderGetAttribute(reader, (xmlChar *)"fallback");
746
3.14k
    item->fbStyle = 
747
3.14k
      xmlTextReaderGetAttribute(reader, (xmlChar *)"fallback-style");
748
3.14k
    item->nspace = 
749
3.14k
      xmlTextReaderGetAttribute(reader, (xmlChar *)"required-namespace");
750
3.14k
    item->modules = 
751
3.14k
      xmlTextReaderGetAttribute(reader, (xmlChar *)"required-modules");
752
    
753
3.14k
    _epub_print_debug(opf->epub, DEBUG_INFO, 
754
3.14k
                      "manifest item %s href %s media-type %s", 
755
3.14k
                      item->id, item->href, item->type);
756
757
3.14k
    AddNode(opf->manifest, NewListNode(opf->manifest, item));
758
759
3.14k
    ret = xmlTextReaderRead(reader);
760
3.14k
  }
761
564
}
762
763
2.00k
struct manifest *_opf_manifest_get_by_id(struct opf *opf, xmlChar* id) {
764
2.00k
  struct manifest data;
765
2.00k
  data.id = id;
766
  
767
2.00k
  return FindNode(opf->manifest, &data);
768
  
769
2.00k
}
770
771
530
void _opf_parse_guide(struct opf *opf, xmlTextReaderPtr reader) {
772
530
  int ret;
773
530
  struct guide *item;
774
775
530
  _epub_print_debug(opf->epub, DEBUG_INFO, "parsing guides");
776
777
530
  opf->guide = NewListAlloc(LIST, NULL, NULL, NULL);
778
779
530
  ret = xmlTextReaderRead(reader);
780
3.62k
  while (ret == 1 && 
781
3.09k
         xmlStrcasecmp(xmlTextReaderConstLocalName(reader),(xmlChar *)"guides")) {
782
783
    // ignore non starting tags
784
3.09k
    if (xmlTextReaderNodeType(reader) != 1) {
785
2.57k
      ret = xmlTextReaderRead(reader);
786
2.57k
      continue;
787
2.57k
    }
788
    
789
521
    item = malloc(sizeof(struct guide));
790
521
    item->type = xmlTextReaderGetAttribute(reader, (xmlChar *)"type");
791
521
    item->title = xmlTextReaderGetAttribute(reader, (xmlChar *)"title");
792
521
    item->href = xmlTextReaderGetAttribute(reader, (xmlChar *)"href");
793
794
521
    _epub_print_debug(opf->epub, DEBUG_INFO, 
795
521
                      "guide item: %s href: %s type: %s", 
796
521
                      item->title, item->href, item->type);
797
521
    AddNode(opf->guide, NewListNode(opf->guide, item));
798
521
    ret = xmlTextReaderRead(reader);
799
521
  }
800
530
}
801
802
0
listPtr _opf_parse_tour(struct opf *opf, xmlTextReaderPtr reader) {
803
0
  int ret;
804
0
  listPtr tour = NewListAlloc(LIST, NULL, NULL, NULL);
805
0
  struct site *item;
806
807
0
  ret = xmlTextReaderRead(reader);
808
  
809
0
  while (ret == 1 && 
810
0
         xmlStrcasecmp(xmlTextReaderConstLocalName(reader),(xmlChar *)"tour")) {
811
812
    // ignore non starting tags
813
0
    if (xmlTextReaderNodeType(reader) != 1) {
814
0
      ret = xmlTextReaderRead(reader);
815
0
      continue;
816
0
    }
817
    
818
0
    item = malloc(sizeof(struct site));
819
0
    item->title = xmlTextReaderGetAttribute(reader, (xmlChar *)"title");
820
0
    item->href = xmlTextReaderGetAttribute(reader, (xmlChar *)"href");
821
0
    _epub_print_debug(opf->epub, DEBUG_INFO, 
822
0
                      "site: %s href: %s", 
823
0
                      item->title, item->href);
824
0
    AddNode(tour, NewListNode(tour, item));
825
826
0
    ret = xmlTextReaderRead(reader);
827
0
  }
828
829
0
  return tour;
830
0
}
831
832
0
void _opf_parse_tours(struct opf *opf, xmlTextReaderPtr reader) {
833
0
  int ret;
834
0
  struct tour *item;
835
836
0
  _epub_print_debug(opf->epub, DEBUG_INFO, "parsing tours");
837
838
0
  opf->tours = NewListAlloc(LIST, NULL, NULL, NULL);
839
840
0
  ret = xmlTextReaderRead(reader);
841
  
842
0
  while (ret == 1 && 
843
0
         xmlStrcasecmp(xmlTextReaderConstLocalName(reader), (xmlChar *)"tours")) {
844
    
845
    // ignore non starting tags
846
0
    if (xmlTextReaderNodeType(reader) != 1) {
847
0
      ret = xmlTextReaderRead(reader);
848
0
      continue;
849
0
    }
850
    
851
0
    item = malloc(sizeof(struct tour));
852
   
853
0
    item->title = xmlTextReaderGetAttribute(reader, (xmlChar *)"title");
854
0
    item->id = xmlTextReaderGetAttribute(reader, (xmlChar *)"id");
855
0
    _epub_print_debug(opf->epub, DEBUG_INFO, 
856
0
                      "tour: %s id: %s", 
857
0
                      item->title, item->id);
858
0
    item->sites = _opf_parse_tour(opf, reader);
859
0
    AddNode(opf->tours, NewListNode(opf->tours, item));
860
861
0
    ret = xmlTextReaderRead(reader);
862
0
  }
863
0
}
864
865
63
xmlChar *_opf_label_get_by_lang(struct opf *opf, listPtr label, char *lang) {
866
63
  struct tocLabel data, *tmp;
867
63
  data.lang = (xmlChar *)lang;
868
63
  label->compare = (NodeCompareFunc)_list_cmp_label_by_lang;
869
63
  tmp =  FindNode(label, &data);
870
63
  return (tmp?tmp->text:NULL);
871
  
872
63
}
873
874
63
xmlChar *_opf_label_get_by_doc_lang(struct opf *opf, listPtr label) {
875
63
  opf->metadata->lang->Current = opf->metadata->lang->Head;
876
63
  return _opf_label_get_by_lang(opf, label, 
877
63
                                (char *)GetNode(opf->metadata->lang));
878
63
}
879
880
0
void _opf_dump(struct opf *opf) {
881
0
  printf("Title(s):\n   ");
882
0
  DumpList(opf->metadata->title, (ListDumpFunc)_list_dump_string);
883
0
  printf("Creator(s):\n   ");
884
0
  DumpList(opf->metadata->creator, (ListDumpFunc)_list_dump_creator);
885
0
  printf("Identifier(s):\n   ");
886
0
  DumpList(opf->metadata->id, (ListDumpFunc)_list_dump_id);
887
0
  printf("Reading order:\n   ");
888
0
  DumpList(opf->spine, (ListDumpFunc)_list_dump_spine);
889
0
  printf("\n");
890
0
  if (opf->guide) {
891
0
    printf("Guide:\n");
892
0
    DumpList(opf->guide, (ListDumpFunc)_list_dump_guide);
893
0
  }
894
0
  if (opf->tours)
895
0
    DumpList(opf->tours, (ListDumpFunc)_list_dump_tour);
896
0
  if (opf->metadata->meta->Size != 0) {
897
0
    printf("Extra local metadata:\n");
898
0
    DumpList(opf->metadata->meta, (ListDumpFunc)_list_dump_meta);
899
0
  }
900
0
}
901
902
516
void _opf_close(struct opf *opf) {
903
516
  if (opf->metadata)
904
508
    _opf_free_metadata(opf->metadata);
905
516
  if (opf->toc)
906
139
    _opf_free_toc(opf->toc);
907
516
  if (opf->spine)
908
516
    FreeList(opf->spine, (ListFreeFunc)_list_free_spine);
909
516
  if (opf->tocName)
910
490
    free(opf->tocName);
911
516
  if (opf->manifest)
912
459
    FreeList(opf->manifest, (ListFreeFunc)_list_free_manifest);
913
516
  if (opf->guide)
914
503
    FreeList(opf->guide, (ListFreeFunc)_list_free_guide);
915
516
  if (opf->tours)
916
0
    FreeList(opf->tours, (ListFreeFunc)_list_free_tours);
917
516
  free(opf);
918
516
}