├── .gitignore ├── .travis.yml ├── AssClass.cpp ├── AssClass.hpp ├── CMakeLists.txt ├── LICENSE ├── README.md ├── danmaku2ass.cpp ├── danmaku2ass.h ├── danmaku2ass.hpp ├── rapidxml ├── LICENSE.txt ├── rapidxml.hpp ├── rapidxml_iterators.hpp ├── rapidxml_print.hpp └── rapidxml_utils.hpp └── tests ├── CMakeLists.txt ├── install_lcov.sh ├── test.sh └── testdata.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | xcbaselines/ 5 | *.pbxuser 6 | !default.pbxuser 7 | *.mode1v3 8 | !default.mode1v3 9 | *.mode2v3 10 | !default.mode2v3 11 | *.perspectivev3 12 | !default.perspectivev3 13 | xcuserdata 14 | *.xccheckout 15 | *.moved-aside 16 | DerivedData 17 | *.hmap 18 | *.ipa 19 | *.xcuserstate -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | compiler: 4 | - gcc 5 | 6 | sudo: false 7 | 8 | addons: 9 | apt: 10 | sources: 11 | - ubuntu-toolchain-r-test 12 | packages: 13 | - cmake 14 | - g++-4.8 15 | 16 | cache: 17 | directories: 18 | - $HOME/lcov 19 | 20 | before_install: 21 | - bash ./tests/install_lcov.sh 22 | 23 | install: 24 | # g++4.8.1 25 | - export CXX="g++-4.8" 26 | - export GCOV="gcov-4.8" 27 | - gem install coveralls-lcov 28 | 29 | script: 30 | - rm -rf CMakeLists.txt 31 | - cp ./tests/CMakeLists.txt ./CMakeLists.txt 32 | - $CXX --version 33 | - mkdir Build 34 | - cd Build 35 | - cmake .. 36 | - make 37 | 38 | after_success: ../tests/test.sh -------------------------------------------------------------------------------- /AssClass.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // AssClass.cpp 3 | // danmaku2ass_native 4 | // 5 | // Created by TYPCN on 2015/4/8. 6 | // 7 | // 8 | 9 | #include "AssClass.hpp" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | using namespace std; 19 | 20 | void Ass::init(const char *filename){ 21 | std::remove(filename); 22 | out.open(filename); 23 | } 24 | 25 | void Ass::SetDuration(int dm,int ds){ 26 | duration_marquee = dm; 27 | duration_still = ds; 28 | } 29 | 30 | void Ass::WriteHead(int width,int height,const char *font,float fontsize,float alpha){ 31 | 32 | srand((int)time(0)); 33 | 34 | FontSize = fontsize; 35 | VideoHeight = height; 36 | VideoWidth = width; 37 | 38 | // Write a♂ss head info 39 | out << "[Script Info]\nScript Updated By: Danmaku2ASS_native (https://github.com/typcn/danmaku2ass_native)\nScriptType: v4.00+" << endl; 40 | out << "PlayResX: " << width << endl; 41 | out << "PlayResY: " << height << endl; 42 | out << "Aspect Ratio: " << width << ":" << height << endl; 43 | out << "Collisions: Normal" << endl; 44 | out << "WrapStyle: 2" << endl; 45 | out << "ScaledBorderAndShadow: yes" << endl; 46 | out << "YCbCr Matrix: TV.601" << endl << endl; 47 | 48 | char alpha_hex[3]; 49 | sprintf(alpha_hex, "%02X", 255-round_int(alpha*255)); 50 | 51 | // Write ass styles , maybe disorder , See https://en.wikipedia.org/wiki/SubStation_Alpha 52 | out << "[V4+ Styles]"<< endl; 53 | out << "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding"<< endl; 54 | 55 | // Get Style name 56 | stringstream ss; 57 | ss << "TYCM_" << rand() % (int)(9999 + 1); 58 | ss >> style_name; 59 | 60 | out << "Style: " << 61 | style_name << "," << // Style name 62 | font << ", " << // Font name 63 | fontsize << ", " << // Font size 64 | "&H"<< alpha_hex <<"FFFFFF, " << // Primary Color 65 | "&H"<< alpha_hex <<"FFFFFF, " << // Secondary Color 66 | "&H"<< alpha_hex <<"000000, " << // Outline Color 67 | "&H"<< alpha_hex <<"000000, " << // Back Color 68 | "0, 0, 0, 0, 100, 100, 0.00, 0.00, 1, 1, 0, 7, 0, 0, 0, 0" << endl << endl; 69 | 70 | out << "[Events]" << endl; 71 | out << "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text" << endl; 72 | } 73 | 74 | void Ass::AppendComment(float appear_time,int comment_mode,int font_color,const char *content){ 75 | int duration; 76 | 77 | string str = content; 78 | 79 | stripStr(str); 80 | 81 | size_t strLength = Utf8StringSize(str); 82 | 83 | if(strLength > 100){ 84 | return; 85 | } 86 | 87 | int ContentFontLen = (int)strLength*FontSize; 88 | 89 | char effect[30]; 90 | if(comment_mode < 4){ 91 | sprintf(effect,"\\move(%d, [MROW], %d, [MROW])",VideoWidth,-ContentFontLen); 92 | duration = duration_marquee; 93 | }else if(comment_mode == 4){ 94 | sprintf(effect,"\\an2\\pos(%d,[BottomROW])",VideoWidth/2); 95 | duration = duration_still; 96 | }else if(comment_mode == 5){ 97 | sprintf(effect,"\\an8\\pos(%d,[TopROW])",VideoWidth/2); 98 | duration = duration_still; 99 | }else{ 100 | return; 101 | } 102 | string effectStr(effect); 103 | string color = ""; 104 | if(font_color != 16777215){ 105 | if(font_color == 0x000000){ 106 | color = "\\c&H000000&"; 107 | }else if(font_color == 0xffffff){ 108 | color = "\\c&HFFFFFF&"; 109 | }else{ 110 | int R = (font_color >> 16) & 0xff; 111 | int G = (font_color >> 8) & 0xff; 112 | int B = font_color & 0xff; 113 | char hexcolor[7]; 114 | sprintf(hexcolor, "%02X%02X%02X",B,G,R); 115 | hexcolor[6] = '\0'; 116 | string strcolor(hexcolor); 117 | color = "\\c&H" + strcolor + "&"; 118 | } 119 | } 120 | 121 | stringstream ss; 122 | ss << "Dialogue: 2," << TS2t(appear_time) << "," << TS2t(appear_time + duration) << "," << style_name << ",,0000,0000,0000,,{" << effectStr << color << "}" << str; 123 | 124 | pair contentPair = make_pair(ContentFontLen,ss.str()); 125 | 126 | comment_map.insert( std::pair>(appear_time,contentPair) ); 127 | 128 | } 129 | 130 | inline void Ass::stripStr(string in){ 131 | std::replace(in.begin(), in.end(), '\r', '\n'); 132 | std::replace(in.begin(), in.end(), '\n', ' '); 133 | } 134 | 135 | int Ass::round_int( double r ) { 136 | return (r > 0.0) ? (r + 0.5) : (r - 0.5); 137 | } 138 | 139 | inline string Ass::TS2t(double timestamp){ 140 | 141 | int ts= (int)timestamp*100.0; 142 | int hour,minute,second,centsecond; 143 | hour = ts/360000; 144 | minute = ts%360000; 145 | second = minute%6000; 146 | minute = minute/6000; 147 | centsecond = second%100; 148 | second = second/100; 149 | char buff[20]; 150 | sprintf(buff,"%d:%02d:%02d.%02d", hour,minute,second,centsecond); 151 | return string(buff); 152 | } 153 | 154 | template 155 | inline size_t IncUtf8StringIterator(_Iterator1& it, const _Iterator2& last) { 156 | if(it == last) return 0; 157 | unsigned char c; 158 | size_t res = 1; 159 | for(++it; last != it; ++it, ++res) { 160 | c = *it; 161 | if(!(c&0x80) || ((c&0xC0) == 0xC0)) break; 162 | } 163 | 164 | return res; 165 | } 166 | 167 | inline size_t Ass::Utf8StringSize(const std::string& str) { 168 | size_t res = 0; 169 | std::string::const_iterator it = str.begin(); 170 | for(; it != str.end(); IncUtf8StringIterator(it, str.end())) 171 | res++; 172 | 173 | return res; 174 | } 175 | 176 | static inline std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) { 177 | size_t start_pos = 0; 178 | while((start_pos = str.find(from, start_pos)) != std::string::npos) { 179 | str.replace(start_pos, from.length(), to); 180 | start_pos += to.length(); // Handles case where 'to' is a substring of 'from' 181 | } 182 | return str; 183 | } 184 | 185 | void Ass::WriteToDisk(std::vector disallowModes){ 186 | 187 | int All_Rows = 0; 188 | int Dropped_Rows = 0; 189 | 190 | typedef std::map>::iterator it_type; 191 | 192 | float TopTime = 0; 193 | float BottomTime = 0; 194 | 195 | int TopROW = -1; 196 | int BottomROW = -1; 197 | 198 | int line = ceil(VideoHeight/FontSize); 199 | 200 | double *rows_dismiss_time = new double[line]; // The time of scroll comment dismiss 201 | double *rows_visible_time = new double[line]; // The time of last char visible on screen 202 | 203 | for (int i = 0 ; i < line; i++) { 204 | rows_dismiss_time[i] = 0; 205 | rows_visible_time[i] = 0; 206 | } 207 | 208 | bool removeMRow = 0; 209 | bool removeTRow = 0; 210 | bool removeBRow = 0; 211 | for (auto i = disallowModes.begin();i != disallowModes.end(); i++ ){ 212 | int mode = *i; 213 | if(mode == 1){ 214 | removeMRow = 1; 215 | }else if(mode == 2){ 216 | removeTRow = 1; 217 | }else if(mode == 3){ 218 | removeBRow = 1; 219 | } 220 | } 221 | 222 | for(it_type iterator = comment_map.begin(); iterator != comment_map.end(); iterator++) { 223 | 224 | All_Rows++; 225 | 226 | string r = iterator->second.second; 227 | 228 | int playbackTime = iterator->first; 229 | double TextWidth = iterator->second.first + 2.0; // Add some space between texts 230 | double act_time = TextWidth / (((double)VideoWidth + TextWidth)/ (double)duration_marquee); // duration of last char visible on screen 231 | 232 | if(r.find("[MROW]") != std::string::npos){ 233 | if(removeMRow){ 234 | continue; 235 | } 236 | bool Replaced = false; 237 | for(int i=0;i < line;i++){ 238 | double Time_Arrive_Border = (playbackTime + (double)duration_marquee) - act_time; // The time of first char reach left border of video 239 | if(Time_Arrive_Border > rows_dismiss_time[i] && playbackTime > rows_visible_time[i]){ 240 | rows_dismiss_time[i] = playbackTime + (double) duration_marquee; 241 | rows_visible_time[i] = playbackTime + act_time; 242 | r = ReplaceAll(r,"[MROW]",to_string(i*FontSize)); 243 | Replaced = true; 244 | break; 245 | } 246 | } 247 | if(!Replaced){ 248 | r = ""; 249 | Dropped_Rows++; 250 | } 251 | }else if(r.find("[TopROW]") != std::string::npos){ 252 | if(removeTRow){ 253 | continue; 254 | } 255 | float timeago = iterator->first - TopTime; 256 | if(timeago > duration_still){ 257 | TopROW = 0; 258 | TopTime = iterator->first; 259 | }else{ 260 | TopROW++; 261 | } 262 | r = ReplaceAll(r,"[TopROW]",to_string(TopROW*FontSize)); 263 | }else if(r.find("[BottomROW]") != std::string::npos){ 264 | if(removeBRow){ 265 | continue; 266 | }else{ 267 | float timeago = iterator->first - BottomTime; 268 | if(timeago > duration_still){ 269 | BottomROW = 0; 270 | BottomTime = iterator->first; 271 | }else{ 272 | BottomROW++; 273 | } 274 | r = ReplaceAll(r,"[BottomROW]",to_string(VideoHeight-BottomROW*FontSize)); 275 | } 276 | }else{ 277 | continue; 278 | } 279 | 280 | if(r.length() < 10){ 281 | Dropped_Rows++; 282 | continue; 283 | } 284 | 285 | out << r << endl; 286 | } 287 | 288 | cout << "Comments:" << All_Rows << " Dropped:" << Dropped_Rows << endl; 289 | delete[] rows_dismiss_time; 290 | delete[] rows_visible_time; 291 | } 292 | -------------------------------------------------------------------------------- /AssClass.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // AssClass.h 3 | // danmaku2ass_native 4 | // 5 | // Created by TYPCN on 2015/4/8. 6 | // 7 | // 8 | 9 | #ifndef __danmaku2ass_native__AssClass__ 10 | #define __danmaku2ass_native__AssClass__ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | class Ass{ 18 | private: 19 | std::ofstream out; 20 | std::map > comment_map; 21 | int round_int( double r ); 22 | char style_name[10]; 23 | 24 | inline std::string TS2t(double timestamp); 25 | inline void stripStr(std::string in); 26 | inline size_t Utf8StringSize(const std::string& str); 27 | 28 | int duration_marquee; 29 | int duration_still; 30 | 31 | int VideoWidth; 32 | int VideoHeight; 33 | 34 | int FontSize; 35 | 36 | public: 37 | void init(const char *filename); 38 | void SetDuration(int dm,int ds); 39 | void WriteHead(int width,int height,const char *font,float fontsize,float alpha); 40 | void AppendComment(float appear_time,int comment_mode,int font_color,const char *content); 41 | void WriteToDisk(std::vector disallowModes); 42 | }; 43 | 44 | #endif /* defined(__danmaku2ass_native__AssClass__) */ 45 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | project(danmaku2ass_native) 3 | 4 | if (NOT MSVC) 5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 6 | endif() 7 | 8 | set(SOURCE_FILES 9 | danmaku2ass.cpp 10 | danmaku2ass.h 11 | rapidxml/rapidxml_iterators.hpp 12 | rapidxml/rapidxml_print.hpp 13 | rapidxml/rapidxml_utils.hpp 14 | rapidxml/rapidxml.hpp 15 | AssClass.cpp 16 | AssClass.hpp 17 | ) 18 | 19 | add_executable(danmaku2ass_native ${SOURCE_FILES}) 20 | add_library(danmaku2ass SHARED ${SOURCE_FILES}) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Danmaku2ass native 2 | 3 | [![Build Status](https://travis-ci.org/typcn/danmaku2ass_native.svg?branch=master)](https://travis-ci.org/typcn/danmaku2ass_native) 4 | [![Coverage Status](https://coveralls.io/repos/typcn/danmaku2ass_native/badge.svg)](https://coveralls.io/r/typcn/danmaku2ass_native) 5 | 6 | # Features 7 | 8 | Convrt comments to ASS subtitle,you can play it with most of media players. 9 | 10 | Written in C ++, convert ten thousand comments just 0.05 seconds, not depend on any third-party libraries, easily to embed. 11 | 12 | 13 | # Supported Website 14 | 15 | Only support bilibili format , other websites will be added in soon. 16 | 17 | # Screenshot 18 | 19 | ![](http://blog.eqoe.cn/images/1428559449093.png) 20 | 21 | # Complie 22 | 23 | CMake 2.6 or higher and C++ 11 compiler required. (GCC 4.8+, Clang 3.5+, MSVC 2013+ are tested) 24 | 25 | git clone https://github.com/typcn/danmaku2ass_native.git 26 | mkdir Build 27 | cd Build 28 | cmake .. 29 | make 30 | 31 | # Usage 32 | 33 | ## Command Line 34 | 35 | ./danmaku2ass -in=InputFile -out=OutputFile -w=VideoWidth -h=VideoHeight -font="FontName" -fontsize=FontSize -alpha=Alpha(0-1) -dm=ScrollCommentDisplayTime -ds=StillCommentDisplayTime 36 | 37 | ## Use in software 38 | 39 | Complie as library or Add all files to project,and delete main function in danmaku2ass.cpp 40 | 41 | ### API 42 | 43 | #include "danmaku2ass.h" 44 | 45 | void danmaku2ass(const char *infile,const char *outfile,int width,int height,const char *font,float fontsize,float alpha,float duration_marquee,float duration_still); 46 | 47 | * infile - Input file 48 | * outfile - Output file 49 | * width - Video width 50 | * height - Video height 51 | * font - Font name 52 | * fontsize - Font Size 53 | * alpha - Transparency(0-1) 54 | * duration_marquee - Scroll comment display time 55 | * duration_still - Still comment display time 56 | 57 | # Render comment into video 58 | 59 | You can use ffmpeg to do that. 60 | 61 | Example usage: 62 | 63 | ffmpeg -i xxx.flv -vf ass=ASS File -vcodec libx264 -acodec copy xxx_cm.flv 64 | 65 | # About 66 | 67 | Thanks for https://github.com/m13253/danmaku2ass 68 | 69 | License: DO ANYTHING YOU WANT TO 70 | -------------------------------------------------------------------------------- /danmaku2ass.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "rapidxml/rapidxml.hpp" 10 | #include "rapidxml/rapidxml_utils.hpp" 11 | #include "AssClass.hpp" 12 | #include "danmaku2ass.h" 13 | #include "danmaku2ass.hpp" 14 | 15 | using namespace std; 16 | using namespace rapidxml; 17 | 18 | /* 19 | Get comment type 20 | 21 | headline: the first line of comment file 22 | 23 | Return: 24 | 1 - Acfun 25 | 2 - Bilibili 26 | 3 - Niconico 27 | */ 28 | 29 | int GetCommentType(string headline){ 30 | if(headline.find("\"commentList\":[") != std::string::npos){ 31 | return 1; 32 | }else if(headline.find("xml version=\"1.0\" encoding=\"UTF-8\"?>\n") != std::string::npos){ 35 | return 2; 36 | }else if(headline.find("xml version=\"1.0\" encoding=\"UTF-8\"?>first_node("d")){ 85 | return false; 86 | } 87 | for (xml_node<> *child = node->first_node("d"); child; child = child->next_sibling()) // Each comment 88 | { 89 | if(!child){ 90 | continue; 91 | } 92 | std::string v = child->value(); 93 | bool isBlocked = false; 94 | for (auto i = blockWords.begin();i != blockWords.end(); i++ ){ 95 | if(v.find(*i) != std::string::npos){ 96 | isBlocked = true; 97 | } 98 | } 99 | if(isBlocked){ 100 | continue; 101 | } 102 | 103 | const char *separator = ","; // Separator of comment properties 104 | char *p; 105 | 106 | /* Arg1 : Appear time 107 | The time of comment appear. 108 | */ 109 | p = strtok(child->first_attribute("p")->value(), separator); 110 | if(!p){ continue; } 111 | float appear_time = atof(p); 112 | 113 | /* Arg2 : Comment mode 114 | 123 - Scroll comment 115 | 4 - Bottom comment 116 | 5 - Top comment 117 | 6 - Reverse comment 118 | 7 - Positioned comment 119 | 8 - Javascript comment ( not convert ) 120 | */ 121 | p = strtok(NULL, separator); 122 | if(!p){ continue; } 123 | int comment_mode = atoi(p); 124 | 125 | /* Arg3 : Font size ( not needed )*/ 126 | p = strtok(NULL, separator); 127 | if(!p){ continue; } 128 | //int font_size = atoi(p); 129 | 130 | /* Arg4 : Font color */ 131 | p = strtok(NULL, separator); 132 | if(!p){ continue; } 133 | int font_color = atoi(p); 134 | 135 | /* Arg5 : Unix timestamp ( not needed ) */ 136 | /* Arg6 : comment pool ( not needed ) */ 137 | /* Arg7 : sender uid ( not needed ) */ 138 | /* Arg8 : database rowID ( not needed ) */ 139 | 140 | ass->AppendComment(appear_time, comment_mode, font_color, child->value()); 141 | } 142 | 143 | 144 | ass->WriteToDisk(disallowModes); 145 | 146 | return true; 147 | } 148 | 149 | /* 150 | Convert comments to .ass subtitle 151 | 152 | infile: comment file path 153 | outfile: output file path 154 | width: video width 155 | height: video height 156 | font: font name 157 | alpha: comment alpha 158 | duration_marquee:Duration of scrolling comment 159 | duration_still:Duration of still comment 160 | */ 161 | 162 | void danmaku2ass(const char *infile,const char *outfile,int width,int height,const char *font,float fontsize,float alpha,float duration_marquee,float duration_still){ 163 | std::ifstream input(infile); 164 | string headline; 165 | getline(input,headline); 166 | int type = GetCommentType(headline); 167 | CommentParser *p = new CommentParser; 168 | p->SetFile(infile, outfile); 169 | p->SetRes(width, height); 170 | p->SetFont(font, fontsize); 171 | p->SetDuration(duration_marquee, duration_still); 172 | p->SetAlpha(alpha); 173 | if(type == 1){ 174 | //cout << "Avfun format detected ! Converting..." << endl; 175 | cout << "Sorry , The format is not supported" << endl; 176 | }else if(type == 2){ 177 | cout << "Bilibili format detected ! Converting..." << endl; 178 | bool result = p->Convert(type); 179 | if(result){ 180 | cout << "Convert succeed" << endl; 181 | }else{ 182 | cout << "Convert failed" << endl; 183 | } 184 | }else if(type == 3){ 185 | //cout << "Niconico format detected ! Converting..." << endl; 186 | cout << "Sorry , The format is not supported" << endl; 187 | }else{ 188 | cout << "ERROR: Unable to get comment type" << endl; 189 | } 190 | input.close(); 191 | delete p; 192 | } 193 | 194 | 195 | #ifndef __danmaku2ass_native__NoMainFunc__ 196 | 197 | int main(int argc,char *argv[]){ 198 | cout << "Starting danmaku2ass native..." << endl; 199 | clock_t begin = clock(); 200 | 201 | map args; 202 | 203 | int count; 204 | for (count=0; count 13 | #include 14 | 15 | void danmaku2ass(const char *infile,const char *outfile,int width,int height,const char *font,float fontsize,float alpha,float duration_marquee,float duration_still); 16 | 17 | #endif 18 | 19 | #ifndef bilibili_danmaku2ass_hpp 20 | #define bilibili_danmaku2ass_hpp 21 | int GetCommentType(std::string headline); 22 | 23 | class CommentParser{ 24 | private: 25 | std::vector blockWords; 26 | std::vector disallowModes; 27 | const char *in; 28 | const char *out; 29 | int width = 1280; 30 | int height = 720; 31 | const char *font = "Heiti"; 32 | float fontsize = 20; 33 | float alpha = 0.8; 34 | float duration_marquee = 5; 35 | float duration_still = 5; 36 | 37 | bool _convertBilibili(); 38 | public: 39 | void SetFile(const char *infile,const char *outfile){ in = infile; out = outfile; }; 40 | void SetRes(int w,int h){ width = w; height = h; }; 41 | void SetFont(const char *name,float size){ font = name; fontsize = size; }; 42 | void SetAlpha(float a){ alpha = a; }; 43 | void SetDuration(float scroll,float still){ duration_marquee = scroll; duration_still = still; }; 44 | void SetBlockWord(const char *word){ blockWords.push_back(word); }; 45 | void AddDisallowMode(int mode){ disallowModes.push_back(mode); }; // 1 scroll 2 top 3 bottom 46 | bool Convert(int type); 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /rapidxml/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Use of this software is granted under one of the following two licenses, 2 | to be chosen freely by the user. 3 | 4 | 1. Boost Software License - Version 1.0 - August 17th, 2003 5 | =============================================================================== 6 | 7 | Copyright (c) 2006, 2007 Marcin Kalicinski 8 | 9 | Permission is hereby granted, free of charge, to any person or organization 10 | obtaining a copy of the software and accompanying documentation covered by 11 | this license (the "Software") to use, reproduce, display, distribute, 12 | execute, and transmit the Software, and to prepare derivative works of the 13 | Software, and to permit third-parties to whom the Software is furnished to 14 | do so, all subject to the following: 15 | 16 | The copyright notices in the Software and this entire statement, including 17 | the above license grant, this restriction and the following disclaimer, 18 | must be included in all copies of the Software, in whole or in part, and 19 | all derivative works of the Software, unless such copies or derivative 20 | works are solely in the form of machine-executable object code generated by 21 | a source language processor. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 26 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 27 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 28 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 29 | DEALINGS IN THE SOFTWARE. 30 | 31 | 2. The MIT License 32 | =============================================================================== 33 | 34 | Copyright (c) 2006, 2007 Marcin Kalicinski 35 | 36 | Permission is hereby granted, free of charge, to any person obtaining a copy 37 | of this software and associated documentation files (the "Software"), to deal 38 | in the Software without restriction, including without limitation the rights 39 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 40 | of the Software, and to permit persons to whom the Software is furnished to do so, 41 | subject to the following conditions: 42 | 43 | The above copyright notice and this permission notice shall be included in all 44 | copies or substantial portions of the Software. 45 | 46 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 47 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 48 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 49 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 50 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 51 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 52 | IN THE SOFTWARE. -------------------------------------------------------------------------------- /rapidxml/rapidxml.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RAPIDXML_HPP_INCLUDED 2 | #define RAPIDXML_HPP_INCLUDED 3 | 4 | // Copyright (C) 2006, 2009 Marcin Kalicinski 5 | // Version 1.13 6 | // Revision $DateTime: 2009/05/13 01:46:17 $ 7 | //! \file rapidxml.hpp This file contains rapidxml parser and DOM implementation 8 | 9 | // If standard library is disabled, user must provide implementations of required functions and typedefs 10 | #if !defined(RAPIDXML_NO_STDLIB) 11 | #include // For std::size_t 12 | #include // For assert 13 | #include // For placement new 14 | #endif 15 | 16 | // On MSVC, disable "conditional expression is constant" warning (level 4). 17 | // This warning is almost impossible to avoid with certain types of templated code 18 | #ifdef _MSC_VER 19 | #pragma warning(push) 20 | #pragma warning(disable:4127) // Conditional expression is constant 21 | #endif 22 | 23 | /////////////////////////////////////////////////////////////////////////// 24 | // RAPIDXML_PARSE_ERROR 25 | 26 | #if defined(RAPIDXML_NO_EXCEPTIONS) 27 | 28 | #define RAPIDXML_PARSE_ERROR(what, where) { parse_error_handler(what, where); assert(0); } 29 | 30 | namespace rapidxml 31 | { 32 | //! When exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, 33 | //! this function is called to notify user about the error. 34 | //! It must be defined by the user. 35 | //!

