00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057 #ifdef HAVE_CONFIG_H
00058 # include "config.h"
00059 #endif
00060 #if NEED_GNUG_PRAGMAS
00061 # pragma implementation
00062 #endif
00063
00064
00065
00066
00067 #include "XMLTags.h"
00068 #include "UnicodeByteStream.h"
00069 #include <ctype.h>
00070 #if HAS_WCTYPE
00071 #include <wctype.h>
00072 #endif
00073
00074
00075 #ifdef HAVE_NAMESPACES
00076 namespace DJVU {
00077 # ifdef NOT_DEFINED // Just to fool emacs c++ mode
00078 }
00079 #endif
00080 #endif
00081
00082 lt_XMLContents::lt_XMLContents(void) {}
00083
00084 lt_XMLContents::lt_XMLContents(GP<lt_XMLTags> t)
00085 {
00086 tag=t;
00087 }
00088
00089 static GUTF8String
00090 getargn(char const tag[], char const *&t)
00091 {
00092 char const *s;
00093 for(s=tag;isspace(*s);s++);
00094 for(t=s;(*t)&&((*t)!='/')&&((*t)!='>')&&((*t)!='=')&&!isspace(*t);++t);
00095 return GUTF8String(s,t-s);
00096 }
00097
00098 static GUTF8String
00099 getargv(char const tag[], char const *&t)
00100 {
00101 GUTF8String retval;
00102 if(tag && tag[0] == '=')
00103 {
00104 char const *s=t=tag+1;
00105 if((*t == '"')||(*t == '\47'))
00106 {
00107 char const q=*(t++);
00108 for(s++;(*t)&&((*t)!=q)&&((*t)!='>');++t);
00109 retval=GUTF8String(s,t-s);
00110 if (t[0] == q)
00111 {
00112 ++t;
00113 }
00114 }else
00115 {
00116 for(t=s;(*t)&&((*t)!='/')&&((*t)!='>')&&!isspace(*t);++t);
00117 retval=GUTF8String(s,t-s);
00118 }
00119 }else
00120 {
00121 t=tag;
00122 }
00123 return retval;
00124 }
00125
00126 static GUTF8String
00127 tagtoname(char const tag[],char const *&t)
00128 {
00129 char const *s;
00130 for(s=tag;isspace(*s);s++);
00131 for(t=s;(*t)&&((*t)!='>')&&((*t)!='/')&&!isspace(*t);++t);
00132 return GUTF8String(s,t-s);
00133 }
00134
00135 static inline GUTF8String
00136 tagtoname(char const tag[])
00137 {
00138 char const *t;
00139 return tagtoname(tag,t);
00140 }
00141
00142 static inline bool
00143 isspaces(const GUTF8String &raw)
00144 {
00145 return (raw.nextNonSpace() == (int)raw.length());
00146 }
00147
00148 void
00149 lt_XMLTags::ParseValues(char const *t, GMap<GUTF8String,GUTF8String> &args,bool downcase)
00150 {
00151 GUTF8String argn;
00152 char const *tt;
00153 while((argn=getargn(t,tt)).length())
00154 {
00155 if(downcase)
00156 argn=argn.downcase();
00157 args[argn]=getargv(tt,t).fromEscaped();
00158 }
00159 }
00160
00161 lt_XMLTags::~lt_XMLTags() {}
00162
00163 lt_XMLTags::lt_XMLTags(void) : startline(0) {}
00164
00165 lt_XMLTags::lt_XMLTags(const char n[]) : startline(0)
00166 {
00167 char const *t;
00168 name=tagtoname(n,t);
00169 ParseValues(t,args);
00170 }
00171
00172 void
00173 lt_XMLTags::init(const GP<ByteStream> &bs)
00174 {
00175 GP<XMLByteStream> gxmlbs=XMLByteStream::create(bs);
00176 init(*gxmlbs);
00177 }
00178
00179 void
00180 lt_XMLTags::init(const GURL &url)
00181 {
00182 const GP<ByteStream> bs=ByteStream::create(url,"rb");
00183 init(bs);
00184 }
00185
00186 void
00187 lt_XMLTags::init(XMLByteStream &xmlbs)
00188 {
00189 if(!get_count())
00190 {
00191 G_THROW( ERR_MSG("XMLTags.no_GP") );
00192 }
00193 GPList<lt_XMLTags> level;
00194 GUTF8String tag,raw(xmlbs.gets(0,'<',false));
00195 int linesread=xmlbs.get_lines_read();
00196 if(!isspaces(raw))
00197 {
00198 G_THROW( (ERR_MSG("XMLTags.raw_string") "\t")+raw);
00199 }
00200 GUTF8String encoding;
00201 for(int len;(len=(tag=xmlbs.gets(0,'>',true)).length());)
00202 {
00203 if(tag[len-1] != '>')
00204 {
00205 G_THROW((ERR_MSG("XMLTags.bad_tag") "\t")+tag);
00206 }
00207 switch(tag[1])
00208 {
00209 case '?':
00210 {
00211 while(len < 4 || tag.substr(len-2,len) != "?>")
00212 {
00213 GUTF8String cont(xmlbs.gets(0,'>',true));
00214 if(!cont.length())
00215 {
00216 G_THROW( (ERR_MSG("XMLTags.bad_PI") "\t")+tag);
00217 }
00218 len=((tag+=cont).length());
00219 }
00220 char const *n;
00221 GUTF8String xtag = tag.substr(2,-1);
00222 GUTF8String xname = tagtoname(xtag,n);
00223 if(xname.downcase() == "xml")
00224 {
00225 ParseValues(n,args);
00226 for(GPosition pos=args;pos;++pos)
00227 {
00228 if(args.key(pos) == "encoding")
00229 {
00230 const GUTF8String e=args[pos].upcase();
00231 if(e != encoding)
00232 {
00233 xmlbs.set_encoding((encoding=e));
00234 }
00235 }
00236 }
00237 }
00238 break;
00239 }
00240 case '!':
00241 {
00242 if(tag[2] == '-' && tag[3] == '-')
00243 {
00244 while((len < 7) ||
00245 (tag.substr(len-3,-1) != "-->"))
00246 {
00247 GUTF8String cont(xmlbs.gets(0,'>',true));
00248 if(!cont.length())
00249 {
00250 GUTF8String mesg;
00251 mesg.format( ERR_MSG("XMLTags.bad_comment") "\t%s",(const char *)tag);
00252 G_THROW(mesg);
00253 }
00254 len=((tag+=cont).length());
00255 }
00256 }
00257 break;
00258 }
00259 case '/':
00260 {
00261 GUTF8String xname=tagtoname(tag.substr(2,-1));
00262 GPosition last=level.lastpos();
00263 if(last)
00264 {
00265 if(level[last]->name != xname)
00266 {
00267 G_THROW( (ERR_MSG("XMLTags.unmatched_end") "\t")
00268 +level[last]->name+("\t"+GUTF8String(level[last]->get_Line()))
00269 +("\t"+xname)+("\t"+GUTF8String(linesread+1)));
00270 }
00271 level.del(last);
00272 }else
00273 {
00274 G_THROW( ERR_MSG("XMLTags.bad_form") );
00275 }
00276 break;
00277 }
00278 default:
00279 {
00280 GPosition last=level.lastpos();
00281 GP<lt_XMLTags> t;
00282 if(last)
00283 {
00284 t=new lt_XMLTags(tag.substr(1,len-1));
00285 level[last]->addtag(t);
00286 if(tag[len-2] != '/')
00287 {
00288 level.append(t);
00289 }
00290 }else if(tag[len-2] != '/')
00291 {
00292 char const *n;
00293 GUTF8String xtag = tag.substr(1,-1);
00294 name=tagtoname(xtag, n);
00295 ParseValues(n,args);
00296 t=this;
00297 level.append(t);
00298 }else
00299 {
00300 G_THROW( ERR_MSG("XMLTags.no_body") );
00301 }
00302 t->set_Line(linesread+1);
00303 break;
00304 }
00305 }
00306 if((raw=xmlbs.gets(0,'<',false))[0])
00307 {
00308 linesread=xmlbs.get_lines_read();
00309 GPosition last=level.lastpos();
00310 if(last)
00311 {
00312 level[last]->addraw(raw);
00313 }else if(!isspaces(raw))
00314 {
00315 G_THROW(( ERR_MSG("XMLTags.raw_string") "\t")+raw);
00316 }
00317 }
00318 }
00319 }
00320
00321 GPList<lt_XMLTags>
00322 lt_XMLTags::get_Tags(char const tagname[]) const
00323 {
00324 GPosition pos=allTags.contains(tagname);
00325 GPList<lt_XMLTags> retval;
00326 return (pos?allTags[pos]:retval);
00327 }
00328
00329 void
00330 lt_XMLTags::get_Maps(char const tagname[],
00331 char const argn[],
00332 GPList<lt_XMLTags> list,
00333 GMap<GUTF8String, GP<lt_XMLTags> > &map)
00334 {
00335 for(GPosition pos=list;pos;++pos)
00336 {
00337 GP<lt_XMLTags> &tag=list[pos];
00338 if(tag)
00339 {
00340 GPosition loc;
00341 if((loc=tag->contains(tagname)))
00342 {
00343 GPList<lt_XMLTags> maps=(GPList<lt_XMLTags> &)((*tag)[loc]);
00344 for(GPosition mloc=maps;mloc;++mloc)
00345 {
00346 GP<lt_XMLTags> gtag=maps[mloc];
00347 if(gtag)
00348 {
00349 GMap<GUTF8String,GUTF8String> &args=gtag->args;
00350 GPosition gpos;
00351 if((gpos=args.contains(argn)))
00352 {
00353 map[args[gpos]]=gtag;
00354 }
00355 }
00356 }
00357 }
00358 }
00359 }
00360 }
00361
00362 void
00363 lt_XMLTags::write(ByteStream &bs,bool const top) const
00364 {
00365 if(name.length())
00366 {
00367 GUTF8String tag="<"+name;
00368 for(GPosition pos=args;pos;++pos)
00369 {
00370 tag+=GUTF8String(' ')+args.key(pos)+GUTF8String("=\42")+args[pos].toEscaped()+GUTF8String("\42");
00371 }
00372 GPosition tags=content;
00373 if(tags||raw.length())
00374 {
00375 tag+=">";
00376 bs.writall((const char *)tag,tag.length());
00377 tag="</"+name+">";
00378 if(raw.length())
00379 {
00380 bs.writestring(raw);
00381 }
00382 for(;tags;++tags)
00383 {
00384 content[tags].write(bs);
00385 }
00386 }else if(!raw.length())
00387 {
00388 tag+="/>";
00389 }
00390 bs.writall((const char *)tag,tag.length());
00391 }
00392 if(top)
00393 {
00394 bs.writall("\n",1);
00395 }
00396 }
00397
00398 void
00399 lt_XMLContents::write(ByteStream &bs) const
00400 {
00401 if(tag)
00402 {
00403 tag->write(bs,false);
00404 }
00405 if(raw.length())
00406 {
00407 bs.writestring(raw);
00408 }
00409 }
00410
00411
00412 #ifdef HAVE_NAMESPACES
00413 }
00414 # ifndef NOT_USING_DJVU_NAMESPACE
00415 using namespace DJVU;
00416 # endif
00417 #endif