1 
2 
3 #include "tstream.h"
4 #include "tpersist.h"
5 #include "tfilepath_io.h"
6 #include "tconvert.h"
7 #include "tsystem.h"
8 #include "tutil.h"
9 
10 #if defined(LZ4_STATIC)
11 #include "lz4frame_static.h"
12 #else
13 #include "lz4frame.h"
14 #endif
15 
16 #include <sstream>
17 #include <memory>
18 
19 using namespace std;
20 
21 //===============================================================
22 namespace {
23 
escape(string v)24 string escape(string v) {
25   int i = 0;
26   for (;;) {
27 // Removing escaping of apostrophe from Windows and OSX as it's not needed and
28 // causes problems
29 #if defined(LINUX) || defined(FREEBSD)
30     i = v.find_first_of("\\\'\"", i);
31 #else
32     i = v.find_first_of("\\\"", i);
33 #endif
34     if (i == (int)string::npos) break;
35     string h = "\\" + v[i];
36     v.insert(i, "\\");
37     i = i + 2;
38   }
39   return v;
40 }
41 
42 //===============================================================
43 
writeCompressedFile(TFilePath dst,const std::string & str)44 void writeCompressedFile(TFilePath dst, const std::string &str) {}
45 
46 //===================================================================
47 
readCompressedFile(string & str,TFilePath src)48 void readCompressedFile(string &str, TFilePath src) {
49   TFileStatus status(src);
50   if (!status.doesExist()) return;
51 
52   size_t in_len = status.getSize();
53   char *in      = (char *)malloc(in_len);
54   {
55     Tifstream is(src);
56     is.read((char *)in, in_len);
57   }
58 
59   LZ4F_decompressionContext_t lz4dctx;
60 
61   LZ4F_errorCode_t err =
62       LZ4F_createDecompressionContext(&lz4dctx, LZ4F_VERSION);
63   if (LZ4F_isError(err)) return;
64 
65   size_t in_len_read = 0;
66 
67   size_t out_len = 1000000, out_len_written, out_len_moved = 0;
68   void *out = (void *)malloc(out_len);
69 
70   while (in_len_read < in_len) {
71     out_len_written = out_len;
72 
73     size_t remaining =
74         LZ4F_decompress(lz4dctx, out, &out_len_written, in, &in_len, NULL);
75     if (LZ4F_isError(remaining)) break;
76 
77     str.resize(out_len_moved + out_len_written);
78 
79     memcpy((void *)(str.c_str() + out_len_moved), (void *)out, out_len_written);
80     out_len_moved += out_len_written;
81   }
82 
83   LZ4F_freeDecompressionContext(lz4dctx);
84 
85   free(in);
86   free(out);
87 }
88 
89 namespace {
90 // TODO: Unify with tcodec.cpp's version
lz4decompress(LZ4F_decompressionContext_t lz4dctx,char * out,size_t * out_len_res,const char * in,size_t in_len)91 bool lz4decompress(LZ4F_decompressionContext_t lz4dctx, char *out,
92                    size_t *out_len_res, const char *in, size_t in_len) {
93   size_t out_len = *out_len_res, in_read, out_written;
94 
95   *out_len_res = 0;
96 
97   while (in_len) {
98     out_written = out_len;
99     in_read     = in_len;
100 
101     size_t res = LZ4F_decompress(lz4dctx, (void *)out, &out_written,
102                                  (const void *)in, &in_read, NULL);
103 
104     if (LZ4F_isError(res)) return false;
105 
106     *out_len_res += out_written;
107 
108     out += out_written;
109     out_len -= out_written;
110 
111     in += in_read;
112     in_len -= in_read;
113   }
114 
115   return true;
116 }
117 }  // namespace
118 
119 //===============================================================
120 
121 class StreamTag {
122 public:
123   string m_name;
124   std::map<std::string, string> m_attributes;
125   enum Type { BeginTag, EndTag, BeginEndTag };
126   Type m_type;
StreamTag()127   StreamTag() : m_type(BeginTag) {}
128 
operator bool() const129   operator bool() const { return m_name != ""; }
130 
dump()131   void dump() {
132     cout << "name = '" << m_name << "'" << endl;
133     cout << "type = ";
134     switch (m_type) {
135     case BeginTag:
136       cout << "begin Tag";
137       break;
138     case EndTag:
139       cout << "end Tag";
140       break;
141     case BeginEndTag:
142       cout << "begin/end Tag";
143       break;
144     default:
145       cout << "**bad Tag**";
146       break;
147     }
148     cout << endl;
149     std::map<std::string, string>::iterator it;
150     for (it = m_attributes.begin(); it != m_attributes.end(); ++it) {
151       cout << " '" << it->first << "' = '" << it->second << "'" << endl;
152     }
153   }
154 };
155 
156 //--------------------------------
157 
158 class TPersistFactory {
159   typedef std::map<std::string, TPersistDeclaration *> Table;
160   static TPersistFactory *m_factory;
161   Table m_table;
TPersistFactory()162   TPersistFactory() {}
163 
164 public:
instance()165   static TPersistFactory *instance() {
166     if (!m_factory) m_factory = new TPersistFactory;
167     return m_factory;
168   }
add(string name,TPersistDeclaration * decl)169   void add(string name, TPersistDeclaration *decl) { m_table[name] = decl; }
create(string name)170   TPersist *create(string name) {
171     Table::iterator it = m_table.find(name);
172     if (it != m_table.end())
173       return (it->second)->create();
174     else
175       return 0;
176   }
177 };
178 
179 //--------------------------------
180 
181 TPersistFactory *TPersistFactory::m_factory = 0;
182 
183 }  // namespace
184 
185 //--------------------------------
186 
TPersistDeclaration(const std::string & id)187 TPersistDeclaration::TPersistDeclaration(const std::string &id) : m_id(id) {
188   TPersistFactory::instance()->add(id, this);
189 }
190 
191 //===============================================================
192 
create(const std::string & name)193 TPersist *TPersist::create(const std::string &name) {
194   return TPersistFactory::instance()->create(name);
195 }
196 
197 //===============================================================
198 
199 class TOStream::Imp {
200 public:
201   ostream *m_os;
202   bool m_chanOwner;
203   bool m_compressed;
204   ostringstream m_ostringstream;
205 
206   vector<std::string> m_tagStack;
207   int m_tab;
208   bool m_justStarted;
209   typedef map<TPersist *, int> PersistTable;
210   PersistTable m_table;
211   int m_maxId;
212   TFilePath m_filepath;
213 
Imp()214   Imp()
215       : m_os(0)
216       , m_chanOwner(false)
217       , m_tab(0)
218       , m_justStarted(true)
219       , m_maxId(0)
220       , m_compressed(false) {}
221 };
222 
223 //---------------------------------------------------------------
224 
TOStream(const TFilePath & fp,bool compressed)225 TOStream::TOStream(const TFilePath &fp, bool compressed) : m_imp(new Imp) {
226   m_imp->m_filepath = fp;
227 
228   if (compressed) {
229     m_imp->m_os         = &m_imp->m_ostringstream;
230     m_imp->m_compressed = true;
231     m_imp->m_chanOwner  = false;
232   } else {
233     std::unique_ptr<Tofstream> os(new Tofstream(fp));
234     m_imp->m_os        = os->isOpen() ? os.release() : 0;
235     m_imp->m_chanOwner = true;
236   }
237 
238   m_imp->m_justStarted = true;
239 }
240 
241 //---------------------------------------------------------------
242 
TOStream(std::shared_ptr<Imp> imp)243 TOStream::TOStream(std::shared_ptr<Imp> imp) : m_imp(std::move(imp)) {
244   assert(!m_imp->m_tagStack.empty());
245   ostream &os = *m_imp->m_os;
246   if (!m_imp->m_justStarted) cr();
247   os << "<" << m_imp->m_tagStack.back() << ">";
248   m_imp->m_tab++;
249   cr();
250   m_imp->m_justStarted = true;
251 }
252 
TOStream(TOStream && that)253 TOStream::TOStream(TOStream &&that) : m_imp(std::move(that.m_imp)) {}
254 
operator =(TOStream && that)255 TOStream &TOStream::operator=(TOStream &&that) {
256   if (this != &that) {
257     this->m_imp = std::move(that.m_imp);
258   }
259   return *this;
260 }
261 
262 //---------------------------------------------------------------
263 
~TOStream()264 TOStream::~TOStream() {
265   if (!m_imp) {
266     return;
267   }
268   try {
269     if (!m_imp->m_tagStack.empty()) {
270       string tagName = m_imp->m_tagStack.back();
271       m_imp->m_tagStack.pop_back();
272       assert(tagName != "");
273       ostream &os = *m_imp->m_os;
274       m_imp->m_tab--;
275       if (!m_imp->m_justStarted) cr();
276       os << "</" << tagName << ">";
277       cr();
278       m_imp->m_justStarted = true;
279     } else {
280       if (m_imp->m_compressed) {
281         std::string tmp = m_imp->m_ostringstream.str();
282         const void *in  = (const void *)tmp.c_str();
283 
284         size_t in_len = strlen((char *)in);
285 
286         size_t out_len = LZ4F_compressFrameBound(in_len, NULL);
287         void *out      = malloc(out_len);
288 
289         out_len = LZ4F_compressFrame(out, out_len, in, in_len, NULL);
290         if (!LZ4F_isError(out_len)) {
291           Tofstream os(m_imp->m_filepath);
292           // TNZC <lunghezza dati decompress> <lunghezza dati compresso> <dati
293           // compressi>
294           os.write("TABc", 4);
295           TINT32 v;
296           v = 0x0A0B0C0D;
297           os.write((char *)&v, sizeof v);
298           v = in_len;
299           os.write((char *)&v, sizeof v);
300           v = out_len;
301           os.write((char *)&v, sizeof v);
302           os.write((char *)out, out_len);
303         }
304 
305         free(out);
306       }
307       if (m_imp->m_chanOwner) delete m_imp->m_os;
308     }
309   } catch (...) {
310   }
311 }
312 
313 //---------------------------------------------------------------
314 
getFilePath()315 TFilePath TOStream::getFilePath() { return m_imp->m_filepath; }
316 
317 //---------------------------------------------------------------
318 
getRepositoryPath()319 TFilePath TOStream::getRepositoryPath() {
320   TFilePath fp = m_imp->m_filepath;
321   return fp.getParentDir() + (fp.getName() + "_files");
322 }
323 
324 //---------------------------------------------------------------
325 
operator <<(int v)326 TOStream &TOStream::operator<<(int v) {
327   *(m_imp->m_os) << v << " ";
328   m_imp->m_justStarted = false;
329   return *this;
330 }
331 
332 //---------------------------------------------------------------
333 
operator <<(double v)334 TOStream &TOStream::operator<<(double v) {
335   if (areAlmostEqual(v, 0))  // con valori molto piccoli (es. 1.4e-310) non
336                              // riesce a rileggerli!
337     v = 0;
338 
339   *(m_imp->m_os) << v << " ";
340   m_imp->m_justStarted = false;
341   return *this;
342 }
343 
344 //---------------------------------------------------------------
345 
operator <<(string v)346 TOStream &TOStream::operator<<(string v) {
347   ostream &os = *(m_imp->m_os);
348   int len     = v.length();
349   if (len == 0) {
350     os << "\"\""
351        << " ";
352     m_imp->m_justStarted = false;
353     return *this;
354   }
355   int i;
356   for (i = 0; i < len; i++)
357     if ((!iswalnum(v[i]) && v[i] != '_' && v[i] != '%') ||
358         v[i] < 32      // Less than ASCII for SPACE
359         || v[i] > 126  // Greater than ASCII for ~
360         )
361       break;
362   if (i == len)
363     os << v << " ";
364   else {
365     os << '"' << escape(v) << '"';
366   }
367   m_imp->m_justStarted = false;
368   return *this;
369 }
370 
371 //---------------------------------------------------------------
372 
operator <<(QString _v)373 TOStream &TOStream::operator<<(QString _v) {
374   string v = _v.toStdString();
375 
376   ostream &os = *(m_imp->m_os);
377   int len     = v.length();
378   if (len == 0) {
379     os << "\"\""
380        << " ";
381     m_imp->m_justStarted = false;
382     return *this;
383   }
384   int i;
385   for (i = 0; i < len; i++)
386     if ((!iswalnum(v[i]) && v[i] != '_' && v[i] != '%') ||
387         v[i] < 32      // Less than ASCII for SPACE
388         || v[i] > 126  // Greater than ASCII for ~
389         )
390       break;
391   if (i == len)
392     os << v << " ";
393   else {
394     os << '"' << escape(v) << '"';
395   }
396   m_imp->m_justStarted = false;
397   return *this;
398 }
399 
400 //---------------------------------------------------------------
401 
operator <<(std::wstring v)402 TOStream &TOStream::operator<<(std::wstring v) {
403   return operator<<(::to_string(v));
404   /*
405 ostream &os = *(m_imp->m_os);
406 int len = v.length();
407 if(len==0)
408 {
409 os << "\"" << "\"" << " ";
410 m_imp->m_justStarted = false;
411 return *this;
412 }
413 int i;
414 for(i=0;i<len;i++)
415 if(!iswalnum(v[i]) && v[i]!=L'_')
416 break;
417 if(i==len)
418 {
419 os << v;
420 os << " ";
421 }
422 else
423 {
424 os.put('"');
425 for(i=0;i<len;i++)
426  if(iswalnum(v[i]))
427    os.put((char)v[i]);
428  else if(v[i]=='"')
429    os << "\\\"";
430  else if(v[i]=='\n')
431    os << "\\n";
432  else if(iswprint(v[i]))
433    os << v[i];
434  else
435    {os.put('\\'); os << (int)v[i];}
436 os << "\" ";
437 }
438 m_imp->m_justStarted = false;
439 return *this;
440 */
441 }
442 
443 //---------------------------------------------------------------
444 
operator <<(const TFilePath & v)445 TOStream &TOStream::operator<<(const TFilePath &v) {
446   return operator<<(v.getWideString());
447 }
448 
449 //---------------------------------------------------------------
450 
operator <<(const TPixel32 & v)451 TOStream &TOStream::operator<<(const TPixel32 &v) {
452   ostream &os = *(m_imp->m_os);
453   os << (int)v.r << " " << (int)v.g << " " << (int)v.b << " " << (int)v.m
454      << " ";
455   m_imp->m_justStarted = false;
456   return *this;
457 }
458 
459 //---------------------------------------------------------------
460 
operator <<(const TPixel64 & v)461 TOStream &TOStream::operator<<(const TPixel64 &v) {
462   ostream &os = *(m_imp->m_os);
463   os << (int)v.r << " " << (int)v.g << " " << (int)v.b << " " << (int)v.m
464      << " ";
465   m_imp->m_justStarted = false;
466   return *this;
467 }
468 
469 //---------------------------------------------------------------
470 
cr()471 void TOStream::cr() {
472   *(m_imp->m_os) << endl;
473   for (int i = 0; i < m_imp->m_tab; i++) *(m_imp->m_os) << "  ";
474   m_imp->m_justStarted = false;
475 }
476 
477 //---------------------------------------------------------------
478 
operator bool() const479 TOStream::operator bool() const { return (m_imp->m_os && *m_imp->m_os); }
480 
481 //---------------------------------------------------------------
child(string tagName)482 TOStream TOStream::child(string tagName) {
483   assert(tagName != "");
484   m_imp->m_tagStack.push_back(tagName);
485   return TOStream(m_imp);
486 }
487 
488 //---------------------------------------------------------------
489 
openChild(string tagName)490 void TOStream::openChild(string tagName) {
491   assert(tagName != "");
492   m_imp->m_tagStack.push_back(tagName);
493   if (m_imp->m_justStarted == false) cr();
494   *(m_imp->m_os) << "<" << m_imp->m_tagStack.back() << ">";
495   m_imp->m_tab++;
496   cr();
497   m_imp->m_justStarted = true;
498 }
499 
500 //---------------------------------------------------------------
501 
openChild(string tagName,const map<std::string,string> & attributes)502 void TOStream::openChild(string tagName,
503                          const map<std::string, string> &attributes) {
504   assert(tagName != "");
505   m_imp->m_tagStack.push_back(tagName);
506   if (m_imp->m_justStarted == false) cr();
507   *(m_imp->m_os) << "<" << m_imp->m_tagStack.back();
508   for (std::map<std::string, string>::const_iterator it = attributes.begin();
509        it != attributes.end(); ++it) {
510     *(m_imp->m_os) << " " << it->first << "=\"" << escape(it->second) << "\"";
511   }
512   *(m_imp->m_os) << ">";
513   m_imp->m_tab++;
514   cr();
515   m_imp->m_justStarted = true;
516 }
517 
518 //---------------------------------------------------------------
519 
closeChild()520 void TOStream::closeChild() {
521   assert(!m_imp->m_tagStack.empty());
522   string tagName = m_imp->m_tagStack.back();
523   m_imp->m_tagStack.pop_back();
524   assert(tagName != "");
525   // ostream &os = *m_imp->m_os; //os non e' usato
526   m_imp->m_tab--;
527   if (!m_imp->m_justStarted) cr();
528   *(m_imp->m_os) << "</" << tagName << ">";
529   cr();
530   m_imp->m_justStarted = true;
531 }
532 
533 //---------------------------------------------------------------
534 
openCloseChild(string tagName,const map<std::string,string> & attributes)535 void TOStream::openCloseChild(string tagName,
536                               const map<std::string, string> &attributes) {
537   assert(tagName != "");
538   // m_imp->m_tagStack.push_back(tagName);
539   if (m_imp->m_justStarted == false) cr();
540   *(m_imp->m_os) << "<" << tagName;
541   for (std::map<std::string, string>::const_iterator it = attributes.begin();
542        it != attributes.end(); ++it) {
543     *(m_imp->m_os) << " " << it->first << "=\"" << escape(it->second) << "\"";
544   }
545   *(m_imp->m_os) << "/>";
546   // m_imp->m_tab++;
547   cr();
548   m_imp->m_justStarted = true;
549 }
550 
551 //---------------------------------------------------------------
552 
operator <<(TPersist & v)553 TOStream &TOStream::operator<<(TPersist &v) {
554   v.saveData(*this);
555   return *this;
556 }
557 
558 //---------------------------------------------------------------
559 
operator <<(TPersist * v)560 TOStream &TOStream::operator<<(TPersist *v) {
561   Imp::PersistTable::iterator it = m_imp->m_table.find(v);
562   if (it != m_imp->m_table.end()) {
563     *(m_imp->m_os) << "<" << v->getStreamTag() << " id='" << it->second
564                    << "'/>";
565     m_imp->m_justStarted = false;
566   } else {
567     int id            = ++m_imp->m_maxId;
568     m_imp->m_table[v] = id;
569     *(m_imp->m_os) << "<" << v->getStreamTag() << " id='" << id << "'>";
570     m_imp->m_tab++;
571     cr();
572     v->saveData(*this);
573     m_imp->m_tab--;
574     cr();
575     *(m_imp->m_os) << "</" << v->getStreamTag() << ">";
576     cr();
577   }
578   return *this;
579 }
580 
581 //---------------------------------------------------------------
582 
checkStatus() const583 bool TOStream::checkStatus() const {
584   if (!m_imp->m_os) return false;
585 
586   m_imp->m_os->flush();
587   return m_imp->m_os->rdstate() == ios_base::goodbit;
588 }
589 
getCurrentTagName()590 std::string TOStream::getCurrentTagName() {
591   return (m_imp->m_tagStack.empty()) ? "" : m_imp->m_tagStack.back();
592 }
593 
594 //===============================================================
595 /*!
596         This class contains TIStream's attributes.
597         It is created by memory allocation in the TIStream's constructor.
598 */
599 class TIStream::Imp {
600 public:
601   istream *m_is;
602   bool m_chanOwner;
603   int m_line;
604   string m_strbuffer;
605   bool m_compressed;
606 
607   vector<std::string> m_tagStack;
608 
609   typedef map<int, TPersist *> PersistTable;
610   PersistTable m_table;
611 
612   StreamTag m_currentTag;
613 
614   TFilePath m_filepath;
615 
616   VersionNumber m_versionNumber;
617 
Imp()618   Imp()
619       : m_is(0)
620       , m_chanOwner(false)
621       , m_line(0)
622       , m_compressed(false)
623       , m_versionNumber(0, 0) {}
624 
625   // update m_line if necessary; returns -e if eof
626   int getNextChar();
627 
628   inline void skipBlanks();
629   bool matchTag();
630   inline bool match(char c);
631   bool matchIdent(string &ident);
632   bool matchValue(string &value);
633 
634   void skipCurrentTag();
635 };
636 
637 //---------------------------------------------------------------
638 
getFilePath()639 TFilePath TIStream::getFilePath() { return m_imp->m_filepath; }
640 
641 //---------------------------------------------------------------
642 
getRepositoryPath()643 TFilePath TIStream::getRepositoryPath() {
644   TFilePath fp = m_imp->m_filepath;
645   return fp.getParentDir() + (fp.getName() + "_files");
646 }
647 
648 //---------------------------------------------------------------
649 
getNextChar()650 int TIStream::Imp::getNextChar() {
651   char c;
652   m_is->get(c);
653   if (m_is->eof()) return -1;
654   if (c == '\r') m_line++;
655   return c;
656 }
657 
658 //---------------------------------------------------------------
659 
skipBlanks()660 void TIStream::Imp::skipBlanks() {
661   istream &is = *m_is;
662   istream::int_type c;
663   while (c = is.peek(), (isspace(c) || c == '\r')) getNextChar();
664 }
665 
666 //---------------------------------------------------------------
667 
match(char c)668 bool TIStream::Imp::match(char c) {
669   if (m_is->peek() == c) {
670     getNextChar();
671     return true;
672   } else
673     return false;
674 }
675 
676 //---------------------------------------------------------------
677 
matchIdent(string & ident)678 bool TIStream::Imp::matchIdent(string &ident) {
679   istream &is = *m_is;
680   if (!isalnum(is.peek())) return false;
681   ident = "";
682   char c;
683   is.get(c);
684   ident.append(1, c);
685   while (c = is.peek(), isalnum(c) || c == '_' || c == '.' || c == '-') {
686     is.get(c);
687     ident.append(1, c);
688   }
689   return true;
690 }
691 
692 //---------------------------------------------------------------
693 
matchValue(string & str)694 bool TIStream::Imp::matchValue(string &str) {
695   istream &is = *m_is;
696   char quote  = is.peek();
697   char c;
698   if (!is || (quote != '\'' && quote != '\"')) return false;
699   is.get(c);
700   str = "";
701   for (;;) {
702     is.get(c);
703     if (!is) throw TException("expected '\"'");
704     if (c == quote) break;
705     if (c == '\\') {
706       is.get(c);
707       if (!is) throw TException("unexpected EOF");
708       if (c != '\'' && c != '\"' && c != '\\')
709         throw TException("bad escape sequence");
710     }
711     str.append(1, c);
712   }
713   if (c != quote) throw TException("missing '\"'");
714   return true;
715 }
716 
717 //---------------------------------------------------------------
718 
matchTag()719 bool TIStream::Imp::matchTag() {
720   if (m_currentTag) return true;
721   StreamTag &tag = m_currentTag;
722   tag            = StreamTag();
723   skipBlanks();
724   if (!match('<')) return false;
725   skipBlanks();
726   if (match('!')) {
727     skipBlanks();
728     if (!match('-') || !match('-')) throw TException("expected '<!--' tag");
729     istream &is = *m_is;
730     char c;
731     int status = 1;
732     while (status != 0 && is.get(c)) switch (status) {
733       case 1:
734         if (c == '-') status = 2;
735         break;
736       case 2:
737         if (c == '-')
738           status = 3;
739         else
740           status = 1;
741         break;
742       case 3:
743         if (c == '>')
744           status = 0;
745         else if (c == '-') {
746         } else
747           status = 1;
748         break;
749       }
750     return matchTag();
751   }
752   if (match('/')) {
753     tag.m_type = StreamTag::EndTag;
754     skipBlanks();
755   }
756 
757   if (!matchIdent(tag.m_name)) throw TException("expected identifier");
758   skipBlanks();
759   for (;;) {
760     if (match('>')) break;
761     if (match('/')) {
762       tag.m_type = StreamTag::BeginEndTag;
763       skipBlanks();
764       if (match('>')) break;
765       throw TException("expected '>'");
766     }
767     string name;
768     if (!matchIdent(name)) throw TException("expected identifier");
769     skipBlanks();
770     if (match('=')) {
771       string value;
772       skipBlanks();
773       if (!matchValue(value)) throw TException("expected value");
774       tag.m_attributes[name] = value;
775       skipBlanks();
776     }
777   }
778   return true;
779 }
780 
781 //---------------------------------------------------------------
782 
skipCurrentTag()783 void TIStream::Imp::skipCurrentTag() {
784   if (m_currentTag.m_type == StreamTag::BeginEndTag) return;
785   istream &is = *m_is;
786   int level   = 1;
787   int c;
788   for (;;) {
789     if (is.eof()) break;  // unexpected eof
790     c = is.peek();
791     if (c != '<') {
792       getNextChar();
793       continue;
794     }
795 
796     // tag found
797     c = getNextChar();
798     if (c < 0) break;
799 
800     c = getNextChar();
801     if (c < 0) break;
802 
803     if (c == '/') {
804       // end tag
805       do
806         c = getNextChar();
807       while (c >= 0 && c != '>');
808       if (c < 0) break;  // unexpected eof
809       if (--level <= 0) {
810         // m_currentTag.m_type = StreamTag::EndTag;
811         m_tagStack.pop_back();
812         m_currentTag = StreamTag();
813         break;
814       }
815     } else {
816       // tag
817       int oldC;
818       do {
819         oldC = c;
820         c    = getNextChar();
821       } while (c >= 0 && c != '>');
822       if (c < 0) break;  // unexpected eof
823       if (oldC != '/') level++;
824     }
825   }
826 }
827 
828 //---------------------------------------------------------------
829 
TIStream(const TFilePath & fp)830 TIStream::TIStream(const TFilePath &fp) : m_imp(new Imp) {
831   m_imp->m_filepath = fp;
832   m_imp->m_is       = new Tifstream(fp);
833 
834   if (m_imp->m_is->peek() == 'T')  // non comincia con '<' dev'essere compresso
835   {
836     bool swapForEndianess = false;
837 
838     unique_ptr<std::istream> is(m_imp->m_is);
839     m_imp->m_is = 0;
840 
841     char magicBuffer[4];
842     is->read(magicBuffer, 4);
843     string magic(magicBuffer, 4);
844     size_t in_len, out_len;
845 
846     if (magic == "TNZC") {
847       // Tab3.0 beta
848       is->read((char *)&out_len, sizeof out_len);
849       is->read((char *)&in_len, sizeof in_len);
850     } else if (magic == "TABc") {
851       TINT32 v;
852       is->read((char *)&v, sizeof v);
853       printf("magic = %08X\n", v);
854 
855       if (v == 0x0A0B0C0D)
856         swapForEndianess = false;
857       else if (v == 0x0D0C0B0A)
858         swapForEndianess = true;
859       else {
860         swapForEndianess = true;
861         printf("UH OH!\n");
862       }
863 
864       is->read((char *)&v, sizeof v);
865       out_len = swapForEndianess ? swapTINT32(v) : v;
866       is->read((char *)&v, sizeof v);
867       in_len = swapForEndianess ? swapTINT32(v) : v;
868     } else
869       throw TException("Bad magic number");
870 
871     if (in_len <= 0 || in_len > 100000000)  // 100M di tnzfile (compresso)
872                                             // sembrano proprio esagerati
873       throw TException("Corrupted file");
874 
875     LZ4F_decompressionContext_t lz4dctx;
876 
877     LZ4F_errorCode_t err =
878         LZ4F_createDecompressionContext(&lz4dctx, LZ4F_VERSION);
879     if (LZ4F_isError(err)) throw TException("Couldn't decompress file");
880 
881     char *in = (char *)malloc(in_len);
882     is->read((char *)in, in_len);
883 
884     m_imp->m_strbuffer.resize(out_len + 1000);  // per prudenza
885     char *out = (char *)m_imp->m_strbuffer.c_str();
886 
887     size_t check_len = out_len;
888 
889     // size_t remaining = LZ4F_decompress(lz4dctx, out, &out_len, in, &in_len,
890     // NULL);
891     bool ok = lz4decompress(lz4dctx, out, &out_len, in, in_len);
892     LZ4F_freeDecompressionContext(lz4dctx);
893 
894     free(in);
895 
896     if (!ok) throw TException("Couldn't decompress file");
897 
898     if (check_len != out_len) throw TException("corrupted file");
899 
900     m_imp->m_is = new istringstream(std::string(out, out_len));
901   }
902 
903   m_imp->m_chanOwner = true;
904 }
905 
906 //---------------------------------------------------------------
907 
908 /*
909 TIStream::TIStream(istream &is)
910          : m_imp(new Imp)
911 {
912   m_imp->m_is = &is;
913 }
914 */
915 
916 //---------------------------------------------------------------
917 
~TIStream()918 TIStream::~TIStream() {
919   if (m_imp->m_chanOwner) delete m_imp->m_is;
920 }
921 
922 //---------------------------------------------------------------
923 
operator >>(int & v)924 TIStream &TIStream::operator>>(int &v) {
925   *(m_imp->m_is) >> v;
926   return *this;
927 }
928 
929 //---------------------------------------------------------------
930 
operator >>(double & v)931 TIStream &TIStream::operator>>(double &v) {
932   *(m_imp->m_is) >> v;
933   return *this;
934 }
935 //---------------------------------------------------------------
936 
operator >>(std::wstring & v)937 TIStream &TIStream::operator>>(std::wstring &v) {
938   string s;
939   operator>>(s);
940   v = ::to_wstring(s);
941   return *this;
942 }
943 
944 //---------------------------------------------------------------
945 
operator >>(string & v)946 TIStream &TIStream::operator>>(string &v) {
947   istream &is = *(m_imp->m_is);
948   v           = "";
949   m_imp->skipBlanks();
950   char c;
951   is.get(c);
952   if (c == '\"') {
953     is.get(c);
954     while (is && c != '"') {
955       if (c == '\\') {
956         is.get(c);
957         if (!is) throw TException("unexpected EOF");
958         if (c == '"')
959           v.append(1, '"');
960         else if (c == '\\')
961           v.append(1, '\\');
962         else if (c == '\'')
963           v.append(1, '\'');
964         else {
965           v.append(1, '\\');
966           v.append(1, c);
967         }
968       } else
969         v.append(1, c);
970       is.get(c);
971     }
972   } else {
973     v.append(1, c);
974     while (c = is.peek(), isalnum(c) || c == '_' || c == '&' || c == '#' ||
975                               c == ';' || c == '%') {
976       is.get(c);
977       v.append(1, c);
978     }
979   }
980 
981   return *this;
982 }
983 
984 //---------------------------------------------------------------
985 
operator >>(QString & v)986 TIStream &TIStream::operator>>(QString &v) {
987   istream &is = *(m_imp->m_is);
988   v           = "";
989   m_imp->skipBlanks();
990   char c;
991   is.get(c);
992   if (c == '\"') {
993     is.get(c);
994     while (is && c != '"') {
995       if (c == '\\') {
996         is.get(c);
997         if (!is) throw TException("unexpected EOF");
998         if (c == '"')
999           v.append('"');
1000         else if (c == '\\')
1001           v.append('\\');
1002         else if (c == '\'')
1003           v.append('\'');
1004         else {
1005           v.append('\\');
1006           v.append(c);
1007         }
1008       } else
1009         v.append(c);
1010       is.get(c);
1011     }
1012   } else {
1013     v.append(c);
1014     while (c = is.peek(), isalnum(c) || c == '_' || c == '&' || c == '#' ||
1015                               c == ';' || c == '%') {
1016       is.get(c);
1017       v.append(c);
1018     }
1019   }
1020 
1021   return *this;
1022 }
1023 
1024 //---------------------------------------------------------------
1025 
getString()1026 string TIStream::getString() {
1027   istream &is = *(m_imp->m_is);
1028   string v    = "";
1029   m_imp->skipBlanks();
1030   char c = is.peek();
1031   while (c != '<') {
1032     is.get(c);
1033     c = is.peek();
1034     if (!is) throw TException("unexpected EOF");
1035     v.append(1, c);
1036   }
1037   return v;
1038 }
1039 
1040 //---------------------------------------------------------------
1041 
operator >>(TPixel32 & v)1042 TIStream &TIStream::operator>>(TPixel32 &v) {
1043   istream &is = *(m_imp->m_is);
1044   int r, g, b, m;
1045   is >> r;
1046   is >> g;
1047   is >> b;
1048   is >> m;
1049   v.r = r;
1050   v.g = g;
1051   v.b = b;
1052   v.m = m;
1053   return *this;
1054 }
1055 //---------------------------------------------------------------
1056 
operator >>(TPixel64 & v)1057 TIStream &TIStream::operator>>(TPixel64 &v) {
1058   istream &is = *(m_imp->m_is);
1059   int r, g, b, m;
1060   is >> r;
1061   is >> g;
1062   is >> b;
1063   is >> m;
1064   v.r = r;
1065   v.g = g;
1066   v.b = b;
1067   v.m = m;
1068   return *this;
1069 }
1070 
1071 //---------------------------------------------------------------
1072 
operator >>(TFilePath & v)1073 TIStream &TIStream::operator>>(TFilePath &v) {
1074   istream &is = *(m_imp->m_is);
1075   string s;
1076   char c;
1077   m_imp->skipBlanks();
1078   is.get(c);
1079   if (c == '"') {
1080     is.get(c);
1081     bool escapeChar = false;
1082     // If processing double-quote ("), if it's escaped, keep reading.
1083     while (is && (c != '"' || escapeChar)) {
1084       // if(c=='\\')
1085       //   is.get(c);
1086       if (c == '\\' && !escapeChar)
1087         escapeChar = true;
1088       else
1089         escapeChar = false;
1090       s.append(1, c);
1091       is.get(c);
1092     }
1093   } else {
1094     // il filepath non e' fra virgolette:
1095     // puo' contenere solo caratteri alfanumerici, % e _
1096     s.append(1, c);
1097     while (is) {
1098       c = is.peek();
1099       if (!isalnum(c) && c != '%' && c != '_') break;
1100       is.get(c);
1101       s.append(1, c);
1102     }
1103   }
1104 
1105   v = TFilePath(s);
1106   return *this;
1107 }
1108 //---------------------------------------------------------------
1109 
operator >>(TPersist & v)1110 TIStream &TIStream::operator>>(TPersist &v) {
1111   v.loadData(*this);
1112   return *this;
1113 }
1114 
1115 //---------------------------------------------------------------
1116 
operator >>(TPersist * & v)1117 TIStream &TIStream::operator>>(TPersist *&v) {
1118   if (!m_imp->matchTag() || m_imp->m_currentTag.m_type == StreamTag::EndTag) {
1119     throw TException("expected begin tag");
1120   }
1121   StreamTag tag       = m_imp->m_currentTag;
1122   m_imp->m_currentTag = StreamTag();
1123   string tagName      = tag.m_name;
1124   std::map<std::string, string>::iterator it;
1125   int id = -1;
1126   it     = tag.m_attributes.find("id");
1127   if (it != tag.m_attributes.end()) id = atoi(it->second.c_str());
1128   // cout << "tagname = " << tagName << " id = " << id << endl;
1129 
1130   Imp::PersistTable::iterator pit = m_imp->m_table.find(id);
1131   if (pit == m_imp->m_table.end()) {
1132     v = TPersistFactory::instance()->create(tagName);
1133     if (!v) throw TException("unable to create a persistent '" + tagName + "'");
1134     m_imp->m_table[id] = v;
1135     if (tag.m_type != StreamTag::BeginTag)
1136       throw TException("expected begin tag");
1137     m_imp->m_tagStack.push_back(tag.m_name);
1138     v->loadData(*this);
1139     m_imp->matchTag();
1140     if (!m_imp->m_currentTag || m_imp->m_currentTag.m_type != StreamTag::EndTag)
1141       throw TException("expected end tag");
1142     if (m_imp->m_currentTag.m_name != m_imp->m_tagStack.back())
1143       throw TException("end tag mismatch");
1144     m_imp->m_tagStack.pop_back();
1145     m_imp->m_currentTag = StreamTag();
1146   } else {
1147     v = pit->second;
1148     if (tag.m_type != StreamTag::BeginEndTag)
1149       throw TException("expected begin/end tag");
1150   }
1151   return *this;
1152 }
1153 
1154 //---------------------------------------------------------------
1155 
matchEndTag()1156 bool TIStream::matchEndTag() {
1157   if (m_imp->m_tagStack.empty()) throw TException("tag stack emtpy");
1158   if (!m_imp->matchTag()) return false;
1159   if (m_imp->m_currentTag.m_type != StreamTag::EndTag) return false;
1160   if (m_imp->m_currentTag.m_name != m_imp->m_tagStack.back())
1161     throw TException("end tag mismatch");
1162   m_imp->m_tagStack.pop_back();
1163   m_imp->m_currentTag = StreamTag();
1164   return true;
1165 }
1166 
1167 //---------------------------------------------------------------
1168 
eos()1169 bool TIStream::eos() {
1170   if (m_imp->matchTag())
1171     return m_imp->m_currentTag.m_type == StreamTag::EndTag;
1172   else
1173     return !(*m_imp->m_is);
1174 }
1175 
1176 //---------------------------------------------------------------
1177 
matchTag(string & tagName)1178 bool TIStream::matchTag(string &tagName) {
1179   if (!m_imp->matchTag()) return false;
1180   if (m_imp->m_currentTag.m_type == StreamTag::EndTag) return false;
1181   tagName                    = m_imp->m_currentTag.m_name;
1182   m_imp->m_currentTag.m_name = "";
1183   if (m_imp->m_currentTag.m_type != StreamTag::BeginEndTag)
1184     m_imp->m_tagStack.push_back(tagName);
1185   return true;
1186 }
1187 
1188 //---------------------------------------------------------------
1189 
getTagAttribute(string name) const1190 string TIStream::getTagAttribute(string name) const {
1191   StreamTag &tag = m_imp->m_currentTag;
1192   std::map<std::string, string>::const_iterator it =
1193       tag.m_attributes.find(name);
1194   if (it == tag.m_attributes.end())
1195     return "";
1196   else
1197     return it->second;
1198 }
1199 
1200 //---------------------------------------------------------------
1201 
getTagParam(string paramName,string & value)1202 bool TIStream::getTagParam(string paramName, string &value) {
1203   if (m_imp->m_tagStack.empty()) return false;
1204   StreamTag &tag = m_imp->m_currentTag;
1205   std::map<std::string, string>::const_iterator it =
1206       tag.m_attributes.find(paramName);
1207   if (it == tag.m_attributes.end()) return false;
1208   value = it->second;
1209   return true;
1210 }
1211 
1212 //---------------------------------------------------------------
1213 
getTagParam(string paramName,int & value)1214 bool TIStream::getTagParam(string paramName, int &value) {
1215   string svalue;
1216   if (!getTagParam(paramName, svalue)) return false;
1217   istringstream is(svalue);
1218   value = 0;
1219   is >> value;
1220   return true;
1221 }
1222 
1223 //---------------------------------------------------------------
1224 
isBeginEndTag()1225 bool TIStream::isBeginEndTag() {
1226   return m_imp->m_currentTag.m_type == StreamTag::BeginEndTag;
1227 }
1228 
1229 //---------------------------------------------------------------
1230 
openChild(string & tagName)1231 bool TIStream::openChild(string &tagName) {
1232   if (!m_imp->matchTag()) return false;
1233   if (m_imp->m_currentTag.m_type != StreamTag::BeginTag) return false;
1234   tagName                    = m_imp->m_currentTag.m_name;
1235   m_imp->m_currentTag.m_name = "";
1236   m_imp->m_tagStack.push_back(tagName);
1237   return true;
1238 }
1239 
1240 //---------------------------------------------------------------
1241 
closeChild()1242 void TIStream::closeChild() {
1243   if (!matchEndTag()) {
1244     string tagName;
1245     if (!m_imp->m_tagStack.empty()) tagName = m_imp->m_tagStack.back();
1246     if (tagName != "")
1247       throw TException("Expected \"" + tagName + "\" end tag");
1248     else
1249       throw TException("expected EndTag");
1250   }
1251 }
1252 
1253 //---------------------------------------------------------------
1254 
match(char c) const1255 bool TIStream::match(char c) const {
1256   m_imp->skipBlanks();
1257   if (m_imp->m_is->peek() != c) return false;
1258   m_imp->m_is->get(c);
1259   if (c == '\r') m_imp->m_line++;
1260   return true;
1261 }
1262 
1263 //---------------------------------------------------------------
1264 
operator bool() const1265 TIStream::operator bool() const { return (m_imp->m_is && *m_imp->m_is); }
1266 
1267 //---------------------------------------------------------------
1268 
getLine() const1269 int TIStream::getLine() const { return m_imp->m_line + 1; }
1270 
1271 //---------------------------------------------------------------
1272 
getVersion() const1273 VersionNumber TIStream::getVersion() const { return m_imp->m_versionNumber; }
1274 
1275 //---------------------------------------------------------------
1276 
setVersion(const VersionNumber & version)1277 void TIStream::setVersion(const VersionNumber &version) {
1278   m_imp->m_versionNumber = version;
1279 }
1280 
1281 //---------------------------------------------------------------
1282 
skipCurrentTag()1283 void TIStream::skipCurrentTag() { m_imp->skipCurrentTag(); }
1284 
1285 //---------------------------------------------------------------
1286 
getCurrentTagName()1287 std::string TIStream::getCurrentTagName() { return m_imp->m_tagStack.back(); }