36 | //! This function cannot return. If it does, the results are undefined. 37 | //!

38 | //! A very simple definition might look like that: 39 | //!
  40 |     //! void %rapidxml::%parse_error_handler(const char *what, void *where)
  41 |     //! {
  42 |     //!     std::cout << "Parse error: " << what << "\n";
  43 |     //!     std::abort();
  44 |     //! }
  45 |     //! 
46 | //! \param what Human readable description of the error. 47 | //! \param where Pointer to character data where error was detected. 48 | void parse_error_handler(const char *what, void *where); 49 | } 50 | 51 | #else 52 | 53 | #include // For std::exception 54 | 55 | #define RAPIDXML_PARSE_ERROR(what, where) throw parse_error(what, where) 56 | 57 | namespace rapidxml 58 | { 59 | 60 | //! Parse error exception. 61 | //! This exception is thrown by the parser when an error occurs. 62 | //! Use what() function to get human-readable error message. 63 | //! Use where() function to get a pointer to position within source text where error was detected. 64 | //!

65 | //! If throwing exceptions by the parser is undesirable, 66 | //! it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included. 67 | //! This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception. 68 | //! This function must be defined by the user. 69 | //!

70 | //! This class derives from std::exception class. 71 | class parse_error: public std::exception 72 | { 73 | 74 | public: 75 | 76 | //! Constructs parse error 77 | parse_error(const char *what, void *where) 78 | : m_what(what) 79 | , m_where(where) 80 | { 81 | } 82 | 83 | //! Gets human readable description of error. 84 | //! \return Pointer to null terminated description of the error. 85 | virtual const char *what() const throw() 86 | { 87 | return m_what; 88 | } 89 | 90 | //! Gets pointer to character data where error happened. 91 | //! Ch should be the same as char type of xml_document that produced the error. 92 | //! \return Pointer to location within the parsed string where error occured. 93 | template 94 | Ch *where() const 95 | { 96 | return reinterpret_cast(m_where); 97 | } 98 | 99 | private: 100 | 101 | const char *m_what; 102 | void *m_where; 103 | 104 | }; 105 | } 106 | 107 | #endif 108 | 109 | /////////////////////////////////////////////////////////////////////////// 110 | // Pool sizes 111 | 112 | #ifndef RAPIDXML_STATIC_POOL_SIZE 113 | // Size of static memory block of memory_pool. 114 | // Define RAPIDXML_STATIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. 115 | // No dynamic memory allocations are performed by memory_pool until static memory is exhausted. 116 | #define RAPIDXML_STATIC_POOL_SIZE (64 * 1024) 117 | #endif 118 | 119 | #ifndef RAPIDXML_DYNAMIC_POOL_SIZE 120 | // Size of dynamic memory block of memory_pool. 121 | // Define RAPIDXML_DYNAMIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. 122 | // After the static block is exhausted, dynamic blocks with approximately this size are allocated by memory_pool. 123 | #define RAPIDXML_DYNAMIC_POOL_SIZE (64 * 1024) 124 | #endif 125 | 126 | #ifndef RAPIDXML_ALIGNMENT 127 | // Memory allocation alignment. 128 | // Define RAPIDXML_ALIGNMENT before including rapidxml.hpp if you want to override the default value, which is the size of pointer. 129 | // All memory allocations for nodes, attributes and strings will be aligned to this value. 130 | // This must be a power of 2 and at least 1, otherwise memory_pool will not work. 131 | #define RAPIDXML_ALIGNMENT sizeof(void *) 132 | #endif 133 | 134 | namespace rapidxml 135 | { 136 | // Forward declarations 137 | template class xml_node; 138 | template class xml_attribute; 139 | template class xml_document; 140 | 141 | //! Enumeration listing all node types produced by the parser. 142 | //! Use xml_node::type() function to query node type. 143 | enum node_type 144 | { 145 | node_document, //!< A document node. Name and value are empty. 146 | node_element, //!< An element node. Name contains element name. Value contains text of first data node. 147 | node_data, //!< A data node. Name is empty. Value contains data text. 148 | node_cdata, //!< A CDATA node. Name is empty. Value contains data text. 149 | node_comment, //!< A comment node. Name is empty. Value contains comment text. 150 | node_declaration, //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes. 151 | node_doctype, //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text. 152 | node_pi //!< A PI node. Name contains target. Value contains instructions. 153 | }; 154 | 155 | /////////////////////////////////////////////////////////////////////// 156 | // Parsing flags 157 | 158 | //! Parse flag instructing the parser to not create data nodes. 159 | //! Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified. 160 | //! Can be combined with other flags by use of | operator. 161 | //!

162 | //! See xml_document::parse() function. 163 | const int parse_no_data_nodes = 0x1; 164 | 165 | //! Parse flag instructing the parser to not use text of first data node as a value of parent element. 166 | //! Can be combined with other flags by use of | operator. 167 | //! Note that child data nodes of element node take precendence over its value when printing. 168 | //! That is, if element has one or more child data nodes and a value, the value will be ignored. 169 | //! Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements. 170 | //!

171 | //! See xml_document::parse() function. 172 | const int parse_no_element_values = 0x2; 173 | 174 | //! Parse flag instructing the parser to not place zero terminators after strings in the source text. 175 | //! By default zero terminators are placed, modifying source text. 176 | //! Can be combined with other flags by use of | operator. 177 | //!

178 | //! See xml_document::parse() function. 179 | const int parse_no_string_terminators = 0x4; 180 | 181 | //! Parse flag instructing the parser to not translate entities in the source text. 182 | //! By default entities are translated, modifying source text. 183 | //! Can be combined with other flags by use of | operator. 184 | //!

185 | //! See xml_document::parse() function. 186 | const int parse_no_entity_translation = 0x8; 187 | 188 | //! Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters. 189 | //! By default, UTF-8 handling is enabled. 190 | //! Can be combined with other flags by use of | operator. 191 | //!

192 | //! See xml_document::parse() function. 193 | const int parse_no_utf8 = 0x10; 194 | 195 | //! Parse flag instructing the parser to create XML declaration node. 196 | //! By default, declaration node is not created. 197 | //! Can be combined with other flags by use of | operator. 198 | //!

199 | //! See xml_document::parse() function. 200 | const int parse_declaration_node = 0x20; 201 | 202 | //! Parse flag instructing the parser to create comments nodes. 203 | //! By default, comment nodes are not created. 204 | //! Can be combined with other flags by use of | operator. 205 | //!

206 | //! See xml_document::parse() function. 207 | const int parse_comment_nodes = 0x40; 208 | 209 | //! Parse flag instructing the parser to create DOCTYPE node. 210 | //! By default, doctype node is not created. 211 | //! Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one. 212 | //! Can be combined with other flags by use of | operator. 213 | //!

214 | //! See xml_document::parse() function. 215 | const int parse_doctype_node = 0x80; 216 | 217 | //! Parse flag instructing the parser to create PI nodes. 218 | //! By default, PI nodes are not created. 219 | //! Can be combined with other flags by use of | operator. 220 | //!

221 | //! See xml_document::parse() function. 222 | const int parse_pi_nodes = 0x100; 223 | 224 | //! Parse flag instructing the parser to validate closing tag names. 225 | //! If not set, name inside closing tag is irrelevant to the parser. 226 | //! By default, closing tags are not validated. 227 | //! Can be combined with other flags by use of | operator. 228 | //!

229 | //! See xml_document::parse() function. 230 | const int parse_validate_closing_tags = 0x200; 231 | 232 | //! Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes. 233 | //! By default, whitespace is not trimmed. 234 | //! This flag does not cause the parser to modify source text. 235 | //! Can be combined with other flags by use of | operator. 236 | //!

237 | //! See xml_document::parse() function. 238 | const int parse_trim_whitespace = 0x400; 239 | 240 | //! Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character. 241 | //! Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag. 242 | //! By default, whitespace is not normalized. 243 | //! If this flag is specified, source text will be modified. 244 | //! Can be combined with other flags by use of | operator. 245 | //!

