1 /* Internal bookmarks XBEL bookmarks basic support */
4 * TODO: Decent XML output.
5 * TODO: Validation of the document (with librxp?). An invalid document can
7 * TODO: Support all the XBEL elements. */
11 #endif /* HAVE_CONFIG_H */
23 #include "bfu/dialog.h"
24 #include "bookmarks/bookmarks.h"
25 #include "bookmarks/backend/common.h"
26 #include "bookmarks/backend/xbel.h"
27 #include "intl/charsets.h"
28 #include "intl/gettext/libintl.h"
29 #include "util/conv.h"
30 #include "util/lists.h"
31 #include "util/string.h"
33 #define BOOKMARKS_XBEL_FILENAME "bookmarks.xbel"
36 /* Elements' attributes */
38 LIST_HEAD(struct attributes
);
44 static void on_element_open(void *data
, const char *name
, const char **attr
);
45 static void on_element_close(void *data
, const char *name
);
46 static void on_text(void *data
, const XML_Char
*text
, int len
);
48 static struct tree_node
*new_node(struct tree_node
*parent
);
49 static void free_node(struct tree_node
*node
);
50 static void free_xbeltree(struct tree_node
*node
);
51 static struct tree_node
*get_child(struct tree_node
*node
, unsigned char *name
);
52 static unsigned char *get_attribute_value(struct tree_node
*node
,
56 static void read_bookmarks_xbel(FILE *f
);
57 static unsigned char * filename_bookmarks_xbel(int writing
);
58 static int xbeltree_to_bookmarks_list(struct tree_node
*root
,
59 struct bookmark
*current_parent
);
60 static void write_bookmarks_list(struct secure_save_info
*ssi
,
61 LIST_OF(struct bookmark
) *bookmarks_list
,
62 int n
, int folder_state
);
63 static void write_bookmarks_xbel(struct secure_save_info
*ssi
,
64 LIST_OF(struct bookmark
) *bookmarks_list
);
68 unsigned char *name
; /* Name of the element */
69 unsigned char *text
; /* Text inside the element */
70 LIST_OF(struct attributes
) attrs
;
71 struct tree_node
*parent
;
72 struct tree_node
*children
;
74 struct tree_node
*prev
;
75 struct tree_node
*next
;
78 static struct tree_node
*root_node
= NULL
;
79 static struct tree_node
*current_node
= NULL
;
81 /* This is 1 so that we won't fail miserably if we read bookmarks in a
82 * different format. */
83 static int readok
= 1;
86 read_bookmarks_xbel(FILE *f
)
88 unsigned char in_buffer
[BUFSIZ
];
95 p
= XML_ParserCreate(NULL
);
97 ERROR(gettext("read_bookmarks_xbel(): Error in XML_ParserCreate()"));
101 XML_SetElementHandler(p
, on_element_open
, on_element_close
);
102 XML_SetCharacterDataHandler(p
, on_text
);
104 while (!done
&& !err
) {
105 size_t len
= fread(in_buffer
, 1, BUFSIZ
, f
);
108 ERROR(gettext("read_bookmarks_xbel(): Error reading %s"),
109 filename_bookmarks_xbel(0));
115 if (!err
&& !XML_Parse(p
, in_buffer
, len
, done
)) {
116 usrerror(gettext("Parse error while processing "
117 "XBEL bookmarks in %s at line %d "
119 filename_bookmarks_xbel(0),
120 XML_GetCurrentLineNumber(p
),
121 XML_GetCurrentColumnNumber(p
),
122 XML_ErrorString(XML_GetErrorCode(p
)));
128 if (!err
) readok
= xbeltree_to_bookmarks_list(root_node
->children
, NULL
); /* Top node is xbel */
131 free_xbeltree(root_node
);
136 write_bookmarks_xbel(struct secure_save_info
*ssi
,
137 LIST_OF(struct bookmarks
) *bookmarks_list
)
139 int folder_state
= get_opt_bool("bookmarks.folder_state");
140 /* We check for readok in filename_bookmarks_xbel(). */
143 "<?xml version=\"1.0\"?>\n"
144 "<!DOCTYPE xbel PUBLIC \"+//IDN python.org//DTD XML "
145 "Bookmark Exchange Language 1.0//EN//XML\"\n"
147 "\"http://www.python.org/topics/xml/dtds/xbel-1.0.dtd\">\n\n"
151 write_bookmarks_list(ssi
, bookmarks_list
, 0, folder_state
);
152 secure_fputs(ssi
, "\n</xbel>\n");
155 static unsigned char *
156 filename_bookmarks_xbel(int writing
)
158 if (writing
&& !readok
) return NULL
;
159 return BOOKMARKS_XBEL_FILENAME
;
163 indentation(struct secure_save_info
*ssi
, int num
)
167 for (i
= 0; i
< num
; i
++)
168 secure_fputs(ssi
, " ");
171 /* FIXME This is totally broken, we should use the Unicode value in
173 * Additionally it is slow, not elegant, incomplete and
174 * if you pay enough attention you can smell the unmistakable
175 * odor of doom coming from it. --fabio */
177 print_xml_entities(struct secure_save_info
*ssi
, const unsigned char *str
)
179 #define accept_char(x) (isident((x)) || (x) == ' ' || (x) == '.' \
180 || (x) == ':' || (x) == ';' \
181 || (x) == '/' || (x) == '(' \
182 || (x) == ')' || (x) == '}' \
183 || (x) == '{' || (x) == '%' \
188 if (cp
== -1) cp
= get_cp_index("us-ascii");
190 for (; *str
; str
++) {
191 if (accept_char(*str
))
192 secure_fputc(ssi
, *str
);
195 secure_fprintf(ssi
, "&#%i;", (int) *str
);
198 const unsigned char *s
= u2cp_no_nbsp(*str
, cp
);
200 if (s
) print_xml_entities(ssi
, s
);
210 write_bookmarks_list(struct secure_save_info
*ssi
,
211 LIST_OF(struct bookmark
) *bookmarks_list
,
212 int n
, int folder_state
)
216 foreach (bm
, *bookmarks_list
) {
217 indentation(ssi
, n
+ 1);
219 if (bm
->box_item
->type
== BI_FOLDER
) {
220 int expanded
= folder_state
&& bm
->box_item
->expanded
;
222 secure_fputs(ssi
, "<folder folded=\"");
223 secure_fputs(ssi
, expanded
? "no" : "yes");
224 secure_fputs(ssi
, "\">\n");
226 indentation(ssi
, n
+ 2);
227 secure_fputs(ssi
, "<title>");
228 print_xml_entities(ssi
, bm
->title
);
229 secure_fputs(ssi
, "</title>\n");
231 if (!list_empty(bm
->child
))
232 write_bookmarks_list(ssi
, &bm
->child
, n
+ 2, folder_state
);
234 indentation(ssi
, n
+ 1);
235 secure_fputs(ssi
, "</folder>\n\n");
237 } else if (bm
->box_item
->type
== BI_LEAF
) {
239 secure_fputs(ssi
, "<bookmark href=\"");
240 print_xml_entities(ssi
, bm
->url
);
241 secure_fputs(ssi
, "\">\n");
243 indentation(ssi
, n
+ 2);
244 secure_fputs(ssi
, "<title>");
245 print_xml_entities(ssi
, bm
->title
);
246 secure_fputs(ssi
, "</title>\n");
248 indentation(ssi
, n
+ 1);
249 secure_fputs(ssi
, "</bookmark>\n\n");
251 } else if (bm
->box_item
->type
== BI_SEPARATOR
) {
252 secure_fputs(ssi
, "<separator/>\n\n");
258 on_element_open(void *data
, const char *name
, const char **attr
)
260 struct attributes
*attribute
;
261 struct tree_node
*node
;
263 node
= new_node(current_node
);
267 if (current_node
->children
) {
268 struct tree_node
*tmp
;
270 tmp
= current_node
->children
;
271 current_node
->children
= node
;
272 current_node
->children
->next
= tmp
;
273 current_node
->children
->prev
= NULL
;
275 else current_node
->children
= node
;
277 else root_node
= node
;
281 current_node
->name
= stracpy((unsigned char *) name
);
282 if (!current_node
->name
) {
283 mem_free(current_node
);
288 unsigned char *tmp
= stracpy((unsigned char *) *attr
);
291 free_node(current_node
);
295 attribute
= mem_calloc(1, sizeof(*attribute
));
298 free_node(current_node
);
302 attribute
->name
= tmp
;
304 add_to_list(current_node
->attrs
, attribute
);
312 on_element_close(void *data
, const char *name
)
314 current_node
= current_node
->parent
;
317 static unsigned char *
318 delete_whites(unsigned char *s
)
321 int count
= 0, c
= 0, i
;
324 r
= mem_alloc(len
+ 1);
327 for (i
= 0; i
< len
; i
++) {
329 if (count
== 1) continue;
334 if (s
[i
] == '\n' || s
[i
] == '\t')
341 /* XXX This should never return NULL, right? wrong! --fabio */
342 /* r = mem_realloc(r, strlen(r + 1)); */
349 on_text(void *data
, const XML_Char
*text
, int len
)
355 len2
= current_node
->text
? strlen(current_node
->text
) : 0;
357 tmp
= mem_realloc(current_node
->text
, (size_t) (len
+ 1 + len2
));
362 strncpy(tmp
+ len2
, text
, len
);
363 tmp
[len
+ len2
] = '\0';
364 current_node
->text
= delete_whites(tmp
);
370 /* xbel_tree_to_bookmarks_list: returns 0 on fail,
373 xbeltree_to_bookmarks_list(struct tree_node
*node
,
374 struct bookmark
*current_parent
)
376 struct bookmark
*tmp
;
377 struct tree_node
*title
;
378 static struct bookmark
*lastbm
;
381 if (!strcmp(node
->name
, "bookmark")) {
384 title
= get_child(node
, "title");
385 href
= get_attribute_value(node
, "href");
387 tmp
= add_bookmark(current_parent
, 0,
388 /* The <title> element is optional */
389 title
&& title
->text
? title
->text
390 : (unsigned char *) gettext("No title"),
391 /* XXX: The href attribute isn't optional but
392 * we don't validate the source XML yet, so
393 * we can't always assume a non NULL value for
394 * get_attribute_value() */
396 : (unsigned char *) gettext("No URL"));
401 tmp
->root
= current_parent
;
404 } else if (!strcmp(node
->name
, "folder")) {
405 unsigned char *folded
;
407 title
= get_child(node
, "title");
409 tmp
= add_bookmark(current_parent
, 0,
410 title
&& title
->text
? title
->text
411 : (unsigned char *) gettext("No title"),
417 folded
= get_attribute_value(node
, "folded");
418 if (folded
&& !strcmp(folded
, "no"))
419 tmp
->box_item
->expanded
= 1;
423 } else if (!strcmp(node
->name
, "separator")) {
424 tmp
= add_bookmark(current_parent
, 0, "-", "");
428 tmp
->root
= current_parent
;
432 if (node
->children
) {
435 /* If this node is a <folder> element, current parent
437 ret
= (!strcmp(node
->name
, "folder") ?
438 xbeltree_to_bookmarks_list(node
->children
,
440 xbeltree_to_bookmarks_list(node
->children
,
454 free_xbeltree(struct tree_node
*node
)
456 struct tree_node
*next_node
;
461 free_xbeltree(node
->children
);
463 next_node
= node
->next
;
470 static struct tree_node
*
471 get_child(struct tree_node
*node
, unsigned char *name
)
473 struct tree_node
*ret
;
475 if (!node
) return NULL
;
477 ret
= node
->children
;
480 if (!strcmp(name
, ret
->name
)) {
489 static unsigned char *
490 get_attribute_value(struct tree_node
*node
, unsigned char *name
)
492 struct attributes
*attribute
;
494 foreachback (attribute
, node
->attrs
) {
495 if (!strcmp(attribute
->name
, name
)) {
496 return attribute
->prev
->name
;
503 static struct tree_node
*
504 new_node(struct tree_node
*parent
)
506 struct tree_node
*node
;
508 node
= mem_calloc(1, sizeof(*node
));
509 if (!node
) return NULL
;
511 node
->parent
= parent
? parent
: node
;
512 init_list(node
->attrs
);
518 free_node(struct tree_node
*node
)
520 struct attributes
*attribute
;
522 foreachback (attribute
, node
->attrs
)
523 mem_free_if(attribute
->name
);
524 free_list(node
->attrs
); /* Don't free list during traversal */
526 mem_free_if(node
->name
);
527 mem_free_if(node
->text
);
532 /* Read and write functions for the XBEL backend */
533 struct bookmarks_backend xbel_bookmarks_backend
= {
534 filename_bookmarks_xbel
,
536 write_bookmarks_xbel
,