diff options
author | James Bursa <james@netsurf-browser.org> | 2004-07-29 11:40:48 +0000 |
---|---|---|
committer | James Bursa <james@netsurf-browser.org> | 2004-07-29 11:40:48 +0000 |
commit | f2ebc60ef0f00f01ff6611d534e201e13e8d1f86 (patch) | |
tree | 847ec70ea49cfa67ec159e3a2bf1c8e72492ee65 /riscos/hotlist.c | |
parent | 386aba4f5901877f26825158a936626d7b6d050b (diff) | |
download | netsurf-f2ebc60ef0f00f01ff6611d534e201e13e8d1f86.tar.gz netsurf-f2ebc60ef0f00f01ff6611d534e201e13e8d1f86.tar.bz2 |
[project @ 2004-07-29 11:40:48 by bursa]
Rewrite saving to use libxml2. Clean up and simplify loading.
svn path=/import/netsurf/; revision=1162
Diffstat (limited to 'riscos/hotlist.c')
-rw-r--r-- | riscos/hotlist.c | 526 |
1 files changed, 361 insertions, 165 deletions
diff --git a/riscos/hotlist.c b/riscos/hotlist.c index d051df3c3..36b34a774 100644 --- a/riscos/hotlist.c +++ b/riscos/hotlist.c @@ -15,6 +15,7 @@ #include <time.h> #include <swis.h> #include "libxml/HTMLparser.h" +#include "libxml/HTMLtree.h" #include "oslib/colourtrans.h" #include "oslib/dragasprite.h" #include "oslib/osfile.h" @@ -68,7 +69,7 @@ struct hotlist_entry { */ int children; - /** The title of the hotlist entry/folder + /** The title of the hotlist entry/folder, UTF-8. */ char *title; @@ -202,16 +203,21 @@ bool dialog_folder_add = false; bool dialog_entry_add = false; bool hotlist_insert = false; -/* Hotlist loading buffers -*/ -static char *load_title = NULL; -static char *load_url = NULL; - static bool ro_gui_hotlist_initialise_sprite(const char *name, int number); static bool ro_gui_hotlist_load(void); -static void ro_gui_hotlist_load_entry(xmlNode *cur, struct hotlist_entry *entry, bool allow_add); -static bool ro_gui_hotlist_save_entry(FILE *fp, struct hotlist_entry *entry); +static void ro_gui_hotlist_load_file(const char *filename); +static void ro_gui_hotlist_load_directory(xmlNode *ul, + struct hotlist_entry *directory); +static void ro_gui_hotlist_load_entry(xmlNode *li, + struct hotlist_entry *directory); +xmlNode *ro_gui_hotlist_find_element(xmlNode *node, const char *name); +bool ro_gui_hotlist_save_directory(struct hotlist_entry *directory, + xmlNode *node); +bool ro_gui_hotlist_save_entry(struct hotlist_entry *entry, + xmlNode *node); +bool ro_gui_hotlist_save_entry_comment(xmlNode *node, + const char *name, int value); static void ro_gui_hotlist_link_entry(struct hotlist_entry *link, struct hotlist_entry *entry, bool before); static void ro_gui_hotlist_delink_entry(struct hotlist_entry *entry); static void ro_gui_hotlist_delete_entry(struct hotlist_entry *entry, bool siblings); @@ -405,11 +411,9 @@ void ro_gui_hotlist_show(void) { bool ro_gui_hotlist_load(void) { - htmlDocPtr doc; - const char *encoding; fileswitch_object_type obj_type = 0; struct hotlist_entry *netsurf; - struct hotlist_entry *entry = &root; + struct hotlist_entry *entry; /* Check if we have an initial hotlist. OS_File does funny things relating to errors, so we use the object type to determine success @@ -417,37 +421,30 @@ bool ro_gui_hotlist_load(void) { xosfile_read_stamped_no_path("Choices:WWW.NetSurf.Hotlist", &obj_type, (bits)0, (bits)0, (int *)0, (fileswitch_attr)0, (bits)0); if (obj_type == fileswitch_IS_FILE) { - /* Read our file - */ - encoding = xmlGetCharEncodingName(XML_CHAR_ENCODING_8859_1); - doc = htmlParseFile("Choices:WWW.NetSurf.Hotlist", encoding); - if ((!doc) || (!(doc->children))) { - xmlFreeDoc(doc); - warn_user("HotlistLoadError", 0); - return false; - } - - /* Perform our recursive load - */ - ro_gui_hotlist_load_entry(doc->children, &root, true); - - /* Exit cleanly - */ - xmlFreeDoc(doc); + ro_gui_hotlist_load_file("Choices:WWW.NetSurf.Hotlist"); return true; + } else { /* Create a folder */ netsurf = ro_gui_hotlist_create_entry("NetSurf", NULL, 0, &root); + if (!netsurf) + return false; /* Add some content */ - entry = ro_gui_hotlist_create_entry("NetSurf homepage", "http://netsurf.sourceforge.net/", + entry = ro_gui_hotlist_create_entry("NetSurf homepage", + "http://netsurf.sourceforge.net/", 0xfaf, netsurf); - entry->add_date = (time_t)-1; - entry = ro_gui_hotlist_create_entry("NetSurf test builds", "http://netsurf.strcprstskrzkrk.co.uk/", + if (!entry) + return false; + entry->add_date = (time_t) -1; + entry = ro_gui_hotlist_create_entry("NetSurf test builds", + "http://netsurf.strcprstskrzkrk.co.uk/", 0xfaf, netsurf); - entry->add_date = (time_t)-1; + if (!entry) + return false; + entry->add_date = (time_t) -1; /* We succeeded */ @@ -456,103 +453,182 @@ bool ro_gui_hotlist_load(void) { } -void ro_gui_hotlist_load_entry(xmlNode *cur, struct hotlist_entry *entry, bool allow_add) { - struct hotlist_entry *last_entry = entry; - char *xml_comment = NULL; - char *comment = comment; - int filetype = 0xfaf; - int add_date = -1; - int last_date = -1; - int visits = 0; - bool add_entry; +/** + * Load the hotlist from file. + * + * \param filename name of file to read + */ - while (true) { - /* Add any items that have had all the data they can have - */ - if ((allow_add) && (load_title != NULL)) { - if ((cur == NULL) || ((cur->type == XML_ELEMENT_NODE) && - ((!(strcmp(cur->name, "li"))) || (!(strcmp(cur->name, "h4"))) || - (!(strcmp(cur->name, "ul")))))) { +void ro_gui_hotlist_load_file(const char *filename) +{ + xmlDoc *doc; + xmlNode *html, *body, *ul; - /* Add the entry - */ - last_entry = ro_gui_hotlist_create_entry(load_title, load_url, filetype, entry); - last_entry->add_date = add_date; - if (last_entry->url) { - last_entry->last_date = last_date; - last_entry->visits = visits; - last_entry->filetype = filetype; - } + doc = htmlParseFile(filename, "iso-8859-1"); + if (!doc) { + warn_user("HotlistLoadError", messages_get("ParsingFail")); + return; + } - /* Reset our variables - */ - if (load_title) xmlFree(load_title); - load_title = NULL; - if (load_url) xmlFree(load_url); - load_url = NULL; - filetype = 0xfaf; - add_date = -1; - last_date = -1; - visits = 0; - } - } + html = ro_gui_hotlist_find_element((xmlNode *) doc, "html"); + body = ro_gui_hotlist_find_element(html, "body"); + ul = ro_gui_hotlist_find_element(body, "ul"); + if (!ul) { + xmlFreeDoc(doc); + warn_user("HotlistLoadError", + "(<html>...<body>...<ul> not found.)"); + return; + } - /* Abort if we've ran out of content - */ - if (cur == NULL) return; - - /* Gather further information and recurse - */ - if (cur->type == XML_ELEMENT_NODE) { - add_entry = allow_add; - if (!(strcmp(cur->name, "h4"))) { - add_entry = false; - if (!load_title) load_title = xmlNodeGetContent(cur); - } else if (!(strcmp(cur->name, "a"))) { - add_entry = false; - load_url = (char *)xmlGetProp(cur, (const xmlChar *)"href"); - } else if (!(strcmp(cur->name, "li"))) { - add_entry = false; + ro_gui_hotlist_load_directory(ul, &root); + + xmlFreeDoc(doc); +} + + +/** + * Parse a directory represented as a ul. + * + * \param ul xmlNode for parsed ul + * \param directory directory to add this directory to + */ + +void ro_gui_hotlist_load_directory(xmlNode *ul, + struct hotlist_entry *directory) +{ + char *title; + struct hotlist_entry *dir; + xmlNode *n; + + for (n = ul->children; n; n = n->next) { + /* The ul may contain entries as a li, or directories as + * an h4 followed by a ul. Non-element nodes may be present + * (eg. text, comments), and are ignored. */ + + if (n->type != XML_ELEMENT_NODE) + continue; + + if (strcmp(n->name, "li") == 0) { + /* entry */ + ro_gui_hotlist_load_entry(n, directory); + + } else if (strcmp(n->name, "h4") == 0) { + /* directory */ + title = (char *) xmlNodeGetContent(n); + if (!title) { + warn_user("HotlistLoadError", "(Empty <h4> " + "or memory exhausted.)"); + return; } - if ((cur->children) && (strcmp(cur->name, "h4"))) { - ro_gui_hotlist_load_entry(cur->children, last_entry, add_entry); + for (n = n->next; + n && n->type != XML_ELEMENT_NODE; + n = n->next) + ; + if (strcmp(n->name, "ul") != 0) { + /* next element isn't expected ul */ + free(title); + warn_user("HotlistLoadError", "(Expected " + "<ul> not present.)"); + return; } - } else { - /* Check for comment data - */ - if (!(strcmp(cur->name, "comment"))) { - xml_comment = xmlNodeGetContent(cur); - comment = xml_comment; - while (comment[0] == ' ') comment++; - if (strncmp("Added:", comment, 6) == 0) { - add_date = atoi(comment + 6); - } else if (strncmp("LastVisit:", comment, 10) == 0) { - last_date = atoi(comment + 10); - } else if (strncmp("Visits:", comment, 7) == 0) { - visits = atoi(comment + 7); - } else if (strncmp("Type:", comment, 5) == 0) { - filetype = atoi(comment + 5); - } - xmlFree(xml_comment); - } else if (!(strcmp(cur->name, "text"))) { - if ((!(strcmp(cur->parent->name, "li"))) || (!(strcmp(cur->parent->name, "a")))) { - if (!load_title) load_title = xmlNodeGetContent(cur); - if ((load_title[0] == 13) || (load_title[0] == 10)) { - xmlFree(load_title); - load_title = NULL; - } - } - } + + dir = ro_gui_hotlist_create_entry(title, NULL, 0, + directory); + if (!dir) + return; + ro_gui_hotlist_load_directory(n, dir); } - cur = cur->next; } } /** + * Parse an entry represented as a li. + * + * \param li xmlNode for parsed li + * \param directory directory to add this entry to + */ + +void ro_gui_hotlist_load_entry(xmlNode *li, + struct hotlist_entry *directory) +{ + char *url = 0; + char *title = 0; + int filetype = 0xfaf; + int add_date = -1; + int last_date = -1; + int visits = 0; + char *comment; + struct hotlist_entry *entry; + xmlNode *n; + + for (n = li->children; n; n = n->next) { + /* The li must contain an "a" element, and may contain + * some additional data as comments. */ + + if (n->type == XML_ELEMENT_NODE && + strcmp(n->name, "a") == 0) { + url = (char *) xmlGetProp(n, (const xmlChar *) "href"); + title = (char *) xmlNodeGetContent(n); + + } else if (n->type == XML_COMMENT_NODE) { + comment = (char *) xmlNodeGetContent(n); + if (!comment) + continue; + if (strncmp("Type:", comment, 5) == 0) + filetype = atoi(comment + 5); + else if (strncmp("Added:", comment, 6) == 0) + add_date = atoi(comment + 6); + else if (strncmp("LastVisit:", comment, 10) == 0) + last_date = atoi(comment + 10); + else if (strncmp("Visits:", comment, 7) == 0) + visits = atoi(comment + 7); + } + } + + if (!url || !title) { + warn_user("HotlistLoadError", "(Missing <a> in <li> or " + "memory exhausted.)"); + return; + } + + entry = ro_gui_hotlist_create_entry(title, url, filetype, directory); + if (!entry) + return; + entry->add_date = add_date; + entry->last_date = last_date; + entry->visits = visits; +} + + +/** + * Search the children of an xmlNode for an element. + * + * \param node xmlNode to search children of, or 0 + * \param name name of element to find + * \return first child of node which is an element and matches name, or + * 0 if not found or parameter node is 0 + */ + +xmlNode *ro_gui_hotlist_find_element(xmlNode *node, const char *name) +{ + xmlNode *n; + if (!node) + return 0; + for (n = node->children; + n && !(n->type == XML_ELEMENT_NODE && + strcmp(n->name, name) == 0); + n = n->next) + ; + return n; +} + + +/** * Perform a save to the default file */ + void ro_gui_hotlist_save(void) { /* Don't save if we didn't load */ @@ -572,71 +648,189 @@ void ro_gui_hotlist_save(void) { /** * Perform a save to a specified file * - * /param file the file to save to + * /param filename the file to save to */ -void ro_gui_hotlist_save_as(const char *file) { - FILE *fp; - /* Open our file - */ - fp = fopen(file, "w"); - if (!fp) { - warn_user("HotlistSaveError", 0); +void ro_gui_hotlist_save_as(const char *filename) +{ + int res; + xmlDoc *doc; + xmlNode *html, *head, *title, *body; + + /* Unfortunately the Browse Hotlist format is invalid HTML, + * so this is a lie. */ + doc = htmlNewDoc("http://www.w3.org/TR/html4/strict.dtd", + "-//W3C//DTD HTML 4.01//EN"); + if (!doc) { + warn_user("NoMemory", 0); return; } - /* HTML header - */ - fprintf(fp, "<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\">\n"); - fprintf(fp, "<title>Hotlist</title>\n</head>\n<body>\n"); + html = xmlNewNode(NULL, "html"); + if (!html) { + warn_user("NoMemory", 0); + xmlFreeDoc(doc); + return; + } + xmlDocSetRootElement(doc, html); - /* Start our recursive save - */ - if (!ro_gui_hotlist_save_entry(fp, root.child_entry)) { - warn_user("HotlistSaveError", 0); + head = xmlNewChild(html, NULL, "head", NULL); + if (!head) { + warn_user("NoMemory", 0); + xmlFreeDoc(doc); + return; } - /* HTML footer - */ - fprintf(fp, "</body>\n</html>\n"); + title = xmlNewTextChild(head, NULL, "title", "NetSurf Hotlist"); + if (!title) { + warn_user("NoMemory", 0); + xmlFreeDoc(doc); + return; + } - /* Close our file - */ - fclose(fp); + body = xmlNewChild(html, NULL, "body", NULL); + if (!body) { + warn_user("NoMemory", 0); + xmlFreeDoc(doc); + return; + } - /* Set the filetype to HTML - */ - xosfile_set_type(file, 0xfaf); + if (!ro_gui_hotlist_save_directory(&root, body)) { + warn_user("NoMemory", 0); + xmlFreeDoc(doc); + return; + } + + doc->charset = XML_CHAR_ENCODING_UTF8; + res = htmlSaveFileEnc(filename, doc, "iso-8859-1"); + if (res == -1) { + warn_user("HotlistSaveError", 0); + xmlFreeDoc(doc); + return; + } + + xmlFreeDoc(doc); + + xosfile_set_type(filename, 0xfaf); } -bool ro_gui_hotlist_save_entry(FILE *fp, struct hotlist_entry *entry) { - if (!entry) return true; - /* We're starting a new child - */ - fprintf(fp, "<ul>\n"); +/** + * Add a directory to the HTML tree for saving. + * + * \param directory hotlist directory to add + * \param node node to add ul to + * \return true on success, false on memory exhaustion + */ + +bool ro_gui_hotlist_save_directory(struct hotlist_entry *directory, + xmlNode *node) +{ + struct hotlist_entry *child; + xmlNode *ul, *h4; + + ul = xmlNewChild(node, NULL, "ul", NULL); + if (!ul) + return false; + + for (child = directory->child_entry; child; child = child->next_entry) { + if (child->url) { + /* entry */ + if (!ro_gui_hotlist_save_entry(child, ul)) + return false; - /* Work through the entries - */ - while (entry) { - /* Save this entry - */ - if (entry->children >= 0) { - fprintf(fp, "<h4>%s</h4>\n", entry->title); - if (entry->child_entry) ro_gui_hotlist_save_entry(fp, entry->child_entry); } else { - fprintf(fp, "<li><a href=\"%s\">%s</a>\n", entry->url, entry->title); - if (entry->filetype != 0xfaf) fprintf(fp, "<!-- Type:%i -->\n", entry->filetype); - if (entry->add_date != -1) fprintf(fp, "<!--Added:%i-->\n", (int)entry->add_date); - if (entry->last_date != -1) fprintf(fp, "<!--LastVisit:%i-->\n", (int)entry->last_date); - if (entry->visits != 0) fprintf(fp, "<!--Visits:%i-->\n", entry->visits); + /* directory */ + /* invalid HTML */ + h4 = xmlNewTextChild(ul, NULL, "h4", child->title); + if (!h4) + return false; + + if (!ro_gui_hotlist_save_directory(child, ul)) + return false; } - entry = entry->next_entry; } - /* We're starting a new child - */ - fprintf(fp, "</ul>\n"); + return true; +} + + +/** + * Add an entry to the HTML tree for saving. + * + * \param entry hotlist entry to add + * \param node node to add li to + * \return true on success, false on memory exhaustion + */ + +bool ro_gui_hotlist_save_entry(struct hotlist_entry *entry, + xmlNode *node) +{ + xmlNode *li, *a; + xmlAttr *href; + + li = xmlNewChild(node, NULL, "li", NULL); + if (!li) + return false; + + a = xmlNewTextChild(li, NULL, "a", entry->title); + if (!a) + return false; + + href = xmlNewProp(a, "href", entry->url); + if (!href) + return false; + + if (entry->filetype != 0xfaf) + if (!ro_gui_hotlist_save_entry_comment(li, + "Type", entry->filetype)) + return false; + + if (entry->add_date != -1) + if (!ro_gui_hotlist_save_entry_comment(li, + "Added", entry->add_date)) + return false; + + if (entry->last_date != -1) + if (!ro_gui_hotlist_save_entry_comment(li, + "LastVisit", entry->last_date)) + return false; + + if (entry->visits != 0) + if (!ro_gui_hotlist_save_entry_comment(li, + "Visits", entry->visits)) + return false; + + return true; +} + + +/** + * Add a special comment node to the HTML tree for saving. + * + * \param node node to add comment to + * \param name name of special comment + * \param value value of special comment + * \return true on success, false on memory exhaustion + */ + +bool ro_gui_hotlist_save_entry_comment(xmlNode *node, + const char *name, int value) +{ + char s[40]; + xmlNode *comment; + + snprintf(s, sizeof s, "%s:%i", name, value); + s[sizeof s - 1] = 0; + + comment = xmlNewComment(s); + if (!comment) + return false; + if (!xmlAddChild(node, comment)) { + xmlFreeNode(comment); + return false; + } + return true; } @@ -700,11 +894,13 @@ void ro_gui_hotlist_visited_update(struct content *content, struct hotlist_entry /** - * Adds a hotlist entry to the root of the tree (internal). + * Adds a hotlist entry to a folder of the tree. * - * \param title the entry title - * \param url the entry url (NULL to create a folder) - * \param folder the folder to add the entry into + * \param title the entry title (copied) + * \param url the entry url (NULL to create a folder) (copied) + * \param filetype filetype of entry + * \param folder the folder to add the entry into + * \return the new entry, or NULL on error and error reported */ struct hotlist_entry *ro_gui_hotlist_create_entry(const char *title, const char *url, int filetype, |