246 | //! See xml_document::parse() function. 247 | const int parse_normalize_whitespace = 0x800; 248 | 249 | // Compound flags 250 | 251 | //! Parse flags which represent default behaviour of the parser. 252 | //! This is always equal to 0, so that all other flags can be simply ored together. 253 | //! Normally there is no need to inconveniently disable flags by anding with their negated (~) values. 254 | //! This also means that meaning of each flag is a negation of the default setting. 255 | //! For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is enabled by default, 256 | //! and using the flag will disable it. 257 | //!

258 | //! See xml_document::parse() function. 259 | const int parse_default = 0; 260 | 261 | //! A combination of parse flags that forbids any modifications of the source text. 262 | //! This also results in faster parsing. However, note that the following will occur: 263 | //!
    264 | //!
  • names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends
  • 265 | //!
  • entities will not be translated
  • 266 | //!
  • whitespace will not be normalized
  • 267 | //!
268 | //! See xml_document::parse() function. 269 | const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation; 270 | 271 | //! A combination of parse flags resulting in fastest possible parsing, without sacrificing important data. 272 | //!

273 | //! See xml_document::parse() function. 274 | const int parse_fastest = parse_non_destructive | parse_no_data_nodes; 275 | 276 | //! A combination of parse flags resulting in largest amount of data being extracted. 277 | //! This usually results in slowest parsing. 278 | //!

279 | //! See xml_document::parse() function. 280 | const int parse_full = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags; 281 | 282 | /////////////////////////////////////////////////////////////////////// 283 | // Internals 284 | 285 | //! \cond internal 286 | namespace internal 287 | { 288 | 289 | // Struct that contains lookup tables for the parser 290 | // It must be a template to allow correct linking (because it has static data members, which are defined in a header file). 291 | template 292 | struct lookup_tables 293 | { 294 | static const unsigned char lookup_whitespace[256]; // Whitespace table 295 | static const unsigned char lookup_node_name[256]; // Node name table 296 | static const unsigned char lookup_text[256]; // Text table 297 | static const unsigned char lookup_text_pure_no_ws[256]; // Text table 298 | static const unsigned char lookup_text_pure_with_ws[256]; // Text table 299 | static const unsigned char lookup_attribute_name[256]; // Attribute name table 300 | static const unsigned char lookup_attribute_data_1[256]; // Attribute data table with single quote 301 | static const unsigned char lookup_attribute_data_1_pure[256]; // Attribute data table with single quote 302 | static const unsigned char lookup_attribute_data_2[256]; // Attribute data table with double quotes 303 | static const unsigned char lookup_attribute_data_2_pure[256]; // Attribute data table with double quotes 304 | static const unsigned char lookup_digits[256]; // Digits 305 | static const unsigned char lookup_upcase[256]; // To uppercase conversion table for ASCII characters 306 | }; 307 | 308 | // Find length of the string 309 | template 310 | inline std::size_t measure(const Ch *p) 311 | { 312 | const Ch *tmp = p; 313 | while (*tmp) 314 | ++tmp; 315 | return tmp - p; 316 | } 317 | 318 | // Compare strings for equality 319 | template 320 | inline bool compare(const Ch *p1, std::size_t size1, const Ch *p2, std::size_t size2, bool case_sensitive) 321 | { 322 | if (size1 != size2) 323 | return false; 324 | if (case_sensitive) 325 | { 326 | for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) 327 | if (*p1 != *p2) 328 | return false; 329 | } 330 | else 331 | { 332 | for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) 333 | if (lookup_tables<0>::lookup_upcase[static_cast(*p1)] != lookup_tables<0>::lookup_upcase[static_cast(*p2)]) 334 | return false; 335 | } 336 | return true; 337 | } 338 | } 339 | //! \endcond 340 | 341 | /////////////////////////////////////////////////////////////////////// 342 | // Memory pool 343 | 344 | //! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation. 345 | //! In most cases, you will not need to use this class directly. 346 | //! However, if you need to create nodes manually or modify names/values of nodes, 347 | //! you are encouraged to use memory_pool of relevant xml_document to allocate the memory. 348 | //! Not only is this faster than allocating them by using new operator, 349 | //! but also their lifetime will be tied to the lifetime of document, 350 | //! possibly simplyfing memory management. 351 | //!

352 | //! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. 353 | //! You can also call allocate_string() function to allocate strings. 354 | //! Such strings can then be used as names or values of nodes without worrying about their lifetime. 355 | //! Note that there is no free() function -- all allocations are freed at once when clear() function is called, 356 | //! or when the pool is destroyed. 357 | //!

358 | //! It is also possible to create a standalone memory_pool, and use it 359 | //! to allocate nodes, whose lifetime will not be tied to any document. 360 | //!

361 | //! Pool maintains RAPIDXML_STATIC_POOL_SIZE bytes of statically allocated memory. 362 | //! Until static memory is exhausted, no dynamic memory allocations are done. 363 | //! When static memory is exhausted, pool allocates additional blocks of memory of size RAPIDXML_DYNAMIC_POOL_SIZE each, 364 | //! by using global new[] and delete[] operators. 365 | //! This behaviour can be changed by setting custom allocation routines. 366 | //! Use set_allocator() function to set them. 367 | //!

368 | //! Allocations for nodes, attributes and strings are aligned at RAPIDXML_ALIGNMENT bytes. 369 | //! This value defaults to the size of pointer on target architecture. 370 | //!

371 | //! To obtain absolutely top performance from the parser, 372 | //! it is important that all nodes are allocated from a single, contiguous block of memory. 373 | //! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably. 374 | //! If required, you can tweak RAPIDXML_STATIC_POOL_SIZE, RAPIDXML_DYNAMIC_POOL_SIZE and RAPIDXML_ALIGNMENT 375 | //! to obtain best wasted memory to performance compromise. 376 | //! To do it, define their values before rapidxml.hpp file is included. 377 | //! \param Ch Character type of created nodes. 378 | template 379 | class memory_pool 380 | { 381 | 382 | public: 383 | 384 | //! \cond internal 385 | typedef void *(alloc_func)(std::size_t); // Type of user-defined function used to allocate memory 386 | typedef void (free_func)(void *); // Type of user-defined function used to free memory 387 | //! \endcond 388 | 389 | //! Constructs empty pool with default allocator functions. 390 | memory_pool() 391 | : m_alloc_func(0) 392 | , m_free_func(0) 393 | { 394 | init(); 395 | } 396 | 397 | //! Destroys pool and frees all the memory. 398 | //! This causes memory occupied by nodes allocated by the pool to be freed. 399 | //! Nodes allocated from the pool are no longer valid. 400 | ~memory_pool() 401 | { 402 | clear(); 403 | } 404 | 405 | //! Allocates a new node from the pool, and optionally assigns name and value to it. 406 | //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. 407 | //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function 408 | //! will call rapidxml::parse_error_handler() function. 409 | //! \param type Type of node to create. 410 | //! \param name Name to assign to the node, or 0 to assign no name. 411 | //! \param value Value to assign to the node, or 0 to assign no value. 412 | //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. 413 | //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. 414 | //! \return Pointer to allocated node. This pointer will never be NULL. 415 | xml_node *allocate_node(node_type type, 416 | const Ch *name = 0, const Ch *value = 0, 417 | std::size_t name_size = 0, std::size_t value_size = 0) 418 | { 419 | void *memory = allocate_aligned(sizeof(xml_node)); 420 | xml_node *node = new(memory) xml_node(type); 421 | if (name) 422 | { 423 | if (name_size > 0) 424 | node->name(name, name_size); 425 | else 426 | node->name(name); 427 | } 428 | if (value) 429 | { 430 | if (value_size > 0) 431 | node->value(value, value_size); 432 | else 433 | node->value(value); 434 | } 435 | return node; 436 | } 437 | 438 | //! Allocates a new attribute from the pool, and optionally assigns name and value to it. 439 | //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. 440 | //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function 441 | //! will call rapidxml::parse_error_handler() function. 442 | //! \param name Name to assign to the attribute, or 0 to assign no name. 443 | //! \param value Value to assign to the attribute, or 0 to assign no value. 444 | //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. 445 | //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. 446 | //! \return Pointer to allocated attribute. This pointer will never be NULL. 447 | xml_attribute *allocate_attribute(const Ch *name = 0, const Ch *value = 0, 448 | std::size_t name_size = 0, std::size_t value_size = 0) 449 | { 450 | void *memory = allocate_aligned(sizeof(xml_attribute)); 451 | xml_attribute *attribute = new(memory) xml_attribute; 452 | if (name) 453 | { 454 | if (name_size > 0) 455 | attribute->name(name, name_size); 456 | else 457 | attribute->name(name); 458 | } 459 | if (value) 460 | { 461 | if (value_size > 0) 462 | attribute->value(value, value_size); 463 | else 464 | attribute->value(value); 465 | } 466 | return attribute; 467 | } 468 | 469 | //! Allocates a char array of given size from the pool, and optionally copies a given string to it. 470 | //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. 471 | //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function 472 | //! will call rapidxml::parse_error_handler() function. 473 | //! \param source String to initialize the allocated memory with, or 0 to not initialize it. 474 | //! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated. 475 | //! \return Pointer to allocated char array. This pointer will never be NULL. 476 | Ch *allocate_string(const Ch *source = 0, std::size_t size = 0) 477 | { 478 | assert(source || size); // Either source or size (or both) must be specified 479 | if (size == 0) 480 | size = internal::measure(source) + 1; 481 | Ch *result = static_cast(allocate_aligned(size * sizeof(Ch))); 482 | if (source) 483 | for (std::size_t i = 0; i < size; ++i) 484 | result[i] = source[i]; 485 | return result; 486 | } 487 | 488 | //! Clones an xml_node and its hierarchy of child nodes and attributes. 489 | //! Nodes and attributes are allocated from this memory pool. 490 | //! Names and values are not cloned, they are shared between the clone and the source. 491 | //! Result node can be optionally specified as a second parameter, 492 | //! in which case its contents will be replaced with cloned source node. 493 | //! This is useful when you want to clone entire document. 494 | //! \param source Node to clone. 495 | //! \param result Node to put results in, or 0 to automatically allocate result node 496 | //! \return Pointer to cloned node. This pointer will never be NULL. 497 | xml_node *clone_node(const xml_node *source, xml_node *result = 0) 498 | { 499 | // Prepare result node 500 | if (result) 501 | { 502 | result->remove_all_attributes(); 503 | result->remove_all_nodes(); 504 | result->type(source->type()); 505 | } 506 | else 507 | result = allocate_node(source->type()); 508 | 509 | // Clone name and value 510 | result->name(source->name(), source->name_size()); 511 | result->value(source->value(), source->value_size()); 512 | 513 | // Clone child nodes and attributes 514 | for (xml_node *child = source->first_node(); child; child = child->next_sibling()) 515 | result->append_node(clone_node(child)); 516 | for (xml_attribute *attr = source->first_attribute(); attr; attr = attr->next_attribute()) 517 | result->append_attribute(allocate_attribute(attr->name(), attr->value(), attr->name_size(), attr->value_size())); 518 | 519 | return result; 520 | } 521 | 522 | //! Clears the pool. 523 | //! This causes memory occupied by nodes allocated by the pool to be freed. 524 | //! Any nodes or strings allocated from the pool will no longer be valid. 525 | void clear() 526 | { 527 | while (m_begin != m_static_memory) 528 | { 529 | char *previous_begin = reinterpret_cast
(align(m_begin))->previous_begin; 530 | if (m_free_func) 531 | m_free_func(m_begin); 532 | else 533 | delete[] m_begin; 534 | m_begin = previous_begin; 535 | } 536 | init(); 537 | } 538 | 539 | //! Sets or resets the user-defined memory allocation functions for the pool. 540 | //! This can only be called when no memory is allocated from the pool yet, otherwise results are undefined. 541 | //! Allocation function must not return invalid pointer on failure. It should either throw, 542 | //! stop the program, or use longjmp() function to pass control to other place of program. 543 | //! If it returns invalid pointer, results are undefined. 544 | //!

545 | //! User defined allocation functions must have the following forms: 546 | //!
547 | //!
void *allocate(std::size_t size); 548 | //!
void free(void *pointer); 549 | //!

550 | //! \param af Allocation function, or 0 to restore default function 551 | //! \param ff Free function, or 0 to restore default function 552 | void set_allocator(alloc_func *af, free_func *ff) 553 | { 554 | assert(m_begin == m_static_memory && m_ptr == align(m_begin)); // Verify that no memory is allocated yet 555 | m_alloc_func = af; 556 | m_free_func = ff; 557 | } 558 | 559 | private: 560 | 561 | struct header 562 | { 563 | char *previous_begin; 564 | }; 565 | 566 | void init() 567 | { 568 | m_begin = m_static_memory; 569 | m_ptr = align(m_begin); 570 | m_end = m_static_memory + sizeof(m_static_memory); 571 | } 572 | 573 | char *align(char *ptr) 574 | { 575 | std::size_t alignment = ((RAPIDXML_ALIGNMENT - (std::size_t(ptr) & (RAPIDXML_ALIGNMENT - 1))) & (RAPIDXML_ALIGNMENT - 1)); 576 | return ptr + alignment; 577 | } 578 | 579 | char *allocate_raw(std::size_t size) 580 | { 581 | // Allocate 582 | void *memory; 583 | if (m_alloc_func) // Allocate memory using either user-specified allocation function or global operator new[] 584 | { 585 | memory = m_alloc_func(size); 586 | assert(memory); // Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp 587 | } 588 | else 589 | { 590 | memory = new char[size]; 591 | #ifdef RAPIDXML_NO_EXCEPTIONS 592 | if (!memory) // If exceptions are disabled, verify memory allocation, because new will not be able to throw bad_alloc 593 | RAPIDXML_PARSE_ERROR("out of memory", 0); 594 | #endif 595 | } 596 | return static_cast(memory); 597 | } 598 | 599 | void *allocate_aligned(std::size_t size) 600 | { 601 | // Calculate aligned pointer 602 | char *result = align(m_ptr); 603 | 604 | // If not enough memory left in current pool, allocate a new pool 605 | if (result + size > m_end) 606 | { 607 | // Calculate required pool size (may be bigger than RAPIDXML_DYNAMIC_POOL_SIZE) 608 | std::size_t pool_size = RAPIDXML_DYNAMIC_POOL_SIZE; 609 | if (pool_size < size) 610 | pool_size = size; 611 | 612 | // Allocate 613 | std::size_t alloc_size = sizeof(header) + (2 * RAPIDXML_ALIGNMENT - 2) + pool_size; // 2 alignments required in worst case: one for header, one for actual allocation 614 | char *raw_memory = allocate_raw(alloc_size); 615 | 616 | // Setup new pool in allocated memory 617 | char *pool = align(raw_memory); 618 | header *new_header = reinterpret_cast
(pool); 619 | new_header->previous_begin = m_begin; 620 | m_begin = raw_memory; 621 | m_ptr = pool + sizeof(header); 622 | m_end = raw_memory + alloc_size; 623 | 624 | // Calculate aligned pointer again using new pool 625 | result = align(m_ptr); 626 | } 627 | 628 | // Update pool and return aligned pointer 629 | m_ptr = result + size; 630 | return result; 631 | } 632 | 633 | char *m_begin; // Start of raw memory making up current pool 634 | char *m_ptr; // First free byte in current pool 635 | char *m_end; // One past last available byte in current pool 636 | char m_static_memory[RAPIDXML_STATIC_POOL_SIZE]; // Static raw memory 637 | alloc_func *m_alloc_func; // Allocator function, or 0 if default is to be used 638 | free_func *m_free_func; // Free function, or 0 if default is to be used 639 | }; 640 | 641 | /////////////////////////////////////////////////////////////////////////// 642 | // XML base 643 | 644 | //! Base class for xml_node and xml_attribute implementing common functions: 645 | //! name(), name_size(), value(), value_size() and parent(). 646 | //! \param Ch Character type to use 647 | template 648 | class xml_base 649 | { 650 | 651 | public: 652 | 653 | /////////////////////////////////////////////////////////////////////////// 654 | // Construction & destruction 655 | 656 | // Construct a base with empty name, value and parent 657 | xml_base() 658 | : m_name(0) 659 | , m_value(0) 660 | , m_parent(0) 661 | { 662 | } 663 | 664 | /////////////////////////////////////////////////////////////////////////// 665 | // Node data access 666 | 667 | //! Gets name of the node. 668 | //! Interpretation of name depends on type of node. 669 | //! Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. 670 | //!

671 | //! Use name_size() function to determine length of the name. 672 | //! \return Name of node, or empty string if node has no name. 673 | Ch *name() const 674 | { 675 | return m_name ? m_name : nullstr(); 676 | } 677 | 678 | //! Gets size of node name, not including terminator character. 679 | //! This function works correctly irrespective of whether name is or is not zero terminated. 680 | //! \return Size of node name, in characters. 681 | std::size_t name_size() const 682 | { 683 | return m_name ? m_name_size : 0; 684 | } 685 | 686 | //! Gets value of node. 687 | //! Interpretation of value depends on type of node. 688 | //! Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. 689 | //!

