From 776c1e9696cda135adf8e045ce2ac54f488b008c Mon Sep 17 00:00:00 2001 From: Martin Fuchs Date: Sun, 4 Apr 2004 16:04:34 +0000 Subject: [PATCH] import IE bookmarks and convert into XBEL format bookmark file svn path=/trunk/; revision=8964 --- reactos/subsys/system/explorer/Jamfile | 1 + reactos/subsys/system/explorer/Makefile | 1 + reactos/subsys/system/explorer/Makefile.MinGW | 1 + reactos/subsys/system/explorer/Makefile.Wine | 1 + .../subsys/system/explorer/doxy-footer.html | 2 +- reactos/subsys/system/explorer/explorer.cpp | 30 +- reactos/subsys/system/explorer/explorer.dsp | 8 + reactos/subsys/system/explorer/globals.h | 9 +- .../subsys/system/explorer/make_explorer.dsp | 2 +- .../subsys/system/explorer/shell/fatfs.cpp | 2 +- .../system/explorer/taskbar/favorites.cpp | 292 ++++++++++++ .../system/explorer/taskbar/favorites.h | 87 ++++ .../system/explorer/taskbar/startmenu.cpp | 2 +- .../system/explorer/taskbar/traynotify.cpp | 8 +- .../system/explorer/utility/xmlstorage.cpp | 168 +++++-- .../system/explorer/utility/xmlstorage.h | 446 +++++++++++++++--- 16 files changed, 929 insertions(+), 131 deletions(-) create mode 100644 reactos/subsys/system/explorer/taskbar/favorites.cpp create mode 100644 reactos/subsys/system/explorer/taskbar/favorites.h diff --git a/reactos/subsys/system/explorer/Jamfile b/reactos/subsys/system/explorer/Jamfile index 9be72f6b573..127ec3898f4 100644 --- a/reactos/subsys/system/explorer/Jamfile +++ b/reactos/subsys/system/explorer/Jamfile @@ -31,6 +31,7 @@ exe explorer : taskbar/startmenu.cpp taskbar/taskbar.cpp taskbar/traynotify.cpp + taskbar/favorites.cpp desktop/desktop.cpp # utility/splitpath.c utility/dragdropimpl.cpp diff --git a/reactos/subsys/system/explorer/Makefile b/reactos/subsys/system/explorer/Makefile index 79bf63e82ec..a1f5fe08fab 100644 --- a/reactos/subsys/system/explorer/Makefile +++ b/reactos/subsys/system/explorer/Makefile @@ -99,6 +99,7 @@ OBJECTS = \ startmenu.o \ traynotify.o \ quicklaunch.o \ + favorites.o \ searchprogram.o \ settings.o \ i386-stub-win32.o \ diff --git a/reactos/subsys/system/explorer/Makefile.MinGW b/reactos/subsys/system/explorer/Makefile.MinGW index 4eb220c29d3..1c5c13949cd 100644 --- a/reactos/subsys/system/explorer/Makefile.MinGW +++ b/reactos/subsys/system/explorer/Makefile.MinGW @@ -68,6 +68,7 @@ OBJECTS = \ startmenu.o \ traynotify.o \ quicklaunch.o \ + favorites.o \ searchprogram.o \ settings.o \ i386-stub-win32.o \ diff --git a/reactos/subsys/system/explorer/Makefile.Wine b/reactos/subsys/system/explorer/Makefile.Wine index 7899fe67a9d..464ada9b625 100644 --- a/reactos/subsys/system/explorer/Makefile.Wine +++ b/reactos/subsys/system/explorer/Makefile.Wine @@ -39,6 +39,7 @@ CPP_SRCS = \ taskbar/startmenu.cpp \ taskbar/traynotify.cpp \ taskbar/quicklaunch.cpp \ + taskbar/favorites.cpp \ dialogs/searchprogram.cpp \ dialogs/settings.cpp diff --git a/reactos/subsys/system/explorer/doxy-footer.html b/reactos/subsys/system/explorer/doxy-footer.html index 451e0f066b6..e9dcac1212c 100644 --- a/reactos/subsys/system/explorer/doxy-footer.html +++ b/reactos/subsys/system/explorer/doxy-footer.html @@ -3,7 +3,7 @@
ROS Explorer Source Code Documentation -
generated on 28.03.2004 by +
generated on 04.04.2004 by
doxygen
diff --git a/reactos/subsys/system/explorer/explorer.cpp b/reactos/subsys/system/explorer/explorer.cpp index 17ecf23f1b7..ef1e911f08d 100644 --- a/reactos/subsys/system/explorer/explorer.cpp +++ b/reactos/subsys/system/explorer/explorer.cpp @@ -82,27 +82,31 @@ void ExplorerGlobals::init(HINSTANCE hInstance) } -bool ExplorerGlobals::read_cfg() +void ExplorerGlobals::read_persistent() { // read configuration file _cfg_dir.printf(TEXT("%s\\ReactOS"), (LPCTSTR)SpecialFolderFSPath(CSIDL_APPDATA,0)); - _cfg_path.printf(TEXT("%s\\ros-explorer.xml"), _cfg_dir.c_str()); + _cfg_path.printf(TEXT("%s\\ros-explorer-cfg.xml"), _cfg_dir.c_str()); - if (_cfg.read(_cfg_path)) - return true; + if (!_cfg.read(_cfg_path)) + _cfg.read(TEXT("explorer-cfg-template.xml")); - if (_cfg.read("explorer-cfg-template.xml")) - return true; + // read bookmarks + _favorites_path.printf(TEXT("%s\\ros-explorer-bookmarks.xml"), _cfg_dir.c_str()); - return false; + if (!_favorites.read(_favorites_path)) { + _favorites.import_IE_favorites(0); + _favorites.write(_favorites_path); + } } -void ExplorerGlobals::write_cfg() +void ExplorerGlobals::write_persistent() { // write configuration file RecursiveCreateDirectory(_cfg_dir); _cfg.write(_cfg_path); + _favorites.write(_favorites_path); } @@ -110,7 +114,7 @@ XMLPos ExplorerGlobals::get_cfg() { XMLPos pos(&_cfg); - pos.create("explorer-cfg"); + pos.smart_create("explorer-cfg"); return pos; } @@ -119,8 +123,8 @@ XMLPos ExplorerGlobals::get_cfg(const String& name) { XMLPos pos(&_cfg); - pos.create("explorer-cfg"); - pos.create(name); + pos.smart_create("explorer-cfg"); + pos.smart_create(name); return pos; } @@ -790,7 +794,7 @@ int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdL // init common controls library CommonControlInit usingCmnCtrl; - g_Globals.read_cfg(); + g_Globals.read_persistent(); if (startup_desktop) { g_Globals._desktops.init(); @@ -818,7 +822,7 @@ int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdL int ret = explorer_main(hInstance, lpCmdLine, nShowCmd); // write configuration file - g_Globals.write_cfg(); + g_Globals.write_persistent(); return ret; } diff --git a/reactos/subsys/system/explorer/explorer.dsp b/reactos/subsys/system/explorer/explorer.dsp index 9f7fb436fd2..f240ab90e46 100644 --- a/reactos/subsys/system/explorer/explorer.dsp +++ b/reactos/subsys/system/explorer/explorer.dsp @@ -554,6 +554,14 @@ SOURCE=.\taskbar\desktopbar.h # End Source File # Begin Source File +SOURCE=.\taskbar\favorites.cpp +# End Source File +# Begin Source File + +SOURCE=.\taskbar\favorites.h +# End Source File +# Begin Source File + SOURCE=.\notifyhook\notifyhook.h # End Source File # Begin Source File diff --git a/reactos/subsys/system/explorer/globals.h b/reactos/subsys/system/explorer/globals.h index b42152755fd..f0ac671f995 100644 --- a/reactos/subsys/system/explorer/globals.h +++ b/reactos/subsys/system/explorer/globals.h @@ -30,6 +30,8 @@ using namespace XMLStorage; +#include "taskbar/favorites.h" + /// management of file types struct FileTypeInfo { @@ -228,8 +230,8 @@ extern struct ExplorerGlobals void init(HINSTANCE hInstance); - bool read_cfg(); - void write_cfg(); + void read_persistent(); + void write_persistent(); XMLPos get_cfg(); XMLPos get_cfg(const String& name); @@ -259,6 +261,9 @@ extern struct ExplorerGlobals XMLDoc _cfg; String _cfg_dir; String _cfg_path; + + Favorites _favorites; + String _favorites_path; } g_Globals; diff --git a/reactos/subsys/system/explorer/make_explorer.dsp b/reactos/subsys/system/explorer/make_explorer.dsp index 1631b01d435..7500119cfab 100644 --- a/reactos/subsys/system/explorer/make_explorer.dsp +++ b/reactos/subsys/system/explorer/make_explorer.dsp @@ -108,7 +108,7 @@ CFG=make_explorer - Win32 bjam # PROP Use_Debug_Libraries 0 # PROP Output_Dir "URelease" # PROP Intermediate_Dir "URelease" -# PROP Cmd_Line "msdevfilt -gcc make -f Makefile.MinGW UNICODE=1" +# PROP Cmd_Line "make -f Makefile.MinGW UNICODE=1" # PROP Rebuild_Opt "clean all" # PROP Target_File "explorer.exe" # PROP Bsc_Name "" diff --git a/reactos/subsys/system/explorer/shell/fatfs.cpp b/reactos/subsys/system/explorer/shell/fatfs.cpp index a2c0b120bc6..96481ac7e7b 100644 --- a/reactos/subsys/system/explorer/shell/fatfs.cpp +++ b/reactos/subsys/system/explorer/shell/fatfs.cpp @@ -460,7 +460,7 @@ FATDrive::FATDrive(LPCTSTR path) _CacheDty = NULL; _Caches = 0; - _hDrive = CreateFile(path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); + _hDrive = CreateFile(path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); if (_hDrive != INVALID_HANDLE_VALUE) { _boot_sector.BytesPerSector = 512; diff --git a/reactos/subsys/system/explorer/taskbar/favorites.cpp b/reactos/subsys/system/explorer/taskbar/favorites.cpp new file mode 100644 index 00000000000..e4ab91fba18 --- /dev/null +++ b/reactos/subsys/system/explorer/taskbar/favorites.cpp @@ -0,0 +1,292 @@ +/* + * Copyright 2004 Martin Fuchs + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + + // + // Explorer and Desktop clone + // + // favorites.cpp + // + // Martin Fuchs, 04.04.2004 + // + + +#include "../utility/utility.h" + +#include "../explorer.h" +#include "../globals.h" + +#include "startmenu.h" + + + /// read .URL file +bool Bookmark::read_url(LPCTSTR path) +{ + char line[BUFFER_LEN]; + + tifstream in(path); + + while(in.good()) { + in.getline(line, BUFFER_LEN); + + const char* p = line; + while(isspace(*p)) + ++p; + + const char* keyword = p; + const char* eq = strchr(p, '='); + + if (eq) { + const char* cont = eq + 1; + while(isspace(*cont)) + ++cont; + + if (!strnicmp(keyword, "URL", 3)) + _url = cont; + else if (!strnicmp(keyword, "IconFile", 8)) + _icon_path = cont; + } + } + + return true; +} + + /// convert XBEL bookmark node +bool Bookmark::read_xbel(const_XMLPos& pos) +{ + _url = pos.get("href"); + + if (!pos.go_down("title")) + return false; + + _name = pos->get_content(); + + pos.back(); + + return true; +} + + +BookmarkNode::BookmarkNode(const Bookmark& bm) + : _type(BMNT_BOOKMARK) +{ + _pbookmark = new Bookmark(bm); +} + +BookmarkNode::BookmarkNode(const BookmarkFolder& bmf) + : _type(BMNT_FOLDER) +{ + _pfolder = new BookmarkFolder(bmf); +} + +BookmarkNode::BookmarkNode(const BookmarkNode& other) + : _type(other._type) +{ + if (_type == BMNT_BOOKMARK) + _pbookmark = new Bookmark(*other._pbookmark); + else + _pfolder = new BookmarkFolder(*other._pfolder); +} + +BookmarkNode::~BookmarkNode() +{ + if (_type == BMNT_BOOKMARK) + delete _pbookmark; + else + delete _pfolder; +} + + + /// read bookmark list from XBEL formated XML tree +void BookmarkList::read(const_XMLPos& pos) +{ + const XMLNode::Children& children = pos->get_children(); + + for(XMLNode::Children::const_iterator it=children.begin(); it!=children.end(); ++it) { + const XMLNode& node = **it; + const_XMLPos sub_pos(&node); + + if (node == "folder") { + BookmarkFolder new_folder; + + if (sub_pos.go_down("title")) { + new_folder._name = sub_pos->get_content(); + sub_pos.back(); + } + + new_folder._bookmarks.read(sub_pos); + + push_back(new_folder); + } else if (node == "bookmark") { + Bookmark bookmark; + + if (bookmark.read_xbel(sub_pos)) + push_back(bookmark); + } + } +} + + /// write bookmark list into XBEL formated XML tree +void BookmarkList::write(XMLPos& pos) const +{ + for(const_iterator it=begin(); it!=end(); ++it) { + const BookmarkNode& node = *it; + + if (node._type == BookmarkNode::BMNT_FOLDER) { + BookmarkFolder& folder = *node._pfolder; + + pos.create("folder"); + + pos.create("title"); + pos->set_content(folder._name); + pos.back(); + + folder._bookmarks.write(pos); + + pos.back(); + } else { // node._type == BookmarkNode::BMNT_BOOKMARK + Bookmark& bookmark = *node._pbookmark; + + if (!bookmark._url.empty()) { + pos.create("bookmark"); + + pos["href"] = bookmark._url; + + pos.create("title"); + pos->set_content(bookmark._name); + pos.back(); + + pos.back(); + } + } + } +} + + +void BookmarkList::import_IE_favorites(ShellDirectory& dir, HWND hwnd) +{ + TCHAR path[MAX_PATH], ext[_MAX_EXT]; + + dir.smart_scan(SCAN_FILESYSTEM); + + for(Entry*entry=dir._down; entry; entry=entry->_next) { + if (entry->_shell_attribs & SFGAO_HIDDEN) // hide files like "desktop.ini" + continue; + + String name; + + if (entry->_etype == ET_SHELL) + name = dir._folder.get_name(static_cast(entry)->_pidl); + else + name = entry->_display_name; + + if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + BookmarkFolder new_folder; + + new_folder._name = name; + + if (entry->_etype == ET_SHELL) { + ShellDirectory new_dir(dir._folder, static_cast(entry)->_pidl, hwnd); + new_folder._bookmarks.import_IE_favorites(new_dir, hwnd); + } else { + entry->get_path(path); + ShellDirectory new_dir(GetDesktopFolder(), path, hwnd); + new_folder._bookmarks.import_IE_favorites(new_dir, hwnd); + } + + push_back(new_folder); + } else { + Bookmark bookmark; + + bookmark._name = name; + + entry->get_path(path); + _tsplitpath(path, NULL, NULL, NULL, ext); + + if (!_tcsicmp(ext, TEXT(".url"))) { + bookmark.read_url(path); + push_back(bookmark); + } else { + ///@todo read shell links + assert(0); + } + } + } +} + + + /// read XBEL bookmark file +bool Favorites::read(LPCTSTR path) +{ + XMLDoc xbel; + + if (!xbel.read(path)) + return false; + + const_XMLPos pos(&xbel); + + if (!pos.go_down("xbel")) + return false; + + super::read(pos); + + pos.back(); + + return true; +} + + /// write XBEL bookmark file +void Favorites::write(LPCTSTR path) const +{ + XMLDoc xbel; + + XMLPos pos(&xbel); + pos.create("xbel"); + super::write(pos); + pos.back(); + + xbel.write(path, XMLNode::FORMAT_SMART, XMLHeader("1.0", "UTF-8", "")); +} + + /// import Internet Explorer bookmarks from Favorites folder +bool Favorites::import_IE_favorites(HWND hwnd) +{ + WaitCursor wait; + + StartMenuShellDirs dirs; + + try { + dirs.push_back(ShellDirectory(GetDesktopFolder(), SpecialFolderPath(CSIDL_COMMON_FAVORITES, hwnd), hwnd)); + dirs.push_back(ShellDirectory(GetDesktopFolder(), SpecialFolderPath(CSIDL_FAVORITES, hwnd), hwnd)); + } catch(COMException&) { + } + + for(StartMenuShellDirs::iterator it=dirs.begin(); it!=dirs.end(); ++it) { + StartMenuDirectory& smd = *it; + ShellDirectory& dir = smd._dir; + + try { + super::import_IE_favorites(dir, hwnd); + } catch(COMException&) { + } + } + + return true; +} diff --git a/reactos/subsys/system/explorer/taskbar/favorites.h b/reactos/subsys/system/explorer/taskbar/favorites.h new file mode 100644 index 00000000000..68c08ff4d62 --- /dev/null +++ b/reactos/subsys/system/explorer/taskbar/favorites.h @@ -0,0 +1,87 @@ +/* + * Copyright 2004 Martin Fuchs + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + + // + // Explorer and Desktop clone + // + // favorites.h + // + // Martin Fuchs, 04.04.2004 + // + + +struct Bookmark +{ + Bookmark() : _icon_idx(0) {} + + String _name; + String _url; + String _description; + String _icon_path; + int _icon_idx; + + bool read_url(LPCTSTR path); + bool read_xbel(const_XMLPos& pos); +}; + +struct BookmarkFolder; + +struct BookmarkNode +{ + BookmarkNode(const Bookmark& bm); + BookmarkNode(const BookmarkFolder& bmf); + BookmarkNode(const BookmarkNode& other); + ~BookmarkNode(); + + enum BOOKMARKNODE_TYPE { + BMNT_BOOKMARK, BMNT_FOLDER + }; + + BOOKMARKNODE_TYPE _type; + + union { + Bookmark* _pbookmark; + BookmarkFolder* _pfolder; + }; +}; + +struct BookmarkList : public list +{ + void import_IE_favorites(struct ShellDirectory& dir, HWND hwnd); + + void read(const_XMLPos& pos); + void write(XMLPos& pos) const; +}; + +struct BookmarkFolder +{ + String _name; + String _description; + BookmarkList _bookmarks; +}; + +struct Favorites : public BookmarkList +{ + typedef BookmarkList super; + + bool read(LPCTSTR path); + void write(LPCTSTR path) const; + + bool import_IE_favorites(HWND hwnd); +}; diff --git a/reactos/subsys/system/explorer/taskbar/startmenu.cpp b/reactos/subsys/system/explorer/taskbar/startmenu.cpp index ff68d1c726e..3c9ef74ab12 100644 --- a/reactos/subsys/system/explorer/taskbar/startmenu.cpp +++ b/reactos/subsys/system/explorer/taskbar/startmenu.cpp @@ -1616,7 +1616,7 @@ int StartMenuHandler::Command(int id, int code) break; case IDC_FAVORITES: - CreateSubmenu(id, CSIDL_FAVORITES, ResString(IDS_FAVORITES)); + CreateSubmenu(id, CSIDL_COMMON_FAVORITES, CSIDL_FAVORITES, ResString(IDS_FAVORITES)); break; case IDC_BROWSE: diff --git a/reactos/subsys/system/explorer/taskbar/traynotify.cpp b/reactos/subsys/system/explorer/taskbar/traynotify.cpp index 841aec1c757..d19a1fcec94 100644 --- a/reactos/subsys/system/explorer/taskbar/traynotify.cpp +++ b/reactos/subsys/system/explorer/taskbar/traynotify.cpp @@ -259,11 +259,11 @@ void NotifyArea::write_config() // write notification icon settings to XML configuration file XMLPos pos = g_Globals.get_cfg(); - pos.create("desktopbar"); + pos.smart_create("desktopbar"); XMLBoolRef(pos, "options", "show-clock") = _hwndClock!=0; pos.back(); - pos.create("notify-icons"); + pos.smart_create("notify-icons"); XMLBoolRef(pos, "options", "hide-inactive") = _hide_inactive; XMLBoolRef(pos, "options", "show-hidden") = _show_hidden; @@ -272,7 +272,7 @@ void NotifyArea::write_config() NotifyIconConfig& cfg = *it; // search for the corresponding node using the original name - pos.create("icon", "name", cfg._name); + pos.smart_create("icon", "name", cfg._name); // refresh unique name cfg.create_name(); @@ -293,7 +293,7 @@ void NotifyArea::show_clock(bool flag) if (vis != flag) { if (flag) { - // create clock window + // smart_create clock window _hwndClock = ClockWindow::Create(_hwnd); if (_hwndClock) { diff --git a/reactos/subsys/system/explorer/utility/xmlstorage.cpp b/reactos/subsys/system/explorer/utility/xmlstorage.cpp index 5f995d0a4ed..05e9de6cb96 100644 --- a/reactos/subsys/system/explorer/utility/xmlstorage.cpp +++ b/reactos/subsys/system/explorer/utility/xmlstorage.cpp @@ -54,6 +54,34 @@ bool XMLPos::go(const char* path) } + /// read XML stream into XML tree below _pos +XML_Status XMLReader::read(std::istream& in) +{ + XML_Status status = XML_STATUS_OK; + + while(in.good() && status==XML_STATUS_OK) { + char* buffer = (char*) XML_GetBuffer(_parser, BUFFER_LEN); + + in.read(buffer, BUFFER_LEN); + + status = XML_ParseBuffer(_parser, in.gcount(), false); + } + + if (status != XML_STATUS_ERROR) + status = XML_ParseBuffer(_parser, 0, true); + +/* + if (status == XML_STATUS_ERROR) + cerr << get_error_string(); +*/ + + _pos->append_trailing(_content.c_str(), _content.length()); + _content.erase(); + + return status; +} + + /// store XML version and encoding into XML reader void XMLCALL XMLReader::XML_XmlDeclHandler(void* userData, const XML_Char* version, const XML_Char* encoding, int standalone) { @@ -65,12 +93,31 @@ void XMLCALL XMLReader::XML_XmlDeclHandler(void* userData, const XML_Char* versi } } - /// notifications about XML tag start + /// notifications about XML start tag void XMLCALL XMLReader::XML_StartElementHandler(void* userData, const XML_Char* name, const XML_Char** atts) { XMLReader* pThis = (XMLReader*) userData; - XMLNode* node = new XMLNode(String_from_XML_Char(name)); + // search for end of first line + const char* s = pThis->_content.c_str(); + const char* p = s; + const char* e = p + pThis->_content.length(); + + for(; p_pos->append_trailing(s, p-s); + + std::string leading; + + if (p != e) + leading.assign(p, e-p); + + XMLNode* node = new XMLNode(String_from_XML_Char(name), leading); pThis->_pos.add_down(node); @@ -81,17 +128,41 @@ void XMLCALL XMLReader::XML_StartElementHandler(void* userData, const XML_Char* (*node)[String_from_XML_Char(attr_name)] = String_from_XML_Char(attr_value); } - pThis->_in_tag = true; + pThis->_in_node = true; + pThis->_content.erase(); } - /// notifications about XML tag end + /// notifications about XML end tag void XMLCALL XMLReader::XML_EndElementHandler(void* userData, const XML_Char* name) { XMLReader* pThis = (XMLReader*) userData; + // search for end of first line + const char* s = pThis->_content.c_str(); + const char* p = s; + const char* e = p + pThis->_content.length(); + + for(; p_pos->append_content(s, p-s); + + std::string leading; + + if (p != e) + leading.assign(p, e-p); + + if (leading.empty()) + pThis->_pos->_end_leading = leading; + pThis->_pos.back(); - pThis->_in_tag = false; + pThis->_in_node = false; + pThis->_content.erase(); } /// store content, white space and comments @@ -99,14 +170,11 @@ void XMLCALL XMLReader::XML_DefaultHandler(void* userData, const XML_Char* s, in { XMLReader* pThis = (XMLReader*) userData; - if (pThis->_in_tag) - pThis->_pos->append_content(s, len); - else - pThis->_pos->append_trailing(s, len); + pThis->_content.append(s, len); } -std::string XMLString(LPCTSTR s) +std::string EncodeXMLString(LPCTSTR s) { TCHAR buffer[BUFFER_LEN]; LPTSTR o = buffer; @@ -132,22 +200,46 @@ std::string XMLString(LPCTSTR s) return get_utf8(buffer, o-buffer); } +String DecodeXMLString(LPCTSTR s) +{ + TCHAR buffer[BUFFER_LEN]; + LPTSTR o = buffer; + + for(LPCTSTR p=s; *p; ++p) + if (*p == '&') { + if (!_tcsnicmp(p+1, TEXT("amp;"), 4)) { + *o++ = '&'; + p += 4; + } else if (!_tcsnicmp(p+1, TEXT("lt;"), 3)) { + *o++ = '<'; + p += 3; + } else if (!_tcsnicmp(p+1, TEXT("gt;"), 3)) { + *o++ = '>'; + p += 3; + } else + *o++ = *p; + } else + *o++ = *p; + + return String(buffer, o-buffer); +} + /// write node with children tree to output stream using original white space -void XMLNode::write_worker(std::ostream& out, WRITE_MODE mode, int indent) const +void XMLNode::write_worker(std::ostream& out, int indent) const { - out << '<' << XMLString(*this); + out << _leading << '<' << EncodeXMLString(*this); for(AttributeMap::const_iterator it=_attributes.begin(); it!=_attributes.end(); ++it) - out << ' ' << XMLString(it->first) << "=\"" << XMLString(it->second) << "\""; + out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\""; if (!_children.empty() || !_content.empty()) { out << '>' << _content; for(Children::const_iterator it=_children.begin(); it!=_children.end(); ++it) - (*it)->write_worker(out, mode, indent+1); + (*it)->write_worker(out, indent+1); - out << "'; + out << _end_leading << "'; } else out << "/>"; @@ -156,81 +248,73 @@ void XMLNode::write_worker(std::ostream& out, WRITE_MODE mode, int indent) const /// pretty print node with children tree to output stream -void XMLNode::pretty_write_worker(std::ostream& out, WRITE_MODE mode, int indent) const +void XMLNode::pretty_write_worker(std::ostream& out, int indent) const { for(int i=indent; i--; ) out << XML_INDENT_SPACE; - out << '<' << XMLString(*this); + out << '<' << EncodeXMLString(*this); for(AttributeMap::const_iterator it=_attributes.begin(); it!=_attributes.end(); ++it) - out << ' ' << XMLString(it->first) << "=\"" << XMLString(it->second) << "\""; + out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\""; if (!_children.empty() || !_content.empty()) { out << ">\n"; for(Children::const_iterator it=_children.begin(); it!=_children.end(); ++it) - (*it)->pretty_write_worker(out, mode, indent+1); + (*it)->pretty_write_worker(out, indent+1); for(int i=indent; i--; ) out << XML_INDENT_SPACE; - out << "\n"; + out << "\n"; } else out << "/>\n"; } - /// write node with children tree to output stream using smart formating -bool XMLNode::smart_write_worker(std::ostream& out, int indent, bool next_format) const +void XMLNode::smart_write_worker(std::ostream& out, int indent) const { - bool format_pre, format_mid, format_post; - - format_pre = next_format; - format_mid = _content.empty(); - format_post = _trailing.empty(); - - if (format_pre) + if (_leading.empty()) for(int i=indent; i--; ) out << XML_INDENT_SPACE; + else + out << _leading; - out << '<' << XMLString(*this); + out << '<' << EncodeXMLString(*this); for(AttributeMap::const_iterator it=_attributes.begin(); it!=_attributes.end(); ++it) - out << ' ' << XMLString(it->first) << "=\"" << XMLString(it->second) << "\""; + out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\""; if (!_children.empty() || !_content.empty()) { out << '>'; - if (format_mid) + if (_content.empty()) out << '\n'; else out << _content; Children::const_iterator it = _children.begin(); - if (it != _children.end()) { - next_format = (*it)->_content.empty() && (*it)->_trailing.empty(); - + if (it != _children.end()) for(; it!=_children.end(); ++it) - next_format = (*it)->smart_write_worker(out, indent+1, next_format); - } + (*it)->smart_write_worker(out, indent+1); - if (next_format) + if (_end_leading.empty()) for(int i=indent; i--; ) out << XML_INDENT_SPACE; + else + out << _end_leading; - out << "'; + out << "'; } else out << "/>"; - if (format_post) + if (_trailing.empty()) out << '\n'; else out << _trailing; - - return format_post; } diff --git a/reactos/subsys/system/explorer/utility/xmlstorage.h b/reactos/subsys/system/explorer/utility/xmlstorage.h index 9ea5bd75d2f..06bfa3751f5 100644 --- a/reactos/subsys/system/explorer/utility/xmlstorage.h +++ b/reactos/subsys/system/explorer/utility/xmlstorage.h @@ -47,7 +47,7 @@ #include // for LPCTSTR #ifdef UNICODE -#define _UNICODE +#define _UNICODE #endif #include @@ -202,11 +202,74 @@ inline std::string get_utf8(const String& s) return get_utf8(s.c_str(), s.length()); } -extern std::string XMLString(LPCTSTR s); +extern std::string EncodeXMLString(LPCTSTR s); +extern String DecodeXMLString(LPCTSTR s); + + +#ifdef __GNUC__ +#include +typedef __gnu_cxx::stdio_filebuf STDIO_FILEBUF; +#else +typedef std::filebuf STDIO_FILEBUF; +#endif + +struct tifstream : public std::istream +{ + typedef std::istream super; + + tifstream(LPCTSTR path) + : super(&_buf), + _pfile(_tfopen(path, TEXT("r"))), +#ifdef __GNUC__ + _buf(_pfile, ios::in) +#else + _buf(_pfile) +#endif + { + } + + ~tifstream() + { + if (_pfile) + fclose(_pfile); + } + +protected: + FILE* _pfile; + STDIO_FILEBUF _buf; +}; + +struct tofstream : public std::ostream +{ + typedef std::ostream super; + + tofstream(LPCTSTR path) + : super(&_buf), + _pfile(_tfopen(path, TEXT("w"))), +#ifdef __GNUC__ + _buf(_pfile, ios::out) +#else + _buf(_pfile) +#endif + { + } + + ~tofstream() + { + flush(); + + if (_pfile) + fclose(_pfile); + } + +protected: + FILE* _pfile; + STDIO_FILEBUF _buf; +}; // write XML files with 2 spaces indenting -#define XML_INDENT_SPACE " " +#define XML_INDENT_SPACE " " #ifdef XML_UNICODE // Are XML_Char strings UTF-16 encoded? @@ -279,6 +342,7 @@ struct XMLNode : public String // access to protected class members for XMLPos and XMLReader friend struct XMLPos; + friend struct const_XMLPos; friend struct XMLReader; XMLNode(const String& name) @@ -286,9 +350,17 @@ struct XMLNode : public String { } + XMLNode(const String& name, const std::string& leading) + : String(name), + _leading(leading) + { + } + XMLNode(const XMLNode& other) : _attributes(other._attributes), + _leading(other._leading), _content(other._content), + _end_leading(other._end_leading), _trailing(other._trailing) { for(Children::const_iterator it=other._children.begin(); it!=other._children.end(); ++it) @@ -312,7 +384,9 @@ struct XMLNode : public String _attributes = other._attributes; + _leading = other._leading; _content = other._content; + _end_leading = other._end_leading; _trailing = other._trailing; return *this; @@ -401,10 +475,24 @@ struct XMLNode : public String return _children; } + String get_content() const + { + String ret; + + assign_utf8(ret, _content.c_str()); + + return DecodeXMLString(ret); + } + + void set_content(const String& s) + { + _content.assign(EncodeXMLString(s)); + } + enum WRITE_MODE { FORMAT_SMART = 0, /// preserve original white space and comments if present; pretty print otherwise FORMAT_ORIGINAL = 1, /// write XML stream preserving original white space and comments - FORMAT_PRETTY = 2 /// pretty print node to stream without preserving original white space + FORMAT_PRETTY = 2 /// pretty print node to stream without preserving original white space }; /// write node with children tree to output stream @@ -412,15 +500,15 @@ struct XMLNode : public String { switch(mode) { case FORMAT_PRETTY: - pretty_write_worker(out, mode, indent); + pretty_write_worker(out, indent); break; case FORMAT_ORIGINAL: - write_worker(out, mode, indent); + write_worker(out, indent); break; default: // FORMAT_SMART - smart_write_worker(out, indent, _content.empty() && _trailing.empty()); + smart_write_worker(out, indent); } return out; @@ -430,8 +518,10 @@ protected: Children _children; AttributeMap _attributes; - std::string _content; - std::string _trailing; + std::string _leading; + std::string _content; + std::string _end_leading; + std::string _trailing; XMLNode* get_first_child() const { @@ -488,10 +578,10 @@ protected: void append_content(const char* s, int l) { - if (_children.empty()) + //if (_children.empty()) _content.append(s, l); - else - _children.back()->_content.append(s, l); + //else + // _children.back()->_content.append(s, l); } void append_trailing(const char* s, int l) @@ -502,9 +592,9 @@ protected: _children.back()->_trailing.append(s, l); } - void write_worker(std::ostream& out, WRITE_MODE mode, int indent) const; - void pretty_write_worker(std::ostream& out, WRITE_MODE mode, int indent) const; - bool smart_write_worker(std::ostream& out, int indent, bool next_format) const; + void write_worker(std::ostream& out, int indent) const; + void pretty_write_worker(std::ostream& out, int indent) const; + void smart_write_worker(std::ostream& out, int indent) const; }; @@ -606,6 +696,104 @@ protected: }; + /// read only iterator access to children nodes with name filtering +struct const_XMLChildrenFilter +{ + const_XMLChildrenFilter(const XMLNode::Children& children, const String& name) + : _begin(children.begin(), children.end(), name), + _end(children.end(), children.end(), name) + { + } + + const_XMLChildrenFilter(const XMLNode* node, const String& name) + : _begin(node->get_children().begin(), node->get_children().end(), name), + _end(node->get_children().end(), node->get_children().end(), name) + { + } + + struct const_iterator + { + typedef XMLNode::Children::const_iterator BaseIterator; + + const_iterator(BaseIterator begin, BaseIterator end, const String& filter_name) + : _cur(begin), + _end(end), + _filter_name(filter_name) + { + search_next(); + } + + operator BaseIterator() + { + return _cur; + } + + const XMLNode* operator*() const + { + return *_cur; + } + + XMLNode* operator*() + { + return *_cur; + } + + const_iterator& operator++() + { + ++_cur; + search_next(); + + return *this; + } + + const_iterator operator++(int) + { + const_iterator ret = *this; + + ++_cur; + search_next(); + + return ret; + } + + bool operator==(const BaseIterator& other) const + { + return _cur == other; + } + + bool operator!=(const BaseIterator& other) const + { + return _cur != other; + } + + protected: + BaseIterator _cur; + BaseIterator _end; + String _filter_name; + + void search_next() + { + while(_cur!=_end && **_cur!=_filter_name) + ++_cur; + } + }; + + const_iterator begin() + { + return _begin; + } + + const_iterator end() + { + return _end; + } + +protected: + const_iterator _begin; + const_iterator _end; +}; + + /// iterator for XML trees struct XMLPos { @@ -622,8 +810,8 @@ struct XMLPos } /// access to current node - operator XMLNode*() {return _cur;} operator const XMLNode*() const {return _cur;} + operator XMLNode*() {return _cur;} const XMLNode* operator->() const {return _cur;} XMLNode* operator->() {return _cur;} @@ -632,8 +820,8 @@ struct XMLPos XMLNode& operator*() {return *_cur;} /// attribute access - String& operator[](const String& attr_name) {return (*_cur)[attr_name];} template String get(const T& attr_name) const {return (*_cur)[attr_name];} + String& operator[](const String& attr_name) {return (*_cur)[attr_name];} /// insert children when building tree void add_down(XMLNode* child) @@ -680,8 +868,14 @@ struct XMLPos /// move X-Path like to position in XML tree bool go(const char* path); - /// create node if not already existing and move to it + /// create node and move to it void create(const String& name) + { + add_down(new XMLNode(name)); + } + + /// create node if not already existing and move to it + void smart_create(const String& name) { XMLNode* node = _cur->find_first(name); @@ -692,7 +886,7 @@ struct XMLPos } /// search matching child node identified by key name and an attribute value - void create(const String& name, const String& attr_name, const String& attr_value) + void smart_create(const String& name, const String& attr_name, const String& attr_value) { XMLNode* node = _cur->find_first(name, attr_name, attr_value); @@ -718,8 +912,14 @@ struct XMLPos return false; } - /// create node if not already existing and move to it + /// create node and move to it void create(const char* name) + { + add_down(new XMLNode(name)); + } + + /// create node if not already existing and move to it + void smart_create(const char* name) { XMLNode* node = _cur->find_first(name); @@ -731,7 +931,7 @@ struct XMLPos /// search matching child node identified by key name and an attribute value template - void create(const char* name, const T& attr_name, const U& attr_value) + void smart_create(const char* name, const T& attr_name, const U& attr_value) { XMLNode* node = _cur->find_first(name, attr_name, attr_value); @@ -759,6 +959,97 @@ protected: }; + /// iterator for XML trees +struct const_XMLPos +{ + const_XMLPos(const XMLNode* root) + : _root(root), + _cur(root) + { + } + + const_XMLPos(const const_XMLPos& other) + : _root(other._root), + _cur(other._cur) + { // don't copy _stack + } + + /// access to current node + operator const XMLNode*() const {return _cur;} + + const XMLNode* operator->() const {return _cur;} + + const XMLNode& operator*() const {return *_cur;} + + /// attribute access + template String get(const T& attr_name) const {return _cur->get(attr_name);} + + /// go back to previous position + bool back() + { + if (!_stack.empty()) { + _cur = _stack.top(); + _stack.pop(); + return true; + } else + return false; + } + + /// go down to first child + bool go_down() + { + const XMLNode* node = _cur->get_first_child(); + + if (node) { + go_to(node); + return true; + } else + return false; + } + + /// search for child and go down + bool go_down(const String& name) + { + XMLNode* node = _cur->find_first(name); + + if (node) { + go_to(node); + return true; + } else + return false; + } + + /// move X-Path like to position in XML tree + bool go(const char* path); + +#ifdef UNICODE + /// search for child and go down + bool go_down(const char* name) + { + XMLNode* node = _cur->find_first(name); + + if (node) { + go_to(node); + return true; + } else + return false; + } +#endif + +protected: + const XMLNode* _root; + const XMLNode* _cur; + std::stack _stack; + + /// go to specified node + void go_to(const XMLNode* child) + { + _stack.push(_cur); + _cur = child; + } +}; + + struct XMLBool { XMLBool(bool value) @@ -789,6 +1080,11 @@ struct XMLBool return _value; } + bool operator!() const + { + return !_value; + } + operator LPCTSTR() const { return _value? TEXT("TRUE"): TEXT("FALSE"); @@ -798,7 +1094,7 @@ protected: bool _value; private: - void operator=(const XMLBool&); // disallow assignment operations + void operator=(const XMLBool&); // disallow assignment operations }; struct XMLBoolRef @@ -815,6 +1111,11 @@ struct XMLBoolRef return !_tcsicmp(_ref, TEXT("TRUE")); } + bool operator!() const + { + return _tcsicmp(_ref, TEXT("TRUE"))? true: false; + } + XMLBoolRef& operator=(bool value) { assign(value); @@ -833,7 +1134,7 @@ struct XMLBoolRef } protected: - String& _ref; + String& _ref; }; @@ -875,10 +1176,10 @@ struct XMLNumber } protected: - int _value; + int _value; private: - void operator=(const XMLBool&); // disallow assignment operations + void operator=(const XMLBool&); // disallow assignment operations }; struct XMLNumberRef @@ -911,7 +1212,7 @@ struct XMLNumberRef } protected: - String& _ref; + String& _ref; }; @@ -931,7 +1232,7 @@ struct XMLReader XML_SetElementHandler(_parser, XML_StartElementHandler, XML_EndElementHandler); XML_SetDefaultHandler(_parser, XML_DefaultHandler); - _in_tag = false; + _in_node = false; } ~XMLReader() @@ -939,28 +1240,7 @@ struct XMLReader XML_ParserFree(_parser); } - XML_Status read(std::istream& in) - { - XML_Status status = XML_STATUS_OK; - - while(in.good() && status==XML_STATUS_OK) { - char* buffer = (char*) XML_GetBuffer(_parser, BUFFER_LEN); - - in.read(buffer, BUFFER_LEN); - - status = XML_ParseBuffer(_parser, in.gcount(), false); - } - - if (status != XML_STATUS_ERROR) - status = XML_ParseBuffer(_parser, 0, true); - - /* - if (status == XML_STATUS_ERROR) - cerr << path << get_error_string(); - */ - - return status; - } + XML_Status read(std::istream& in); std::string get_position() const { @@ -986,9 +1266,11 @@ struct XMLReader protected: XMLPos _pos; XML_Parser _parser; - std::string _xml_version; - std::string _encoding; - bool _in_tag; + std::string _xml_version; + std::string _encoding; + + std::string _content; + bool _in_node; static void XMLCALL XML_XmlDeclHandler(void* userData, const XML_Char* version, const XML_Char* encoding, int standalone); static void XMLCALL XML_StartElementHandler(void* userData, const XML_Char* name, const XML_Char** atts); @@ -997,6 +1279,29 @@ protected: }; +struct XMLHeader : public std::string +{ + XMLHeader(const std::string& xml_version="1.0", const std::string& encoding="UTF-8", const std::string& doctype="") + : _version(xml_version), + _encoding(encoding), + _doctype(doctype) + { + } + + void print(std::ostream& out) const + { + out << "\n"; + + if (!_doctype.empty()) + out << _doctype << '\n'; + } + + std::string _version; + std::string _encoding; + std::string _doctype; +}; + + struct XMLDoc : public XMLNode { XMLDoc() @@ -1004,7 +1309,7 @@ struct XMLDoc : public XMLNode { } - XMLDoc(const std::string& path) + XMLDoc(LPCTSTR path) : XMLNode("") { read(path); @@ -1012,23 +1317,33 @@ struct XMLDoc : public XMLNode std::istream& read(std::istream& in) { - XMLReader(this).read(in); + XMLReader reader(this); + /*XML_Status status = */reader.read(in); +/* + if (status == XML_STATUS_ERROR) + cerr << reader.get_error_string(); +*/ return in; } - bool read(const std::string& path) + bool read(LPCTSTR path) { - std::ifstream in(path.c_str()); + tifstream in(path); + XMLReader reader(this); - return XMLReader(this).read(in) != XML_STATUS_ERROR; + XML_Status status = reader.read(in); +/* + if (status == XML_STATUS_ERROR) + cerr << path << reader.get_error_string(); +*/ + return status != XML_STATUS_ERROR; } /// write XML stream preserving previous white space and comments - std::ostream& write(std::ostream& out, WRITE_MODE mode=FORMAT_SMART, - const std::string& xml_version="1.0", const std::string& encoding="UTF-8") const + std::ostream& write(std::ostream& out, WRITE_MODE mode=FORMAT_SMART, const XMLHeader& header=XMLHeader()) const { - out << "\n"; + header.print(out); if (!_children.empty()) _children.front()->write(out); @@ -1042,17 +1357,16 @@ struct XMLDoc : public XMLNode return write(out, FORMAT_PRETTY); } - void write(const std::string& path, WRITE_MODE mode=FORMAT_SMART, - const std::string& xml_version="1.0", const std::string& encoding="UTF-8") const + void write(LPCTSTR path, WRITE_MODE mode=FORMAT_SMART, const XMLHeader& header=XMLHeader()) const { - std::ofstream out(path.c_str()); + tofstream out(path); - write(out, mode, xml_version, encoding); + write(out, mode, header); } - void write_formating(const std::string& path) const + void write_formating(LPCTSTR path) const { - std::ofstream out(path.c_str()); + tofstream out(path); write_formating(out); }