690 | //! Use value_size() function to determine length of the value. 691 | //! \return Value of node, or empty string if node has no value. 692 | Ch *value() const 693 | { 694 | return m_value ? m_value : nullstr(); 695 | } 696 | 697 | //! Gets size of node value, not including terminator character. 698 | //! This function works correctly irrespective of whether value is or is not zero terminated. 699 | //! \return Size of node value, in characters. 700 | std::size_t value_size() const 701 | { 702 | return m_value ? m_value_size : 0; 703 | } 704 | 705 | /////////////////////////////////////////////////////////////////////////// 706 | // Node modification 707 | 708 | //! Sets name of node to a non zero-terminated string. 709 | //! See \ref ownership_of_strings. 710 | //!

711 | //! Note that node does not own its name or value, it only stores a pointer to it. 712 | //! It will not delete or otherwise free the pointer on destruction. 713 | //! It is reponsibility of the user to properly manage lifetime of the string. 714 | //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - 715 | //! on destruction of the document the string will be automatically freed. 716 | //!

717 | //! Size of name must be specified separately, because name does not have to be zero terminated. 718 | //! Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated). 719 | //! \param name Name of node to set. Does not have to be zero terminated. 720 | //! \param size Size of name, in characters. This does not include zero terminator, if one is present. 721 | void name(const Ch *name, std::size_t size) 722 | { 723 | m_name = const_cast(name); 724 | m_name_size = size; 725 | } 726 | 727 | //! Sets name of node to a zero-terminated string. 728 | //! See also \ref ownership_of_strings and xml_node::name(const Ch *, std::size_t). 729 | //! \param name Name of node to set. Must be zero terminated. 730 | void name(const Ch *name) 731 | { 732 | this->name(name, internal::measure(name)); 733 | } 734 | 735 | //! Sets value of node to a non zero-terminated string. 736 | //! See \ref ownership_of_strings. 737 | //!

738 | //! Note that node does not own its name or value, it only stores a pointer to it. 739 | //! It will not delete or otherwise free the pointer on destruction. 740 | //! It is reponsibility of the user to properly manage lifetime of the string. 741 | //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - 742 | //! on destruction of the document the string will be automatically freed. 743 | //!

744 | //! Size of value must be specified separately, because it does not have to be zero terminated. 745 | //! Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated). 746 | //!

747 | //! If an element has a child node of type node_data, it will take precedence over element value when printing. 748 | //! If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser. 749 | //! \param value value of node to set. Does not have to be zero terminated. 750 | //! \param size Size of value, in characters. This does not include zero terminator, if one is present. 751 | void value(const Ch *value, std::size_t size) 752 | { 753 | m_value = const_cast(value); 754 | m_value_size = size; 755 | } 756 | 757 | //! Sets value of node to a zero-terminated string. 758 | //! See also \ref ownership_of_strings and xml_node::value(const Ch *, std::size_t). 759 | //! \param value Vame of node to set. Must be zero terminated. 760 | void value(const Ch *value) 761 | { 762 | this->value(value, internal::measure(value)); 763 | } 764 | 765 | /////////////////////////////////////////////////////////////////////////// 766 | // Related nodes access 767 | 768 | //! Gets node parent. 769 | //! \return Pointer to parent node, or 0 if there is no parent. 770 | xml_node *parent() const 771 | { 772 | return m_parent; 773 | } 774 | 775 | protected: 776 | 777 | // Return empty string 778 | static Ch *nullstr() 779 | { 780 | static Ch zero = Ch('\0'); 781 | return &zero; 782 | } 783 | 784 | Ch *m_name; // Name of node, or 0 if no name 785 | Ch *m_value; // Value of node, or 0 if no value 786 | std::size_t m_name_size; // Length of node name, or undefined of no name 787 | std::size_t m_value_size; // Length of node value, or undefined if no value 788 | xml_node *m_parent; // Pointer to parent node, or 0 if none 789 | 790 | }; 791 | 792 | //! Class representing attribute node of XML document. 793 | //! Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base). 794 | //! Note that after parse, both name and value of attribute will point to interior of source text used for parsing. 795 | //! Thus, this text must persist in memory for the lifetime of attribute. 796 | //! \param Ch Character type to use. 797 | template 798 | class xml_attribute: public xml_base 799 | { 800 | 801 | friend class xml_node; 802 | 803 | public: 804 | 805 | /////////////////////////////////////////////////////////////////////////// 806 | // Construction & destruction 807 | 808 | //! Constructs an empty attribute with the specified type. 809 | //! Consider using memory_pool of appropriate xml_document if allocating attributes manually. 810 | xml_attribute() 811 | { 812 | } 813 | 814 | /////////////////////////////////////////////////////////////////////////// 815 | // Related nodes access 816 | 817 | //! Gets document of which attribute is a child. 818 | //! \return Pointer to document that contains this attribute, or 0 if there is no parent document. 819 | xml_document *document() const 820 | { 821 | if (xml_node *node = this->parent()) 822 | { 823 | while (node->parent()) 824 | node = node->parent(); 825 | return node->type() == node_document ? static_cast *>(node) : 0; 826 | } 827 | else 828 | return 0; 829 | } 830 | 831 | //! Gets previous attribute, optionally matching attribute name. 832 | //! \param name Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero 833 | //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string 834 | //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters 835 | //! \return Pointer to found attribute, or 0 if not found. 836 | xml_attribute *previous_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const 837 | { 838 | if (name) 839 | { 840 | if (name_size == 0) 841 | name_size = internal::measure(name); 842 | for (xml_attribute *attribute = m_prev_attribute; attribute; attribute = attribute->m_prev_attribute) 843 | if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) 844 | return attribute; 845 | return 0; 846 | } 847 | else 848 | return this->m_parent ? m_prev_attribute : 0; 849 | } 850 | 851 | //! Gets next attribute, optionally matching attribute name. 852 | //! \param name Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero 853 | //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string 854 | //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters 855 | //! \return Pointer to found attribute, or 0 if not found. 856 | xml_attribute *next_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const 857 | { 858 | if (name) 859 | { 860 | if (name_size == 0) 861 | name_size = internal::measure(name); 862 | for (xml_attribute *attribute = m_next_attribute; attribute; attribute = attribute->m_next_attribute) 863 | if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) 864 | return attribute; 865 | return 0; 866 | } 867 | else 868 | return this->m_parent ? m_next_attribute : 0; 869 | } 870 | 871 | private: 872 | 873 | xml_attribute *m_prev_attribute; // Pointer to previous sibling of attribute, or 0 if none; only valid if parent is non-zero 874 | xml_attribute *m_next_attribute; // Pointer to next sibling of attribute, or 0 if none; only valid if parent is non-zero 875 | 876 | }; 877 | 878 | /////////////////////////////////////////////////////////////////////////// 879 | // XML node 880 | 881 | //! Class representing a node of XML document. 882 | //! Each node may have associated name and value strings, which are available through name() and value() functions. 883 | //! Interpretation of name and value depends on type of the node. 884 | //! Type of node can be determined by using type() function. 885 | //!

886 | //! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. 887 | //! Thus, this text must persist in the memory for the lifetime of node. 888 | //! \param Ch Character type to use. 889 | template 890 | class xml_node: public xml_base 891 | { 892 | 893 | public: 894 | 895 | /////////////////////////////////////////////////////////////////////////// 896 | // Construction & destruction 897 | 898 | //! Constructs an empty node with the specified type. 899 | //! Consider using memory_pool of appropriate document to allocate nodes manually. 900 | //! \param type Type of node to construct. 901 | xml_node(node_type type) 902 | : m_type(type) 903 | , m_first_node(0) 904 | , m_first_attribute(0) 905 | { 906 | } 907 | 908 | /////////////////////////////////////////////////////////////////////////// 909 | // Node data access 910 | 911 | //! Gets type of node. 912 | //! \return Type of node. 913 | node_type type() const 914 | { 915 | return m_type; 916 | } 917 | 918 | /////////////////////////////////////////////////////////////////////////// 919 | // Related nodes access 920 | 921 | //! Gets document of which node is a child. 922 | //! \return Pointer to document that contains this node, or 0 if there is no parent document. 923 | xml_document *document() const 924 | { 925 | xml_node *node = const_cast *>(this); 926 | while (node->parent()) 927 | node = node->parent(); 928 | return node->type() == node_document ? static_cast *>(node) : 0; 929 | } 930 | 931 | //! Gets first child node, optionally matching node name. 932 | //! \param name Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero 933 | //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string 934 | //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters 935 | //! \return Pointer to found child, or 0 if not found. 936 | xml_node *first_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const 937 | { 938 | if (name) 939 | { 940 | if (name_size == 0) 941 | name_size = internal::measure(name); 942 | for (xml_node *child = m_first_node; child; child = child->next_sibling()) 943 | if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) 944 | return child; 945 | return 0; 946 | } 947 | else 948 | return m_first_node; 949 | } 950 | 951 | //! Gets last child node, optionally matching node name. 952 | //! Behaviour is undefined if node has no children. 953 | //! Use first_node() to test if node has children. 954 | //! \param name Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero 955 | //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string 956 | //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters 957 | //! \return Pointer to found child, or 0 if not found. 958 | xml_node *last_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const 959 | { 960 | assert(m_first_node); // Cannot query for last child if node has no children 961 | if (name) 962 | { 963 | if (name_size == 0) 964 | name_size = internal::measure(name); 965 | for (xml_node *child = m_last_node; child; child = child->previous_sibling()) 966 | if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) 967 | return child; 968 | return 0; 969 | } 970 | else 971 | return m_last_node; 972 | } 973 | 974 | //! Gets previous sibling node, optionally matching node name. 975 | //! Behaviour is undefined if node has no parent. 976 | //! Use parent() to test if node has a parent. 977 | //! \param name Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero 978 | //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string 979 | //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters 980 | //! \return Pointer to found sibling, or 0 if not found. 981 | xml_node *previous_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const 982 | { 983 | assert(this->m_parent); // Cannot query for siblings if node has no parent 984 | if (name) 985 | { 986 | if (name_size == 0) 987 | name_size = internal::measure(name); 988 | for (xml_node *sibling = m_prev_sibling; sibling; sibling = sibling->m_prev_sibling) 989 | if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) 990 | return sibling; 991 | return 0; 992 | } 993 | else 994 | return m_prev_sibling; 995 | } 996 | 997 | //! Gets next sibling node, optionally matching node name. 998 | //! Behaviour is undefined if node has no parent. 999 | //! Use parent() to test if node has a parent. 1000 | //! \param name Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero 1001 | //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string 1002 | //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters 1003 | //! \return Pointer to found sibling, or 0 if not found. 1004 | xml_node *next_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const 1005 | { 1006 | assert(this->m_parent); // Cannot query for siblings if node has no parent 1007 | if (name) 1008 | { 1009 | if (name_size == 0) 1010 | name_size = internal::measure(name); 1011 | for (xml_node *sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling) 1012 | if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) 1013 | return sibling; 1014 | return 0; 1015 | } 1016 | else 1017 | return m_next_sibling; 1018 | } 1019 | 1020 | //! Gets first attribute of node, optionally matching attribute name. 1021 | //! \param name Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero 1022 | //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string 1023 | //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters 1024 | //! \return Pointer to found attribute, or 0 if not found. 1025 | xml_attribute *first_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const 1026 | { 1027 | if (name) 1028 | { 1029 | if (name_size == 0) 1030 | name_size = internal::measure(name); 1031 | for (xml_attribute *attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute) 1032 | if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) 1033 | return attribute; 1034 | return 0; 1035 | } 1036 | else 1037 | return m_first_attribute; 1038 | } 1039 | 1040 | //! Gets last attribute of node, optionally matching attribute name. 1041 | //! \param name Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero 1042 | //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string 1043 | //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters 1044 | //! \return Pointer to found attribute, or 0 if not found. 1045 | xml_attribute *last_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const 1046 | { 1047 | if (name) 1048 | { 1049 | if (name_size == 0) 1050 | name_size = internal::measure(name); 1051 | for (xml_attribute *attribute = m_last_attribute; attribute; attribute = attribute->m_prev_attribute) 1052 | if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) 1053 | return attribute; 1054 | return 0; 1055 | } 1056 | else 1057 | return m_first_attribute ? m_last_attribute : 0; 1058 | } 1059 | 1060 | /////////////////////////////////////////////////////////////////////////// 1061 | // Node modification 1062 | 1063 | //! Sets type of node. 1064 | //! \param type Type of node to set. 1065 | void type(node_type type) 1066 | { 1067 | m_type = type; 1068 | } 1069 | 1070 | /////////////////////////////////////////////////////////////////////////// 1071 | // Node manipulation 1072 | 1073 | //! Prepends a new child node. 1074 | //! The prepended child becomes the first child, and all existing children are moved one position back. 1075 | //! \param child Node to prepend. 1076 | void prepend_node(xml_node *child) 1077 | { 1078 | assert(child && !child->parent() && child->type() != node_document); 1079 | if (first_node()) 1080 | { 1081 | child->m_next_sibling = m_first_node; 1082 | m_first_node->m_prev_sibling = child; 1083 | } 1084 | else 1085 | { 1086 | child->m_next_sibling = 0; 1087 | m_last_node = child; 1088 | } 1089 | m_first_node = child; 1090 | child->m_parent = this; 1091 | child->m_prev_sibling = 0; 1092 | } 1093 | 1094 | //! Appends a new child node. 1095 | //! The appended child becomes the last child. 1096 | //! \param child Node to append. 1097 | void append_node(xml_node *child) 1098 | { 1099 | assert(child && !child->parent() && child->type() != node_document); 1100 | if (first_node()) 1101 | { 1102 | child->m_prev_sibling = m_last_node; 1103 | m_last_node->m_next_sibling = child; 1104 | } 1105 | else 1106 | { 1107 | child->m_prev_sibling = 0; 1108 | m_first_node = child; 1109 | } 1110 | m_last_node = child; 1111 | child->m_parent = this; 1112 | child->m_next_sibling = 0; 1113 | } 1114 | 1115 | //! Inserts a new child node at specified place inside the node. 1116 | //! All children after and including the specified node are moved one position back. 1117 | //! \param where Place where to insert the child, or 0 to insert at the back. 1118 | //! \param child Node to insert. 1119 | void insert_node(xml_node *where, xml_node *child) 1120 | { 1121 | assert(!where || where->parent() == this); 1122 | assert(child && !child->parent() && child->type() != node_document); 1123 | if (where == m_first_node) 1124 | prepend_node(child); 1125 | else if (where == 0) 1126 | append_node(child); 1127 | else 1128 | { 1129 | child->m_prev_sibling = where->m_prev_sibling; 1130 | child->m_next_sibling = where; 1131 | where->m_prev_sibling->m_next_sibling = child; 1132 | where->m_prev_sibling = child; 1133 | child->m_parent = this; 1134 | } 1135 | } 1136 | 1137 | //! Removes first child node. 1138 | //! If node has no children, behaviour is undefined. 1139 | //! Use first_node() to test if node has children. 1140 | void remove_first_node() 1141 | { 1142 | assert(first_node()); 1143 | xml_node *child = m_first_node; 1144 | m_first_node = child->m_next_sibling; 1145 | if (child->m_next_sibling) 1146 | child->m_next_sibling->m_prev_sibling = 0; 1147 | else 1148 | m_last_node = 0; 1149 | child->m_parent = 0; 1150 | } 1151 | 1152 | //! Removes last child of the node. 1153 | //! If node has no children, behaviour is undefined. 1154 | //! Use first_node() to test if node has children. 1155 | void remove_last_node() 1156 | { 1157 | assert(first_node()); 1158 | xml_node *child = m_last_node; 1159 | if (child->m_prev_sibling) 1160 | { 1161 | m_last_node = child->m_prev_sibling; 1162 | child->m_prev_sibling->m_next_sibling = 0; 1163 | } 1164 | else 1165 | m_first_node = 0; 1166 | child->m_parent = 0; 1167 | } 1168 | 1169 | //! Removes specified child from the node 1170 | // \param where Pointer to child to be removed. 1171 | void remove_node(xml_node *where) 1172 | { 1173 | assert(where && where->parent() == this); 1174 | assert(first_node()); 1175 | if (where == m_first_node) 1176 | remove_first_node(); 1177 | else if (where == m_last_node) 1178 | remove_last_node(); 1179 | else 1180 | { 1181 | where->m_prev_sibling->m_next_sibling = where->m_next_sibling; 1182 | where->m_next_sibling->m_prev_sibling = where->m_prev_sibling; 1183 | where->m_parent = 0; 1184 | } 1185 | } 1186 | 1187 | //! Removes all child nodes (but not attributes). 1188 | void remove_all_nodes() 1189 | { 1190 | for (xml_node *node = first_node(); node; node = node->m_next_sibling) 1191 | node->m_parent = 0; 1192 | m_first_node = 0; 1193 | } 1194 | 1195 | //! Prepends a new attribute to the node. 1196 | //! \param attribute Attribute to prepend. 1197 | void prepend_attribute(xml_attribute *attribute) 1198 | { 1199 | assert(attribute && !attribute->parent()); 1200 | if (first_attribute()) 1201 | { 1202 | attribute->m_next_attribute = m_first_attribute; 1203 | m_first_attribute->m_prev_attribute = attribute; 1204 | } 1205 | else 1206 | { 1207 | attribute->m_next_attribute = 0; 1208 | m_last_attribute = attribute; 1209 | } 1210 | m_first_attribute = attribute; 1211 | attribute->m_parent = this; 1212 | attribute->m_prev_attribute = 0; 1213 | } 1214 | 1215 | //! Appends a new attribute to the node. 1216 | //! \param attribute Attribute to append. 1217 | void append_attribute(xml_attribute *attribute) 1218 | { 1219 | assert(attribute && !attribute->parent()); 1220 | if (first_attribute()) 1221 | { 1222 | attribute->m_prev_attribute = m_last_attribute; 1223 | m_last_attribute->m_next_attribute = attribute; 1224 | } 1225 | else 1226 | { 1227 | attribute->m_prev_attribute = 0; 1228 | m_first_attribute = attribute; 1229 | } 1230 | m_last_attribute = attribute; 1231 | attribute->m_parent = this; 1232 | attribute->m_next_attribute = 0; 1233 | } 1234 | 1235 | //! Inserts a new attribute at specified place inside the node. 1236 | //! All attributes after and including the specified attribute are moved one position back. 1237 | //! \param where Place where to insert the attribute, or 0 to insert at the back. 1238 | //! \param attribute Attribute to insert. 1239 | void insert_attribute(xml_attribute *where, xml_attribute *attribute) 1240 | { 1241 | assert(!where || where->parent() == this); 1242 | assert(attribute && !attribute->parent()); 1243 | if (where == m_first_attribute) 1244 | prepend_attribute(attribute); 1245 | else if (where == 0) 1246 | append_attribute(attribute); 1247 | else 1248 | { 1249 | attribute->m_prev_attribute = where->m_prev_attribute; 1250 | attribute->m_next_attribute = where; 1251 | where->m_prev_attribute->m_next_attribute = attribute; 1252 | where->m_prev_attribute = attribute; 1253 | attribute->m_parent = this; 1254 | } 1255 | } 1256 | 1257 | //! Removes first attribute of the node. 1258 | //! If node has no attributes, behaviour is undefined. 1259 | //! Use first_attribute() to test if node has attributes. 1260 | void remove_first_attribute() 1261 | { 1262 | assert(first_attribute()); 1263 | xml_attribute *attribute = m_first_attribute; 1264 | if (attribute->m_next_attribute) 1265 | { 1266 | attribute->m_next_attribute->m_prev_attribute = 0; 1267 | } 1268 | else 1269 | m_last_attribute = 0; 1270 | attribute->m_parent = 0; 1271 | m_first_attribute = attribute->m_next_attribute; 1272 | } 1273 | 1274 | //! Removes last attribute of the node. 1275 | //! If node has no attributes, behaviour is undefined. 1276 | //! Use first_attribute() to test if node has attributes. 1277 | void remove_last_attribute() 1278 | { 1279 | assert(first_attribute()); 1280 | xml_attribute *attribute = m_last_attribute; 1281 | if (attribute->m_prev_attribute) 1282 | { 1283 | attribute->m_prev_attribute->m_next_attribute = 0; 1284 | m_last_attribute = attribute->m_prev_attribute; 1285 | } 1286 | else 1287 | m_first_attribute = 0; 1288 | attribute->m_parent = 0; 1289 | } 1290 | 1291 | //! Removes specified attribute from node. 1292 | //! \param where Pointer to attribute to be removed. 1293 | void remove_attribute(xml_attribute *where) 1294 | { 1295 | assert(first_attribute() && where->parent() == this); 1296 | if (where == m_first_attribute) 1297 | remove_first_attribute(); 1298 | else if (where == m_last_attribute) 1299 | remove_last_attribute(); 1300 | else 1301 | { 1302 | where->m_prev_attribute->m_next_attribute = where->m_next_attribute; 1303 | where->m_next_attribute->m_prev_attribute = where->m_prev_attribute; 1304 | where->m_parent = 0; 1305 | } 1306 | } 1307 | 1308 | //! Removes all attributes of node. 1309 | void remove_all_attributes() 1310 | { 1311 | for (xml_attribute *attribute = first_attribute(); attribute; attribute = attribute->m_next_attribute) 1312 | attribute->m_parent = 0; 1313 | m_first_attribute = 0; 1314 | } 1315 | 1316 | private: 1317 | 1318 | /////////////////////////////////////////////////////////////////////////// 1319 | // Restrictions 1320 | 1321 | // No copying 1322 | xml_node(const xml_node &); 1323 | void operator =(const xml_node &); 1324 | 1325 | /////////////////////////////////////////////////////////////////////////// 1326 | // Data members 1327 | 1328 | // Note that some of the pointers below have UNDEFINED values if certain other pointers are 0. 1329 | // This is required for maximum performance, as it allows the parser to omit initialization of 1330 | // unneded/redundant values. 1331 | // 1332 | // The rules are as follows: 1333 | // 1. first_node and first_attribute contain valid pointers, or 0 if node has no children/attributes respectively 1334 | // 2. last_node and last_attribute are valid only if node has at least one child/attribute respectively, otherwise they contain garbage 1335 | // 3. prev_sibling and next_sibling are valid only if node has a parent, otherwise they contain garbage 1336 | 1337 | node_type m_type; // Type of node; always valid 1338 | xml_node *m_first_node; // Pointer to first child node, or 0 if none; always valid 1339 | xml_node *m_last_node; // Pointer to last child node, or 0 if none; this value is only valid if m_first_node is non-zero 1340 | xml_attribute *m_first_attribute; // Pointer to first attribute of node, or 0 if none; always valid 1341 | xml_attribute *m_last_attribute; // Pointer to last attribute of node, or 0 if none; this value is only valid if m_first_attribute is non-zero 1342 | xml_node *m_prev_sibling; // Pointer to previous sibling of node, or 0 if none; this value is only valid if m_parent is non-zero 1343 | xml_node *m_next_sibling; // Pointer to next sibling of node, or 0 if none; this value is only valid if m_parent is non-zero 1344 | 1345 | }; 1346 | 1347 | /////////////////////////////////////////////////////////////////////////// 1348 | // XML document 1349 | 1350 | //! This class represents root of the DOM hierarchy. 1351 | //! It is also an xml_node and a memory_pool through public inheritance. 1352 | //! Use parse() function to build a DOM tree from a zero-terminated XML text string. 1353 | //! parse() function allocates memory for nodes and attributes by using functions of xml_document, 1354 | //! which are inherited from memory_pool. 1355 | //! To access root node of the document, use the document itself, as if it was an xml_node. 1356 | //! \param Ch Character type to use. 1357 | template 1358 | class xml_document: public xml_node, public memory_pool 1359 | { 1360 | 1361 | public: 1362 | 1363 | //! Constructs empty XML document 1364 | xml_document() 1365 | : xml_node(node_document) 1366 | { 1367 | } 1368 | 1369 | //! Parses zero-terminated XML string according to given flags. 1370 | //! Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used. 1371 | //! The string must persist for the lifetime of the document. 1372 | //! In case of error, rapidxml::parse_error exception will be thrown. 1373 | //!

1374 | //! If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning. 1375 | //! Make sure that data is zero-terminated. 1376 | //!

1377 | //! Document can be parsed into multiple times. 1378 | //! Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool. 1379 | //! \param text XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser. 1380 | template 1381 | void parse(Ch *text) 1382 | { 1383 | assert(text); 1384 | 1385 | // Remove current contents 1386 | this->remove_all_nodes(); 1387 | this->remove_all_attributes(); 1388 | 1389 | // Parse BOM, if any 1390 | parse_bom(text); 1391 | 1392 | // Parse children 1393 | while (1) 1394 | { 1395 | // Skip whitespace before node 1396 | skip(text); 1397 | if (*text == 0) 1398 | break; 1399 | 1400 | // Parse and append new child 1401 | if (*text == Ch('<')) 1402 | { 1403 | ++text; // Skip '<' 1404 | if (xml_node *node = parse_node(text)) 1405 | this->append_node(node); 1406 | } 1407 | else 1408 | RAPIDXML_PARSE_ERROR("expected <", text); 1409 | } 1410 | 1411 | } 1412 | 1413 | //! Clears the document by deleting all nodes and clearing the memory pool. 1414 | //! All nodes owned by document pool are destroyed. 1415 | void clear() 1416 | { 1417 | this->remove_all_nodes(); 1418 | this->remove_all_attributes(); 1419 | memory_pool::clear(); 1420 | } 1421 | 1422 | private: 1423 | 1424 | /////////////////////////////////////////////////////////////////////// 1425 | // Internal character utility functions 1426 | 1427 | // Detect whitespace character 1428 | struct whitespace_pred 1429 | { 1430 | static unsigned char test(Ch ch) 1431 | { 1432 | return internal::lookup_tables<0>::lookup_whitespace[static_cast(ch)]; 1433 | } 1434 | }; 1435 | 1436 | // Detect node name character 1437 | struct node_name_pred 1438 | { 1439 | static unsigned char test(Ch ch) 1440 | { 1441 | return internal::lookup_tables<0>::lookup_node_name[static_cast(ch)]; 1442 | } 1443 | }; 1444 | 1445 | // Detect attribute name character 1446 | struct attribute_name_pred 1447 | { 1448 | static unsigned char test(Ch ch) 1449 | { 1450 | return internal::lookup_tables<0>::lookup_attribute_name[static_cast(ch)]; 1451 | } 1452 | }; 1453 | 1454 | // Detect text character (PCDATA) 1455 | struct text_pred 1456 | { 1457 | static unsigned char test(Ch ch) 1458 | { 1459 | return internal::lookup_tables<0>::lookup_text[static_cast(ch)]; 1460 | } 1461 | }; 1462 | 1463 | // Detect text character (PCDATA) that does not require processing 1464 | struct text_pure_no_ws_pred 1465 | { 1466 | static unsigned char test(Ch ch) 1467 | { 1468 | return internal::lookup_tables<0>::lookup_text_pure_no_ws[static_cast(ch)]; 1469 | } 1470 | }; 1471 | 1472 | // Detect text character (PCDATA) that does not require processing 1473 | struct text_pure_with_ws_pred 1474 | { 1475 | static unsigned char test(Ch ch) 1476 | { 1477 | return internal::lookup_tables<0>::lookup_text_pure_with_ws[static_cast(ch)]; 1478 | } 1479 | }; 1480 | 1481 | // Detect attribute value character 1482 | template 1483 | struct attribute_value_pred 1484 | { 1485 | static unsigned char test(Ch ch) 1486 | { 1487 | if (Quote == Ch('\'')) 1488 | return internal::lookup_tables<0>::lookup_attribute_data_1[static_cast(ch)]; 1489 | if (Quote == Ch('\"')) 1490 | return internal::lookup_tables<0>::lookup_attribute_data_2[static_cast(ch)]; 1491 | return 0; // Should never be executed, to avoid warnings on Comeau 1492 | } 1493 | }; 1494 | 1495 | // Detect attribute value character 1496 | template 1497 | struct attribute_value_pure_pred 1498 | { 1499 | static unsigned char test(Ch ch) 1500 | { 1501 | if (Quote == Ch('\'')) 1502 | return internal::lookup_tables<0>::lookup_attribute_data_1_pure[static_cast(ch)]; 1503 | if (Quote == Ch('\"')) 1504 | return internal::lookup_tables<0>::lookup_attribute_data_2_pure[static_cast(ch)]; 1505 | return 0; // Should never be executed, to avoid warnings on Comeau 1506 | } 1507 | }; 1508 | 1509 | // Insert coded character, using UTF8 or 8-bit ASCII 1510 | template 1511 | static void insert_coded_character(Ch *&text, unsigned long code) 1512 | { 1513 | if (Flags & parse_no_utf8) 1514 | { 1515 | // Insert 8-bit ASCII character 1516 | // Todo: possibly verify that code is less than 256 and use replacement char otherwise? 1517 | text[0] = static_cast(code); 1518 | text += 1; 1519 | } 1520 | else 1521 | { 1522 | // Insert UTF8 sequence 1523 | if (code < 0x80) // 1 byte sequence 1524 | { 1525 | text[0] = static_cast(code); 1526 | text += 1; 1527 | } 1528 | else if (code < 0x800) // 2 byte sequence 1529 | { 1530 | text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; 1531 | text[0] = static_cast(code | 0xC0); 1532 | text += 2; 1533 | } 1534 | else if (code < 0x10000) // 3 byte sequence 1535 | { 1536 | text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; 1537 | text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; 1538 | text[0] = static_cast(code | 0xE0); 1539 | text += 3; 1540 | } 1541 | else if (code < 0x110000) // 4 byte sequence 1542 | { 1543 | text[3] = static_cast((code | 0x80) & 0xBF); code >>= 6; 1544 | text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; 1545 | text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; 1546 | text[0] = static_cast(code | 0xF0); 1547 | text += 4; 1548 | } 1549 | else // Invalid, only codes up to 0x10FFFF are allowed in Unicode 1550 | { 1551 | RAPIDXML_PARSE_ERROR("invalid numeric character entity", text); 1552 | } 1553 | } 1554 | } 1555 | 1556 | // Skip characters until predicate evaluates to true 1557 | template 1558 | static void skip(Ch *&text) 1559 | { 1560 | Ch *tmp = text; 1561 | while (StopPred::test(*tmp)) 1562 | ++tmp; 1563 | text = tmp; 1564 | } 1565 | 1566 | // Skip characters until predicate evaluates to true while doing the following: 1567 | // - replacing XML character entity references with proper characters (' & " < > &#...;) 1568 | // - condensing whitespace sequences to single space character 1569 | template 1570 | static Ch *skip_and_expand_character_refs(Ch *&text) 1571 | { 1572 | // If entity translation, whitespace condense and whitespace trimming is disabled, use plain skip 1573 | if (Flags & parse_no_entity_translation && 1574 | !(Flags & parse_normalize_whitespace) && 1575 | !(Flags & parse_trim_whitespace)) 1576 | { 1577 | skip(text); 1578 | return text; 1579 | } 1580 | 1581 | // Use simple skip until first modification is detected 1582 | skip(text); 1583 | 1584 | // Use translation skip 1585 | Ch *src = text; 1586 | Ch *dest = src; 1587 | while (StopPred::test(*src)) 1588 | { 1589 | // If entity translation is enabled 1590 | if (!(Flags & parse_no_entity_translation)) 1591 | { 1592 | // Test if replacement is needed 1593 | if (src[0] == Ch('&')) 1594 | { 1595 | switch (src[1]) 1596 | { 1597 | 1598 | // & ' 1599 | case Ch('a'): 1600 | if (src[2] == Ch('m') && src[3] == Ch('p') && src[4] == Ch(';')) 1601 | { 1602 | *dest = Ch('&'); 1603 | ++dest; 1604 | src += 5; 1605 | continue; 1606 | } 1607 | if (src[2] == Ch('p') && src[3] == Ch('o') && src[4] == Ch('s') && src[5] == Ch(';')) 1608 | { 1609 | *dest = Ch('\''); 1610 | ++dest; 1611 | src += 6; 1612 | continue; 1613 | } 1614 | break; 1615 | 1616 | // " 1617 | case Ch('q'): 1618 | if (src[2] == Ch('u') && src[3] == Ch('o') && src[4] == Ch('t') && src[5] == Ch(';')) 1619 | { 1620 | *dest = Ch('"'); 1621 | ++dest; 1622 | src += 6; 1623 | continue; 1624 | } 1625 | break; 1626 | 1627 | // > 1628 | case Ch('g'): 1629 | if (src[2] == Ch('t') && src[3] == Ch(';')) 1630 | { 1631 | *dest = Ch('>'); 1632 | ++dest; 1633 | src += 4; 1634 | continue; 1635 | } 1636 | break; 1637 | 1638 | // < 1639 | case Ch('l'): 1640 | if (src[2] == Ch('t') && src[3] == Ch(';')) 1641 | { 1642 | *dest = Ch('<'); 1643 | ++dest; 1644 | src += 4; 1645 | continue; 1646 | } 1647 | break; 1648 | 1649 | // &#...; - assumes ASCII 1650 | case Ch('#'): 1651 | if (src[2] == Ch('x')) 1652 | { 1653 | unsigned long code = 0; 1654 | src += 3; // Skip &#x 1655 | while (1) 1656 | { 1657 | unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; 1658 | if (digit == 0xFF) 1659 | break; 1660 | code = code * 16 + digit; 1661 | ++src; 1662 | } 1663 | insert_coded_character(dest, code); // Put character in output 1664 | } 1665 | else 1666 | { 1667 | unsigned long code = 0; 1668 | src += 2; // Skip &# 1669 | while (1) 1670 | { 1671 | unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; 1672 | if (digit == 0xFF) 1673 | break; 1674 | code = code * 10 + digit; 1675 | ++src; 1676 | } 1677 | insert_coded_character(dest, code); // Put character in output 1678 | } 1679 | if (*src == Ch(';')) 1680 | ++src; 1681 | else 1682 | RAPIDXML_PARSE_ERROR("expected ;", src); 1683 | continue; 1684 | 1685 | // Something else 1686 | default: 1687 | // Ignore, just copy '&' verbatim 1688 | break; 1689 | 1690 | } 1691 | } 1692 | } 1693 | 1694 | // If whitespace condensing is enabled 1695 | if (Flags & parse_normalize_whitespace) 1696 | { 1697 | // Test if condensing is needed 1698 | if (whitespace_pred::test(*src)) 1699 | { 1700 | *dest = Ch(' '); ++dest; // Put single space in dest 1701 | ++src; // Skip first whitespace char 1702 | // Skip remaining whitespace chars 1703 | while (whitespace_pred::test(*src)) 1704 | ++src; 1705 | continue; 1706 | } 1707 | } 1708 | 1709 | // No replacement, only copy character 1710 | *dest++ = *src++; 1711 | 1712 | } 1713 | 1714 | // Return new end 1715 | text = src; 1716 | return dest; 1717 | 1718 | } 1719 | 1720 | /////////////////////////////////////////////////////////////////////// 1721 | // Internal parsing functions 1722 | 1723 | // Parse BOM, if any 1724 | template 1725 | void parse_bom(Ch *&text) 1726 | { 1727 | // UTF-8? 1728 | if (static_cast(text[0]) == 0xEF && 1729 | static_cast(text[1]) == 0xBB && 1730 | static_cast(text[2]) == 0xBF) 1731 | { 1732 | text += 3; // Skup utf-8 bom 1733 | } 1734 | } 1735 | 1736 | // Parse XML declaration ( 1738 | xml_node *parse_xml_declaration(Ch *&text) 1739 | { 1740 | // If parsing of declaration is disabled 1741 | if (!(Flags & parse_declaration_node)) 1742 | { 1743 | // Skip until end of declaration 1744 | while (text[0] != Ch('?') || text[1] != Ch('>')) 1745 | { 1746 | if (!text[0]) 1747 | RAPIDXML_PARSE_ERROR("unexpected end of data", text); 1748 | ++text; 1749 | } 1750 | text += 2; // Skip '?>' 1751 | return 0; 1752 | } 1753 | 1754 | // Create declaration 1755 | xml_node *declaration = this->allocate_node(node_declaration); 1756 | 1757 | // Skip whitespace before attributes or ?> 1758 | skip(text); 1759 | 1760 | // Parse declaration attributes 1761 | parse_node_attributes(text, declaration); 1762 | 1763 | // Skip ?> 1764 | if (text[0] != Ch('?') || text[1] != Ch('>')) 1765 | RAPIDXML_PARSE_ERROR("expected ?>", text); 1766 | text += 2; 1767 | 1768 | return declaration; 1769 | } 1770 | 1771 | // Parse XML comment (' 1786 | return 0; // Do not produce comment node 1787 | } 1788 | 1789 | // Remember value start 1790 | Ch *value = text; 1791 | 1792 | // Skip until end of comment 1793 | while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>')) 1794 | { 1795 | if (!text[0]) 1796 | RAPIDXML_PARSE_ERROR("unexpected end of data", text); 1797 | ++text; 1798 | } 1799 | 1800 | // Create comment node 1801 | xml_node *comment = this->allocate_node(node_comment); 1802 | comment->value(value, text - value); 1803 | 1804 | // Place zero terminator after comment value 1805 | if (!(Flags & parse_no_string_terminators)) 1806 | *text = Ch('\0'); 1807 | 1808 | text += 3; // Skip '-->' 1809 | return comment; 1810 | } 1811 | 1812 | // Parse DOCTYPE 1813 | template 1814 | xml_node *parse_doctype(Ch *&text) 1815 | { 1816 | // Remember value start 1817 | Ch *value = text; 1818 | 1819 | // Skip to > 1820 | while (*text != Ch('>')) 1821 | { 1822 | // Determine character type 1823 | switch (*text) 1824 | { 1825 | 1826 | // If '[' encountered, scan for matching ending ']' using naive algorithm with depth 1827 | // This works for all W3C test files except for 2 most wicked 1828 | case Ch('['): 1829 | { 1830 | ++text; // Skip '[' 1831 | int depth = 1; 1832 | while (depth > 0) 1833 | { 1834 | switch (*text) 1835 | { 1836 | case Ch('['): ++depth; break; 1837 | case Ch(']'): --depth; break; 1838 | case 0: RAPIDXML_PARSE_ERROR("unexpected end of data", text); 1839 | } 1840 | ++text; 1841 | } 1842 | break; 1843 | } 1844 | 1845 | // Error on end of text 1846 | case Ch('\0'): 1847 | RAPIDXML_PARSE_ERROR("unexpected end of data", text); 1848 | 1849 | // Other character, skip it 1850 | default: 1851 | ++text; 1852 | 1853 | } 1854 | } 1855 | 1856 | // If DOCTYPE nodes enabled 1857 | if (Flags & parse_doctype_node) 1858 | { 1859 | // Create a new doctype node 1860 | xml_node *doctype = this->allocate_node(node_doctype); 1861 | doctype->value(value, text - value); 1862 | 1863 | // Place zero terminator after value 1864 | if (!(Flags & parse_no_string_terminators)) 1865 | *text = Ch('\0'); 1866 | 1867 | text += 1; // skip '>' 1868 | return doctype; 1869 | } 1870 | else 1871 | { 1872 | text += 1; // skip '>' 1873 | return 0; 1874 | } 1875 | 1876 | } 1877 | 1878 | // Parse PI 1879 | template 1880 | xml_node *parse_pi(Ch *&text) 1881 | { 1882 | // If creation of PI nodes is enabled 1883 | if (Flags & parse_pi_nodes) 1884 | { 1885 | // Create pi node 1886 | xml_node *pi = this->allocate_node(node_pi); 1887 | 1888 | // Extract PI target name 1889 | Ch *name = text; 1890 | skip(text); 1891 | if (text == name) 1892 | RAPIDXML_PARSE_ERROR("expected PI target", text); 1893 | pi->name(name, text - name); 1894 | 1895 | // Skip whitespace between pi target and pi 1896 | skip(text); 1897 | 1898 | // Remember start of pi 1899 | Ch *value = text; 1900 | 1901 | // Skip to '?>' 1902 | while (text[0] != Ch('?') || text[1] != Ch('>')) 1903 | { 1904 | if (*text == Ch('\0')) 1905 | RAPIDXML_PARSE_ERROR("unexpected end of data", text); 1906 | ++text; 1907 | } 1908 | 1909 | // Set pi value (verbatim, no entity expansion or whitespace normalization) 1910 | pi->value(value, text - value); 1911 | 1912 | // Place zero terminator after name and value 1913 | if (!(Flags & parse_no_string_terminators)) 1914 | { 1915 | pi->name()[pi->name_size()] = Ch('\0'); 1916 | pi->value()[pi->value_size()] = Ch('\0'); 1917 | } 1918 | 1919 | text += 2; // Skip '?>' 1920 | return pi; 1921 | } 1922 | else 1923 | { 1924 | // Skip to '?>' 1925 | while (text[0] != Ch('?') || text[1] != Ch('>')) 1926 | { 1927 | if (*text == Ch('\0')) 1928 | RAPIDXML_PARSE_ERROR("unexpected end of data", text); 1929 | ++text; 1930 | } 1931 | text += 2; // Skip '?>' 1932 | return 0; 1933 | } 1934 | } 1935 | 1936 | // Parse and append data 1937 | // Return character that ends data. 1938 | // This is necessary because this character might have been overwritten by a terminating 0 1939 | template 1940 | Ch parse_and_append_data(xml_node *node, Ch *&text, Ch *contents_start) 1941 | { 1942 | // Backup to contents start if whitespace trimming is disabled 1943 | if (!(Flags & parse_trim_whitespace)) 1944 | text = contents_start; 1945 | 1946 | // Skip until end of data 1947 | Ch *value = text, *end; 1948 | if (Flags & parse_normalize_whitespace) 1949 | end = skip_and_expand_character_refs(text); 1950 | else 1951 | end = skip_and_expand_character_refs(text); 1952 | 1953 | // Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after > 1954 | if (Flags & parse_trim_whitespace) 1955 | { 1956 | if (Flags & parse_normalize_whitespace) 1957 | { 1958 | // Whitespace is already condensed to single space characters by skipping function, so just trim 1 char off the end 1959 | if (*(end - 1) == Ch(' ')) 1960 | --end; 1961 | } 1962 | else 1963 | { 1964 | // Backup until non-whitespace character is found 1965 | while (whitespace_pred::test(*(end - 1))) 1966 | --end; 1967 | } 1968 | } 1969 | 1970 | // If characters are still left between end and value (this test is only necessary if normalization is enabled) 1971 | // Create new data node 1972 | if (!(Flags & parse_no_data_nodes)) 1973 | { 1974 | xml_node *data = this->allocate_node(node_data); 1975 | data->value(value, end - value); 1976 | node->append_node(data); 1977 | } 1978 | 1979 | // Add data to parent node if no data exists yet 1980 | if (!(Flags & parse_no_element_values)) 1981 | if (*node->value() == Ch('\0')) 1982 | node->value(value, end - value); 1983 | 1984 | // Place zero terminator after value 1985 | if (!(Flags & parse_no_string_terminators)) 1986 | { 1987 | Ch ch = *text; 1988 | *end = Ch('\0'); 1989 | return ch; // Return character that ends data; this is required because zero terminator overwritten it 1990 | } 1991 | 1992 | // Return character that ends data 1993 | return *text; 1994 | } 1995 | 1996 | // Parse CDATA 1997 | template 1998 | xml_node *parse_cdata(Ch *&text) 1999 | { 2000 | // If CDATA is disabled 2001 | if (Flags & parse_no_data_nodes) 2002 | { 2003 | // Skip until end of cdata 2004 | while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) 2005 | { 2006 | if (!text[0]) 2007 | RAPIDXML_PARSE_ERROR("unexpected end of data", text); 2008 | ++text; 2009 | } 2010 | text += 3; // Skip ]]> 2011 | return 0; // Do not produce CDATA node 2012 | } 2013 | 2014 | // Skip until end of cdata 2015 | Ch *value = text; 2016 | while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) 2017 | { 2018 | if (!text[0]) 2019 | RAPIDXML_PARSE_ERROR("unexpected end of data", text); 2020 | ++text; 2021 | } 2022 | 2023 | // Create new cdata node 2024 | xml_node *cdata = this->allocate_node(node_cdata); 2025 | cdata->value(value, text - value); 2026 | 2027 | // Place zero terminator after value 2028 | if (!(Flags & parse_no_string_terminators)) 2029 | *text = Ch('\0'); 2030 | 2031 | text += 3; // Skip ]]> 2032 | return cdata; 2033 | } 2034 | 2035 | // Parse element node 2036 | template 2037 | xml_node *parse_element(Ch *&text) 2038 | { 2039 | // Create element node 2040 | xml_node *element = this->allocate_node(node_element); 2041 | 2042 | // Extract element name 2043 | Ch *name = text; 2044 | skip(text); 2045 | if (text == name) 2046 | RAPIDXML_PARSE_ERROR("expected element name", text); 2047 | element->name(name, text - name); 2048 | 2049 | // Skip whitespace between element name and attributes or > 2050 | skip(text); 2051 | 2052 | // Parse attributes, if any 2053 | parse_node_attributes(text, element); 2054 | 2055 | // Determine ending type 2056 | if (*text == Ch('>')) 2057 | { 2058 | ++text; 2059 | parse_node_contents(text, element); 2060 | } 2061 | else if (*text == Ch('/')) 2062 | { 2063 | ++text; 2064 | if (*text != Ch('>')) 2065 | RAPIDXML_PARSE_ERROR("expected >", text); 2066 | ++text; 2067 | } 2068 | else 2069 | RAPIDXML_PARSE_ERROR("expected >", text); 2070 | 2071 | // Place zero terminator after name 2072 | if (!(Flags & parse_no_string_terminators)) 2073 | element->name()[element->name_size()] = Ch('\0'); 2074 | 2075 | // Return parsed element 2076 | return element; 2077 | } 2078 | 2079 | // Determine node type, and parse it 2080 | template 2081 | xml_node *parse_node(Ch *&text) 2082 | { 2083 | // Parse proper node type 2084 | switch (text[0]) 2085 | { 2086 | 2087 | // <... 2088 | default: 2089 | // Parse and append element node 2090 | return parse_element(text); 2091 | 2092 | // (text); 2103 | } 2104 | else 2105 | { 2106 | // Parse PI 2107 | return parse_pi(text); 2108 | } 2109 | 2110 | // (text); 2124 | } 2125 | break; 2126 | 2127 | // (text); 2135 | } 2136 | break; 2137 | 2138 | // (text); 2147 | } 2148 | 2149 | } // switch 2150 | 2151 | // Attempt to skip other, unrecognized node types starting with ')) 2154 | { 2155 | if (*text == 0) 2156 | RAPIDXML_PARSE_ERROR("unexpected end of data", text); 2157 | ++text; 2158 | } 2159 | ++text; // Skip '>' 2160 | return 0; // No node recognized 2161 | 2162 | } 2163 | } 2164 | 2165 | // Parse contents of the node - children, data etc. 2166 | template 2167 | void parse_node_contents(Ch *&text, xml_node *node) 2168 | { 2169 | // For all children and text 2170 | while (1) 2171 | { 2172 | // Skip whitespace between > and node contents 2173 | Ch *contents_start = text; // Store start of node contents before whitespace is skipped 2174 | skip(text); 2175 | Ch next_char = *text; 2176 | 2177 | // After data nodes, instead of continuing the loop, control jumps here. 2178 | // This is because zero termination inside parse_and_append_data() function 2179 | // would wreak havoc with the above code. 2180 | // Also, skipping whitespace after data nodes is unnecessary. 2181 | after_data_node: 2182 | 2183 | // Determine what comes next: node closing, child node, data node, or 0? 2184 | switch (next_char) 2185 | { 2186 | 2187 | // Node closing or child node 2188 | case Ch('<'): 2189 | if (text[1] == Ch('/')) 2190 | { 2191 | // Node closing 2192 | text += 2; // Skip '(text); 2198 | if (!internal::compare(node->name(), node->name_size(), closing_name, text - closing_name, true)) 2199 | RAPIDXML_PARSE_ERROR("invalid closing tag name", text); 2200 | } 2201 | else 2202 | { 2203 | // No validation, just skip name 2204 | skip(text); 2205 | } 2206 | // Skip remaining whitespace after node name 2207 | skip(text); 2208 | if (*text != Ch('>')) 2209 | RAPIDXML_PARSE_ERROR("expected >", text); 2210 | ++text; // Skip '>' 2211 | return; // Node closed, finished parsing contents 2212 | } 2213 | else 2214 | { 2215 | // Child node 2216 | ++text; // Skip '<' 2217 | if (xml_node *child = parse_node(text)) 2218 | node->append_node(child); 2219 | } 2220 | break; 2221 | 2222 | // End of data - error 2223 | case Ch('\0'): 2224 | RAPIDXML_PARSE_ERROR("unexpected end of data", text); 2225 | 2226 | // Data node 2227 | default: 2228 | next_char = parse_and_append_data(node, text, contents_start); 2229 | goto after_data_node; // Bypass regular processing after data nodes 2230 | 2231 | } 2232 | } 2233 | } 2234 | 2235 | // Parse XML attributes of the node 2236 | template 2237 | void parse_node_attributes(Ch *&text, xml_node *node) 2238 | { 2239 | // For all attributes 2240 | while (attribute_name_pred::test(*text)) 2241 | { 2242 | // Extract attribute name 2243 | Ch *name = text; 2244 | ++text; // Skip first character of attribute name 2245 | skip(text); 2246 | if (text == name) 2247 | RAPIDXML_PARSE_ERROR("expected attribute name", name); 2248 | 2249 | // Create new attribute 2250 | xml_attribute *attribute = this->allocate_attribute(); 2251 | attribute->name(name, text - name); 2252 | node->append_attribute(attribute); 2253 | 2254 | // Skip whitespace after attribute name 2255 | skip(text); 2256 | 2257 | // Skip = 2258 | if (*text != Ch('=')) 2259 | RAPIDXML_PARSE_ERROR("expected =", text); 2260 | ++text; 2261 | 2262 | // Add terminating zero after name 2263 | if (!(Flags & parse_no_string_terminators)) 2264 | attribute->name()[attribute->name_size()] = 0; 2265 | 2266 | // Skip whitespace after = 2267 | skip(text); 2268 | 2269 | // Skip quote and remember if it was ' or " 2270 | Ch quote = *text; 2271 | if (quote != Ch('\'') && quote != Ch('"')) 2272 | RAPIDXML_PARSE_ERROR("expected ' or \"", text); 2273 | ++text; 2274 | 2275 | // Extract attribute value and expand char refs in it 2276 | Ch *value = text, *end; 2277 | const int AttFlags = Flags & ~parse_normalize_whitespace; // No whitespace normalization in attributes 2278 | if (quote == Ch('\'')) 2279 | end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); 2280 | else 2281 | end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); 2282 | 2283 | // Set attribute value 2284 | attribute->value(value, end - value); 2285 | 2286 | // Make sure that end quote is present 2287 | if (*text != quote) 2288 | RAPIDXML_PARSE_ERROR("expected ' or \"", text); 2289 | ++text; // Skip quote 2290 | 2291 | // Add terminating zero after value 2292 | if (!(Flags & parse_no_string_terminators)) 2293 | attribute->value()[attribute->value_size()] = 0; 2294 | 2295 | // Skip whitespace after attribute value 2296 | skip(text); 2297 | } 2298 | } 2299 | 2300 | }; 2301 | 2302 | //! \cond internal 2303 | namespace internal 2304 | { 2305 | 2306 | // Whitespace (space \n \r \t) 2307 | template 2308 | const unsigned char lookup_tables::lookup_whitespace[256] = 2309 | { 2310 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 2311 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, // 0 2312 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 2313 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 2314 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 2315 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 2316 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 2317 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 2318 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 2319 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 2320 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 2321 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 2322 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 2323 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 2324 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 2325 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 2326 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F 2327 | }; 2328 | 2329 | // Node name (anything but space \n \r \t / > ? \0) 2330 | template 2331 | const unsigned char lookup_tables::lookup_node_name[256] = 2332 | { 2333 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 2334 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 2335 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 2336 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 2337 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, // 3 2338 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 2339 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 2340 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 2341 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 2342 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 2343 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 2344 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 2345 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 2346 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 2347 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 2348 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 2349 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F 2350 | }; 2351 | 2352 | // Text (i.e. PCDATA) (anything but < \0) 2353 | template 2354 | const unsigned char lookup_tables::lookup_text[256] = 2355 | { 2356 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 2357 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 2358 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 2359 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 2360 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 2361 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 2362 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 2363 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 2364 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 2365 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 2366 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 2367 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 2368 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 2369 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 2370 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 2371 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 2372 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F 2373 | }; 2374 | 2375 | // Text (i.e. PCDATA) that does not require processing when ws normalization is disabled 2376 | // (anything but < \0 &) 2377 | template 2378 | const unsigned char lookup_tables::lookup_text_pure_no_ws[256] = 2379 | { 2380 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 2381 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 2382 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 2383 | 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 2384 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 2385 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 2386 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 2387 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 2388 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 2389 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 2390 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 2391 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 2392 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 2393 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 2394 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 2395 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 2396 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F 2397 | }; 2398 | 2399 | // Text (i.e. PCDATA) that does not require processing when ws normalizationis is enabled 2400 | // (anything but < \0 & space \n \r \t) 2401 | template 2402 | const unsigned char lookup_tables::lookup_text_pure_with_ws[256] = 2403 | { 2404 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 2405 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 2406 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 2407 | 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 2408 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 2409 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 2410 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 2411 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 2412 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 2413 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 2414 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 2415 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 2416 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 2417 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 2418 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 2419 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 2420 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F 2421 | }; 2422 | 2423 | // Attribute name (anything but space \n \r \t / < > = ? ! \0) 2424 | template 2425 | const unsigned char lookup_tables::lookup_attribute_name[256] = 2426 | { 2427 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 2428 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 2429 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 2430 | 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 2431 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 3 2432 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 2433 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 2434 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 2435 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 2436 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 2437 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 2438 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 2439 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 2440 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 2441 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 2442 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 2443 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F 2444 | }; 2445 | 2446 | // Attribute data with single quote (anything but ' \0) 2447 | template 2448 | const unsigned char lookup_tables::lookup_attribute_data_1[256] = 2449 | { 2450 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 2451 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 2452 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 2453 | 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 2454 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 2455 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 2456 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 2457 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 2458 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 2459 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 2460 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 2461 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 2462 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 2463 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 2464 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 2465 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 2466 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F 2467 | }; 2468 | 2469 | // Attribute data with single quote that does not require processing (anything but ' \0 &) 2470 | template 2471 | const unsigned char lookup_tables::lookup_attribute_data_1_pure[256] = 2472 | { 2473 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 2474 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 2475 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 2476 | 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 2477 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 2478 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 2479 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 2480 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 2481 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 2482 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 2483 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 2484 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 2485 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 2486 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 2487 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 2488 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 2489 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F 2490 | }; 2491 | 2492 | // Attribute data with double quote (anything but " \0) 2493 | template 2494 | const unsigned char lookup_tables::lookup_attribute_data_2[256] = 2495 | { 2496 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 2497 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 2498 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 2499 | 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 2500 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 2501 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 2502 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 2503 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 2504 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 2505 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 2506 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 2507 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 2508 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 2509 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 2510 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 2511 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 2512 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F 2513 | }; 2514 | 2515 | // Attribute data with double quote that does not require processing (anything but " \0 &) 2516 | template 2517 | const unsigned char lookup_tables::lookup_attribute_data_2_pure[256] = 2518 | { 2519 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 2520 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 2521 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 2522 | 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 2523 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 2524 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 2525 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 2526 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 2527 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 2528 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 2529 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 2530 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 2531 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 2532 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 2533 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 2534 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 2535 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F 2536 | }; 2537 | 2538 | // Digits (dec and hex, 255 denotes end of numeric character reference) 2539 | template 2540 | const unsigned char lookup_tables::lookup_digits[256] = 2541 | { 2542 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 2543 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 0 2544 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 1 2545 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 2 2546 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,255,255,255,255,255,255, // 3 2547 | 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 4 2548 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 5 2549 | 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 6 2550 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 7 2551 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 8 2552 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 9 2553 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // A 2554 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // B 2555 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // C 2556 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // D 2557 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // E 2558 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 // F 2559 | }; 2560 | 2561 | // Upper case conversion 2562 | template 2563 | const unsigned char lookup_tables::lookup_upcase[256] = 2564 | { 2565 | // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A B C D E F 2566 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0 2567 | 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, // 1 2568 | 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 2 2569 | 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 3 2570 | 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 4 2571 | 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 5 2572 | 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 6 2573 | 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123,124,125,126,127, // 7 2574 | 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 8 2575 | 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 9 2576 | 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // A 2577 | 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // B 2578 | 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // C 2579 | 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // D 2580 | 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // E 2581 | 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // F 2582 | }; 2583 | } 2584 | //! \endcond 2585 | 2586 | } 2587 | 2588 | // Undefine internal macros 2589 | #undef RAPIDXML_PARSE_ERROR 2590 | 2591 | // On MSVC, restore warnings state 2592 | #ifdef _MSC_VER 2593 | #pragma warning(pop) 2594 | #endif 2595 | 2596 | #endif 2597 | -------------------------------------------------------------------------------- /rapidxml/rapidxml_iterators.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RAPIDXML_ITERATORS_HPP_INCLUDED 2 | #define RAPIDXML_ITERATORS_HPP_INCLUDED 3 | 4 | // Copyright (C) 2006, 2009 Marcin Kalicinski 5 | // Version 1.13 6 | // Revision $DateTime: 2009/05/13 01:46:17 $ 7 | //! \file rapidxml_iterators.hpp This file contains rapidxml iterators 8 | 9 | #include "rapidxml.hpp" 10 | 11 | namespace rapidxml 12 | { 13 | 14 | //! Iterator of child nodes of xml_node 15 | template 16 | class node_iterator 17 | { 18 | 19 | public: 20 | 21 | typedef typename xml_node value_type; 22 | typedef typename xml_node &reference; 23 | typedef typename xml_node *pointer; 24 | typedef std::ptrdiff_t difference_type; 25 | typedef std::bidirectional_iterator_tag iterator_category; 26 | 27 | node_iterator() 28 | : m_node(0) 29 | { 30 | } 31 | 32 | node_iterator(xml_node *node) 33 | : m_node(node->first_node()) 34 | { 35 | } 36 | 37 | reference operator *() const 38 | { 39 | assert(m_node); 40 | return *m_node; 41 | } 42 | 43 | pointer operator->() const 44 | { 45 | assert(m_node); 46 | return m_node; 47 | } 48 | 49 | node_iterator& operator++() 50 | { 51 | assert(m_node); 52 | m_node = m_node->next_sibling(); 53 | return *this; 54 | } 55 | 56 | node_iterator operator++(int) 57 | { 58 | node_iterator tmp = *this; 59 | ++this; 60 | return tmp; 61 | } 62 | 63 | node_iterator& operator--() 64 | { 65 | assert(m_node && m_node->previous_sibling()); 66 | m_node = m_node->previous_sibling(); 67 | return *this; 68 | } 69 | 70 | node_iterator operator--(int) 71 | { 72 | node_iterator tmp = *this; 73 | ++this; 74 | return tmp; 75 | } 76 | 77 | bool operator ==(const node_iterator &rhs) 78 | { 79 | return m_node == rhs.m_node; 80 | } 81 | 82 | bool operator !=(const node_iterator &rhs) 83 | { 84 | return m_node != rhs.m_node; 85 | } 86 | 87 | private: 88 | 89 | xml_node *m_node; 90 | 91 | }; 92 | 93 | //! Iterator of child attributes of xml_node 94 | template 95 | class attribute_iterator 96 | { 97 | 98 | public: 99 | 100 | typedef typename xml_attribute value_type; 101 | typedef typename xml_attribute &reference; 102 | typedef typename xml_attribute *pointer; 103 | typedef std::ptrdiff_t difference_type; 104 | typedef std::bidirectional_iterator_tag iterator_category; 105 | 106 | attribute_iterator() 107 | : m_attribute(0) 108 | { 109 | } 110 | 111 | attribute_iterator(xml_node *node) 112 | : m_attribute(node->first_attribute()) 113 | { 114 | } 115 | 116 | reference operator *() const 117 | { 118 | assert(m_attribute); 119 | return *m_attribute; 120 | } 121 | 122 | pointer operator->() const 123 | { 124 | assert(m_attribute); 125 | return m_attribute; 126 | } 127 | 128 | attribute_iterator& operator++() 129 | { 130 | assert(m_attribute); 131 | m_attribute = m_attribute->next_attribute(); 132 | return *this; 133 | } 134 | 135 | attribute_iterator operator++(int) 136 | { 137 | attribute_iterator tmp = *this; 138 | ++this; 139 | return tmp; 140 | } 141 | 142 | attribute_iterator& operator--() 143 | { 144 | assert(m_attribute && m_attribute->previous_attribute()); 145 | m_attribute = m_attribute->previous_attribute(); 146 | return *this; 147 | } 148 | 149 | attribute_iterator operator--(int) 150 | { 151 | attribute_iterator tmp = *this; 152 | ++this; 153 | return tmp; 154 | } 155 | 156 | bool operator ==(const attribute_iterator &rhs) 157 | { 158 | return m_attribute == rhs.m_attribute; 159 | } 160 | 161 | bool operator !=(const attribute_iterator &rhs) 162 | { 163 | return m_attribute != rhs.m_attribute; 164 | } 165 | 166 | private: 167 | 168 | xml_attribute *m_attribute; 169 | 170 | }; 171 | 172 | } 173 | 174 | #endif 175 | -------------------------------------------------------------------------------- /rapidxml/rapidxml_print.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RAPIDXML_PRINT_HPP_INCLUDED 2 | #define RAPIDXML_PRINT_HPP_INCLUDED 3 | 4 | // Copyright (C) 2006, 2009 Marcin Kalicinski 5 | // Version 1.13 6 | // Revision $DateTime: 2009/05/13 01:46:17 $ 7 | //! \file rapidxml_print.hpp This file contains rapidxml printer implementation 8 | 9 | #include "rapidxml.hpp" 10 | 11 | // Only include streams if not disabled 12 | #ifndef RAPIDXML_NO_STREAMS 13 | #include 14 | #include 15 | #endif 16 | 17 | namespace rapidxml 18 | { 19 | 20 | /////////////////////////////////////////////////////////////////////// 21 | // Printing flags 22 | 23 | const int print_no_indenting = 0x1; //!< Printer flag instructing the printer to suppress indenting of XML. See print() function. 24 | 25 | /////////////////////////////////////////////////////////////////////// 26 | // Internal 27 | 28 | //! \cond internal 29 | namespace internal 30 | { 31 | 32 | /////////////////////////////////////////////////////////////////////////// 33 | // Internal character operations 34 | 35 | // Copy characters from given range to given output iterator 36 | template 37 | inline OutIt copy_chars(const Ch *begin, const Ch *end, OutIt out) 38 | { 39 | while (begin != end) 40 | *out++ = *begin++; 41 | return out; 42 | } 43 | 44 | // Copy characters from given range to given output iterator and expand 45 | // characters into references (< > ' " &) 46 | template 47 | inline OutIt copy_and_expand_chars(const Ch *begin, const Ch *end, Ch noexpand, OutIt out) 48 | { 49 | while (begin != end) 50 | { 51 | if (*begin == noexpand) 52 | { 53 | *out++ = *begin; // No expansion, copy character 54 | } 55 | else 56 | { 57 | switch (*begin) 58 | { 59 | case Ch('<'): 60 | *out++ = Ch('&'); *out++ = Ch('l'); *out++ = Ch('t'); *out++ = Ch(';'); 61 | break; 62 | case Ch('>'): 63 | *out++ = Ch('&'); *out++ = Ch('g'); *out++ = Ch('t'); *out++ = Ch(';'); 64 | break; 65 | case Ch('\''): 66 | *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('p'); *out++ = Ch('o'); *out++ = Ch('s'); *out++ = Ch(';'); 67 | break; 68 | case Ch('"'): 69 | *out++ = Ch('&'); *out++ = Ch('q'); *out++ = Ch('u'); *out++ = Ch('o'); *out++ = Ch('t'); *out++ = Ch(';'); 70 | break; 71 | case Ch('&'): 72 | *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('m'); *out++ = Ch('p'); *out++ = Ch(';'); 73 | break; 74 | default: 75 | *out++ = *begin; // No expansion, copy character 76 | } 77 | } 78 | ++begin; // Step to next character 79 | } 80 | return out; 81 | } 82 | 83 | // Fill given output iterator with repetitions of the same character 84 | template 85 | inline OutIt fill_chars(OutIt out, int n, Ch ch) 86 | { 87 | for (int i = 0; i < n; ++i) 88 | *out++ = ch; 89 | return out; 90 | } 91 | 92 | // Find character 93 | template 94 | inline bool find_char(const Ch *begin, const Ch *end) 95 | { 96 | while (begin != end) 97 | if (*begin++ == ch) 98 | return true; 99 | return false; 100 | } 101 | 102 | /////////////////////////////////////////////////////////////////////////// 103 | // Internal printing operations 104 | 105 | // Print node 106 | template 107 | inline OutIt print_node(OutIt out, const xml_node *node, int flags, int indent) 108 | { 109 | // Print proper node type 110 | switch (node->type()) 111 | { 112 | 113 | // Document 114 | case node_document: 115 | out = print_children(out, node, flags, indent); 116 | break; 117 | 118 | // Element 119 | case node_element: 120 | out = print_element_node(out, node, flags, indent); 121 | break; 122 | 123 | // Data 124 | case node_data: 125 | out = print_data_node(out, node, flags, indent); 126 | break; 127 | 128 | // CDATA 129 | case node_cdata: 130 | out = print_cdata_node(out, node, flags, indent); 131 | break; 132 | 133 | // Declaration 134 | case node_declaration: 135 | out = print_declaration_node(out, node, flags, indent); 136 | break; 137 | 138 | // Comment 139 | case node_comment: 140 | out = print_comment_node(out, node, flags, indent); 141 | break; 142 | 143 | // Doctype 144 | case node_doctype: 145 | out = print_doctype_node(out, node, flags, indent); 146 | break; 147 | 148 | // Pi 149 | case node_pi: 150 | out = print_pi_node(out, node, flags, indent); 151 | break; 152 | 153 | // Unknown 154 | default: 155 | assert(0); 156 | break; 157 | } 158 | 159 | // If indenting not disabled, add line break after node 160 | if (!(flags & print_no_indenting)) 161 | *out = Ch('\n'), ++out; 162 | 163 | // Return modified iterator 164 | return out; 165 | } 166 | 167 | // Print children of the node 168 | template 169 | inline OutIt print_children(OutIt out, const xml_node *node, int flags, int indent) 170 | { 171 | for (xml_node *child = node->first_node(); child; child = child->next_sibling()) 172 | out = print_node(out, child, flags, indent); 173 | return out; 174 | } 175 | 176 | // Print attributes of the node 177 | template 178 | inline OutIt print_attributes(OutIt out, const xml_node *node, int flags) 179 | { 180 | for (xml_attribute *attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute()) 181 | { 182 | if (attribute->name() && attribute->value()) 183 | { 184 | // Print attribute name 185 | *out = Ch(' '), ++out; 186 | out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out); 187 | *out = Ch('='), ++out; 188 | // Print attribute value using appropriate quote type 189 | if (find_char(attribute->value(), attribute->value() + attribute->value_size())) 190 | { 191 | *out = Ch('\''), ++out; 192 | out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out); 193 | *out = Ch('\''), ++out; 194 | } 195 | else 196 | { 197 | *out = Ch('"'), ++out; 198 | out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out); 199 | *out = Ch('"'), ++out; 200 | } 201 | } 202 | } 203 | return out; 204 | } 205 | 206 | // Print data node 207 | template 208 | inline OutIt print_data_node(OutIt out, const xml_node *node, int flags, int indent) 209 | { 210 | assert(node->type() == node_data); 211 | if (!(flags & print_no_indenting)) 212 | out = fill_chars(out, indent, Ch('\t')); 213 | out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); 214 | return out; 215 | } 216 | 217 | // Print data node 218 | template 219 | inline OutIt print_cdata_node(OutIt out, const xml_node *node, int flags, int indent) 220 | { 221 | assert(node->type() == node_cdata); 222 | if (!(flags & print_no_indenting)) 223 | out = fill_chars(out, indent, Ch('\t')); 224 | *out = Ch('<'); ++out; 225 | *out = Ch('!'); ++out; 226 | *out = Ch('['); ++out; 227 | *out = Ch('C'); ++out; 228 | *out = Ch('D'); ++out; 229 | *out = Ch('A'); ++out; 230 | *out = Ch('T'); ++out; 231 | *out = Ch('A'); ++out; 232 | *out = Ch('['); ++out; 233 | out = copy_chars(node->value(), node->value() + node->value_size(), out); 234 | *out = Ch(']'); ++out; 235 | *out = Ch(']'); ++out; 236 | *out = Ch('>'); ++out; 237 | return out; 238 | } 239 | 240 | // Print element node 241 | template 242 | inline OutIt print_element_node(OutIt out, const xml_node *node, int flags, int indent) 243 | { 244 | assert(node->type() == node_element); 245 | 246 | // Print element name and attributes, if any 247 | if (!(flags & print_no_indenting)) 248 | out = fill_chars(out, indent, Ch('\t')); 249 | *out = Ch('<'), ++out; 250 | out = copy_chars(node->name(), node->name() + node->name_size(), out); 251 | out = print_attributes(out, node, flags); 252 | 253 | // If node is childless 254 | if (node->value_size() == 0 && !node->first_node()) 255 | { 256 | // Print childless node tag ending 257 | *out = Ch('/'), ++out; 258 | *out = Ch('>'), ++out; 259 | } 260 | else 261 | { 262 | // Print normal node tag ending 263 | *out = Ch('>'), ++out; 264 | 265 | // Test if node contains a single data node only (and no other nodes) 266 | xml_node *child = node->first_node(); 267 | if (!child) 268 | { 269 | // If node has no children, only print its value without indenting 270 | out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); 271 | } 272 | else if (child->next_sibling() == 0 && child->type() == node_data) 273 | { 274 | // If node has a sole data child, only print its value without indenting 275 | out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out); 276 | } 277 | else 278 | { 279 | // Print all children with full indenting 280 | if (!(flags & print_no_indenting)) 281 | *out = Ch('\n'), ++out; 282 | out = print_children(out, node, flags, indent + 1); 283 | if (!(flags & print_no_indenting)) 284 | out = fill_chars(out, indent, Ch('\t')); 285 | } 286 | 287 | // Print node end 288 | *out = Ch('<'), ++out; 289 | *out = Ch('/'), ++out; 290 | out = copy_chars(node->name(), node->name() + node->name_size(), out); 291 | *out = Ch('>'), ++out; 292 | } 293 | return out; 294 | } 295 | 296 | // Print declaration node 297 | template 298 | inline OutIt print_declaration_node(OutIt out, const xml_node *node, int flags, int indent) 299 | { 300 | // Print declaration start 301 | if (!(flags & print_no_indenting)) 302 | out = fill_chars(out, indent, Ch('\t')); 303 | *out = Ch('<'), ++out; 304 | *out = Ch('?'), ++out; 305 | *out = Ch('x'), ++out; 306 | *out = Ch('m'), ++out; 307 | *out = Ch('l'), ++out; 308 | 309 | // Print attributes 310 | out = print_attributes(out, node, flags); 311 | 312 | // Print declaration end 313 | *out = Ch('?'), ++out; 314 | *out = Ch('>'), ++out; 315 | 316 | return out; 317 | } 318 | 319 | // Print comment node 320 | template 321 | inline OutIt print_comment_node(OutIt out, const xml_node *node, int flags, int indent) 322 | { 323 | assert(node->type() == node_comment); 324 | if (!(flags & print_no_indenting)) 325 | out = fill_chars(out, indent, Ch('\t')); 326 | *out = Ch('<'), ++out; 327 | *out = Ch('!'), ++out; 328 | *out = Ch('-'), ++out; 329 | *out = Ch('-'), ++out; 330 | out = copy_chars(node->value(), node->value() + node->value_size(), out); 331 | *out = Ch('-'), ++out; 332 | *out = Ch('-'), ++out; 333 | *out = Ch('>'), ++out; 334 | return out; 335 | } 336 | 337 | // Print doctype node 338 | template 339 | inline OutIt print_doctype_node(OutIt out, const xml_node *node, int flags, int indent) 340 | { 341 | assert(node->type() == node_doctype); 342 | if (!(flags & print_no_indenting)) 343 | out = fill_chars(out, indent, Ch('\t')); 344 | *out = Ch('<'), ++out; 345 | *out = Ch('!'), ++out; 346 | *out = Ch('D'), ++out; 347 | *out = Ch('O'), ++out; 348 | *out = Ch('C'), ++out; 349 | *out = Ch('T'), ++out; 350 | *out = Ch('Y'), ++out; 351 | *out = Ch('P'), ++out; 352 | *out = Ch('E'), ++out; 353 | *out = Ch(' '), ++out; 354 | out = copy_chars(node->value(), node->value() + node->value_size(), out); 355 | *out = Ch('>'), ++out; 356 | return out; 357 | } 358 | 359 | // Print pi node 360 | template 361 | inline OutIt print_pi_node(OutIt out, const xml_node *node, int flags, int indent) 362 | { 363 | assert(node->type() == node_pi); 364 | if (!(flags & print_no_indenting)) 365 | out = fill_chars(out, indent, Ch('\t')); 366 | *out = Ch('<'), ++out; 367 | *out = Ch('?'), ++out; 368 | out = copy_chars(node->name(), node->name() + node->name_size(), out); 369 | *out = Ch(' '), ++out; 370 | out = copy_chars(node->value(), node->value() + node->value_size(), out); 371 | *out = Ch('?'), ++out; 372 | *out = Ch('>'), ++out; 373 | return out; 374 | } 375 | 376 | } 377 | //! \endcond 378 | 379 | /////////////////////////////////////////////////////////////////////////// 380 | // Printing 381 | 382 | //! Prints XML to given output iterator. 383 | //! \param out Output iterator to print to. 384 | //! \param node Node to be printed. Pass xml_document to print entire document. 385 | //! \param flags Flags controlling how XML is printed. 386 | //! \return Output iterator pointing to position immediately after last character of printed text. 387 | template 388 | inline OutIt print(OutIt out, const xml_node &node, int flags = 0) 389 | { 390 | return internal::print_node(out, &node, flags, 0); 391 | } 392 | 393 | #ifndef RAPIDXML_NO_STREAMS 394 | 395 | //! Prints XML to given output stream. 396 | //! \param out Output stream to print to. 397 | //! \param node Node to be printed. Pass xml_document to print entire document. 398 | //! \param flags Flags controlling how XML is printed. 399 | //! \return Output stream. 400 | template 401 | inline std::basic_ostream &print(std::basic_ostream &out, const xml_node &node, int flags = 0) 402 | { 403 | print(std::ostream_iterator(out), node, flags); 404 | return out; 405 | } 406 | 407 | //! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process. 408 | //! \param out Output stream to print to. 409 | //! \param node Node to be printed. 410 | //! \return Output stream. 411 | template 412 | inline std::basic_ostream &operator <<(std::basic_ostream &out, const xml_node &node) 413 | { 414 | return print(out, node); 415 | } 416 | 417 | #endif 418 | 419 | } 420 | 421 | #endif 422 | -------------------------------------------------------------------------------- /rapidxml/rapidxml_utils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RAPIDXML_UTILS_HPP_INCLUDED 2 | #define RAPIDXML_UTILS_HPP_INCLUDED 3 | 4 | // Copyright (C) 2006, 2009 Marcin Kalicinski 5 | // Version 1.13 6 | // Revision $DateTime: 2009/05/13 01:46:17 $ 7 | //! \file rapidxml_utils.hpp This file contains high-level rapidxml utilities that can be useful 8 | //! in certain simple scenarios. They should probably not be used if maximizing performance is the main objective. 9 | 10 | #include "rapidxml.hpp" 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace rapidxml 17 | { 18 | 19 | //! Represents data loaded from a file 20 | template 21 | class file 22 | { 23 | 24 | public: 25 | 26 | //! Loads file into the memory. Data will be automatically destroyed by the destructor. 27 | //! \param filename Filename to load. 28 | file(const char *filename) 29 | { 30 | using namespace std; 31 | 32 | // Open stream 33 | basic_ifstream stream(filename, ios::binary); 34 | if (!stream) 35 | throw runtime_error(string("cannot open file ") + filename); 36 | stream.unsetf(ios::skipws); 37 | 38 | // Determine stream size 39 | stream.seekg(0, ios::end); 40 | size_t size = stream.tellg(); 41 | stream.seekg(0); 42 | 43 | // Load data and add terminating 0 44 | m_data.resize(size + 1); 45 | stream.read(&m_data.front(), static_cast(size)); 46 | m_data[size] = 0; 47 | } 48 | 49 | //! Loads file into the memory. Data will be automatically destroyed by the destructor 50 | //! \param stream Stream to load from 51 | file(std::basic_istream &stream) 52 | { 53 | using namespace std; 54 | 55 | // Load data and add terminating 0 56 | stream.unsetf(ios::skipws); 57 | m_data.assign(istreambuf_iterator(stream), istreambuf_iterator()); 58 | if (stream.fail() || stream.bad()) 59 | throw runtime_error("error reading stream"); 60 | m_data.push_back(0); 61 | } 62 | 63 | //! Gets file data. 64 | //! \return Pointer to data of file. 65 | Ch *data() 66 | { 67 | return &m_data.front(); 68 | } 69 | 70 | //! Gets file data. 71 | //! \return Pointer to data of file. 72 | const Ch *data() const 73 | { 74 | return &m_data.front(); 75 | } 76 | 77 | //! Gets file data size. 78 | //! \return Size of file data, in characters. 79 | std::size_t size() const 80 | { 81 | return m_data.size(); 82 | } 83 | 84 | private: 85 | 86 | std::vector m_data; // File data 87 | 88 | }; 89 | 90 | //! Counts children of node. Time complexity is O(n). 91 | //! \return Number of children of node 92 | template 93 | inline std::size_t count_children(xml_node *node) 94 | { 95 | xml_node *child = node->first_node(); 96 | std::size_t count = 0; 97 | while (child) 98 | { 99 | ++count; 100 | child = child->next_sibling(); 101 | } 102 | return count; 103 | } 104 | 105 | //! Counts attributes of node. Time complexity is O(n). 106 | //! \return Number of attributes of node 107 | template 108 | inline std::size_t count_attributes(xml_node *node) 109 | { 110 | xml_attribute *attr = node->first_attribute(); 111 | std::size_t count = 0; 112 | while (attr) 113 | { 114 | ++count; 115 | attr = attr->next_attribute(); 116 | } 117 | return count; 118 | } 119 | 120 | } 121 | 122 | #endif 123 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | project(danmaku2ass_native) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0") 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") 7 | 8 | set(SOURCE_FILES 9 | danmaku2ass.cpp 10 | danmaku2ass.h 11 | rapidxml/rapidxml_iterators.hpp 12 | rapidxml/rapidxml_print.hpp 13 | rapidxml/rapidxml_utils.hpp 14 | rapidxml/rapidxml.hpp 15 | AssClass.cpp 16 | AssClass.hpp 17 | ) 18 | 19 | add_executable(danmaku2ass_native ${SOURCE_FILES}) 20 | add_library(danmaku2ass SHARED ${SOURCE_FILES}) -------------------------------------------------------------------------------- /tests/install_lcov.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | if [ ! -d "$HOME/protobuf/lib" ]; then 4 | wget http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.11.orig.tar.gz; 5 | tar xf lcov_1.11.orig.tar.gz; 6 | export PREFIX=$HOME/lcov; 7 | make -C lcov-1.11/ install; 8 | else 9 | echo 'Using cached lcov.'; 10 | fi -------------------------------------------------------------------------------- /tests/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ./danmaku2ass_native -in=../tests/testdata.xml -out=./test.out -w=1280 -h=720 -font="Heiti SC" -fontsize=25 -alpha=0.8 -dm=5 -ds=5 3 | $HOME/lcov/usr/bin/lcov --gcov-tool "gcov-4.8" --directory . --capture --output-file coverage.info 4 | $HOME/lcov/usr/bin/lcov --gcov-tool "gcov-4.8" --remove coverage.info 'tests/*' '/usr/*' 'rapid*' --output-file coverage.info 5 | $HOME/lcov/usr/bin/lcov --gcov-tool "gcov-4.8" --list coverage.info 6 | coveralls-lcov --repo-token $COVERALLS_TOKEN coverage.info -------------------------------------------------------------------------------- /tests/testdata.xml: -------------------------------------------------------------------------------- 1 | chat.bilibili.com000k-v 2 | 注意:该数据仅供测试转换 3 | 没有星星,哭得有点疲累的夜晚 4 | 星のない、泣き疲れた夜に 5 | 迷失在深不见尽头的森林 6 | 深い森に迷い込んで 7 | 穿过微微发光的小道 8 | 薄暗い道を抜け 9 | 一边寻找着那小小的光明一边走着 10 | 小さな灯りを探して歩いた 11 | 无论出现了怎么样强大的敌人 12 | どんなに强い敌が出ても 13 | 是我的话本应该能战胜的 14 | 仆なら胜てるはずだって 15 | 只不过是单纯地一味奋起对抗 16 | ひたすらただ立ち向かっても 17 | 也无法变成跟想像中一样 18 | 思い通りに行かなくて 19 | 不知从何时开始变得害怕受伤 20 | 伤つく事が怖くなって 21 | 借口所堆砌而成的盾不断增加 22 | 言い訳の盾が増えて行く 23 | 放弃的话明明就能变得更轻松 24 | 谛められたら楽なのに 25 | 可是我就连那样的勇气也没有 26 | 仆はそんな勇気もなくて 27 | 一边寻找答案却又再度迷茫 28 | 答えを探してまた迷って 29 | 离大团圆的结局虽然还有很远 30 | まだハッピーエンドは远いけど 31 | 总有哪一天一定能够到达的 32 | きっといつかたどり着けるよって 33 | 我得相信那一个正焦急着的我啊 34 | もがいている仆を信じなくちゃ 35 | 为了战斗而一直挥舞着的剑 36 | 戦うために振りかざした 37 | 那剑刃现在却是朝着自己的方向 38 | 刃は仆を向いているんだ 39 | 其实很久以前已经察觉到了 40 | 本当はもう気づいていたんだ 41 | 我的敌人其实一直都在我心中 42 | 敌は仆の中にいること 43 | 拜拜 44 | バイバイ 45 | 突然发现,我已经迷失在某一个 46 | 気づけば、どこかで见たような 47 | 似曾相识的深深的森林里 48 | 深い森に迷い込んで 49 | 总是一边摸索着前进却仍依旧 50 | いつでも手探りで进んで 51 | 迷失方向已经是第几次了呢 52 | 迷うのはもう何度目だ 53 | 尽管如此我啊在这个世界之上 54 | それでも仆はこの世界で 55 | 总是一直埋头寻找着一些什么 56 | いつも何かを探してきた 57 | 目的地之类的虽然我完全没有 58 | 行く当て何てないけども 59 | 但是无论如何我也不想后悔啊 60 | 后悔だけはしたくないから 61 | 即使另一个自己因为我的懦弱 62 | 弱気でもう一人の自分が 63 | 阻扰着我前往自己所决定的去向 64 | 仆の向かう道を阻んでも 65 | 可我一定要用比任何的觉悟 66 | どんな覚悟より固い剣で 67 | 都要坚固的剑摆脱它再度前进啊 68 | 振り払って前に进めなくちゃ 69 | 即使这样的我也很清楚明白到 70 | よくあるおとぎ话みたいに 71 | 离大团圆的结局虽然还有很远 72 | まだハッピーエンドは远いけど 73 | 总有哪一天一定能够到达的 74 | きっといつかたどり着けるよって 75 | 我得相信那一个正焦急着的我啊 76 | もがいている仆を信じなくちゃ 77 | 为了战斗而一直挥舞着的剑 78 | 戦うために振りかざした 79 | 那剑刃现在却是朝着自己的方向 80 | 刃は仆を向いているんだ 81 | 其实很久以前已经察觉到了 82 | 本当はもう気づいていたんだ 83 | 我的敌人其实一直都在我心中 84 | 敌は仆の中にいること 85 | 拜拜 86 | バイバイ 87 | 第一发~ 88 | 元氣好評 89 | 第三 90 | 第四发~ 91 | 我要吃一串烤鱼! 92 | 霞我们去约吧! 93 | 请问BGM是啥。。。。 94 | 这是什么动漫啊? 95 | 南酱出品,必属精品! 96 | = =卧槽 好屌的特技 97 | 听到BGM我就收藏好了 98 | 好喜欢这BGM 伴了我半年 99 | 所以 要早点回来哦 100 | 所以 要早点回来哦 101 | 所以 要早点回来哦 102 | 所以 要早点回来哦 103 | 所以 要早点回来哦 104 | 所以 要早点回来哦 105 | 不错 106 | 呜呜呜囡仔赛高!!!!QAQ 107 | 棒棒棒啦QAQ 108 | 画质有点儿难过【躺 109 | 泪目了啊TwT 110 | 33我爱你ლ(′◉❥◉`ლ) 111 | 超喜欢这幅字! 112 | 赛高~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 113 | 大赞。为什么我发弹幕就卡 114 | 半田传说~·~~ 115 | 囝仔必须发一个 116 | 半田老师! 117 | 明明是很好看的动漫为何人这么少啊 118 | --------------------------------------------------------------------------------