diff --git a/include/Filedat.hpp b/include/Filedat.hpp index 46d1e0a..31adc1f 100644 --- a/include/Filedat.hpp +++ b/include/Filedat.hpp @@ -1,15 +1,46 @@ #ifndef FILEDAT_H #define FILEDAT_H -#include "stringTools.hpp" - #include #include #include #include #include -//TESTS NEEDED +#include + +class file_format_error : public std::exception +{ +public: + file_format_error(const std::string& what, const std::string& origin, const char* data, int where) { desc=what; index=where; filename=origin; cdat=data; } + + const char * what () const throw () {return desc.c_str();} + const int where () const throw () {return index;} + const char * data() const throw () {return cdat;} + const char * origin() const throw () {return filename.c_str();} +private: + std::string desc; + int index; + std::string filename; + const char* cdat; +}; + +class chunk_format_error : public std::exception +{ +public: + chunk_format_error(std::string const& what, int where) { desc=what; index=where; } + + const char * what () const throw () {return desc.c_str();} + const int where () const throw () {return index;} +private: + std::string desc; + int index; +}; + +void printFileException(file_format_error& exc); +void printErrorIndex(const char* in, const int index, const std::string& message, const std::string& origin); + +class Filedat; class AbstractChunk { @@ -28,20 +59,30 @@ protected: class Chunk { public: - Chunk() { m_achunk=nullptr; } - Chunk(char* const in) { m_achunk=nullptr; set(std::string(in)); } - Chunk(std::string const& in) { m_achunk=nullptr; set(in); } - Chunk(Chunk const& in) { m_achunk=nullptr; set(in); } + Chunk(const char* in, const int in_size, int offset=0, Filedat* data=nullptr) + { m_achunk=nullptr; set(in, in_size, offset, data);} + Chunk(std::string const& in, int offset=0, Filedat* data=nullptr) + { m_achunk=nullptr; set(in, offset, data);} + + Chunk(Chunk const& in) { m_achunk=nullptr; set(in);} + void clear() { if(m_achunk!=nullptr) delete m_achunk; m_achunk=nullptr; } ~Chunk() { clear(); } - void set(std::string const& in); - void set(Chunk const& in); //TO OPTIMIZE + Filedat* parent() const { return m_parent; } + int offset() const { return m_offset; } + + + void set(const char* in, const int in_size, int offset=0, Filedat* data=nullptr); + void set(std::string const& in, int offset=0, Filedat* data=nullptr) { this->set(in.c_str(), in.size(), offset, data); } + + void set(Chunk const& in) { this->set(in.strval(), in.offset(), in.parent()); } // TODO + std::string strval(unsigned int alignment=0, std::string const& aligner="\t") const; + // bool concatenate(Chunk const& chk); //concatenates chunks bool addToChunk(std::string const& name, Chunk const& val); //adds if datachunk - inline bool addToChunk(std::pair const& pair) { return add(pair.first, pair.second); } //adds if datachunk bool addToChunk(std::vector> const& vec); //adds if datachunk bool addToList(Chunk const& val); //adds if list bool addToList(std::vector const& vec); //adds if list @@ -66,15 +107,19 @@ public: Chunk& operator[](std::string const& a) const { return subChunkRef(a); } Chunk& operator[](unsigned int a) const { return subChunkRef(a); } Chunk& operator=(Chunk const& a) { set(a); return *this; } - inline bool operator+=(std::pair const& a) { return addToChunk(a); } - inline bool operator+=(std::vector> const& a) { return addToChunk(a); } - inline bool operator+=(Chunk const& a) { return addToList(a); } - inline bool operator+=(std::vector const& a) { return addToList(a); } + inline bool operator+=(std::pair const& a) { return add(a); } + inline bool operator+=(std::vector> const& a) { return add(a); } + inline bool operator+=(Chunk const& a) { return add(a); } + inline bool operator+=(std::vector const& a) { return add(a); } // inline bool operator*=(Chunk const& a) { concatenate(a); } //add operator+ and operator* + protected: + Filedat* m_parent; + int m_offset; + AbstractChunk* m_achunk; }; @@ -117,24 +162,37 @@ public: bool readTest() const; - bool importFile(); + void importFile(); bool exportFile(std::string const& path="", std::string const& aligner="\t") const; void clear(); - std::string filePath() const { return m_filePath; } - void setFilePath(std::string const& in) { m_filePath=in; } + inline std::string filePath() const { return m_filePath; } + inline void setFilePath(std::string const& in) { m_filePath=in; } - std::string strval() const; + std::string strval(std::string const& aligner="\t") const; - inline Chunk* pchunk() const { return m_dataChunk; } - inline Chunk& chunk() const { return *m_dataChunk; } + Chunk* pchunk() const { return m_dataChunk; } + Chunk& chunk() const { return *m_dataChunk; } inline Chunk* pdata() const { return m_dataChunk; } inline Chunk& data() const { return *m_dataChunk; } + inline const std::string& stringdata() const { return m_data; } + inline const char* c_data() const { return m_data.c_str(); } + + Chunk& operator[](const std::string& index) + { + return m_dataChunk->subChunkRef(index); + } + Chunk& operator[](const int index) + { + return m_dataChunk->subChunkRef(index); + } + private: std::string m_filePath; + std::string m_data; Chunk* m_dataChunk; }; diff --git a/src/Filedat.cpp b/src/Filedat.cpp index 86aef92..8862c98 100644 --- a/src/Filedat.cpp +++ b/src/Filedat.cpp @@ -2,6 +2,51 @@ #include +bool _isRead(char in) +{ + bool out=false; + if(in>=33 && in<=126) + out=true; + return out; +} + +std::string _repeatString(const std::string& str, const unsigned int n) +{ + std::string ret; + for(unsigned int i=0 ; iclear(); } void Filedat::clear() @@ -39,21 +81,32 @@ bool Filedat::readTest() const return true; } -bool Filedat::importFile() +void Filedat::importFile() { std::ifstream stream(m_filePath); if(!stream) - return false; + { + throw std::runtime_error("Cannot open file '" + m_filePath + '\''); + } + + m_data=""; + std::string line; - std::string str, line; while(stream) { getline(stream, line); - str += (line + '\n'); + m_data += (line + '\n'); } + this->clear(); - m_dataChunk = new Chunk(str); - return true; + try + { + m_dataChunk = new Chunk(m_data.c_str(), m_data.size(), 0, this); + } + catch(chunk_format_error& e) + { + throw file_format_error(e.what(), m_filePath, m_data.c_str(), e.where()); + } } bool Filedat::exportFile(std::string const& path, std::string const& aligner) const @@ -65,61 +118,433 @@ bool Filedat::exportFile(std::string const& path, std::string const& aligner) co stream.open(path); if(!stream) return false; - stream << this->strval(); + stream << this->strval(aligner); return true; } -std::string Filedat::strval() const +std::string Filedat::strval(std::string const& aligner) const { if(m_dataChunk == nullptr) return ""; else - return m_dataChunk->strval(); + return m_dataChunk->strval(0, aligner); } -void Chunk::set(Chunk const& in) +std::string _getname(const char* in, const int in_size, int* start, int* val_size, int* end) { - this->set(in.strval()); + int i=0; + + *start = in_size; //default no value + *end = in_size; //default end + *val_size=0; //default no value + + while(i= in_size) //ends without value + return ""; + + int j=i; //name start + while(i= in_size) //no = + { + throw chunk_format_error("Tag has no value", j); + } + + if(i == j) //nothing preceding = + throw chunk_format_error("Value has no tag", i); + + int k=i-1; //name end + while( !_isRead(in[k]) ) + k--; + std::string name=std::string(in+j, k-j+1); + + i++; + while(i < in_size && !_isRead(in[i])) + i++; + if(i >= in_size) //no value + { + *start=i; + *val_size=0; + *end=i; + return name; + } + if(in[i] == '\"') //"" val + { + i++; + *start=i; //value starts + j=0; //size + while(i+j < in_size && in[i+j]!='\"') + { + if(in[i]+j=='\\') + j++; + j++; + } + if(i+j >= in_size) // no closing " + throw chunk_format_error("Double quote does not close", i-1); + *val_size=j; + *end=i+j+1; + return name; + } + if(in[i] == '\'') //"" val + { + i++; + *start=i; //value starts + j=0; //size + while(i+j < in_size && in[i+j]!='\'') + { + if(in[i]+j=='\\') + j++; + j++; + } + if(i+j >= in_size) // no closing ' + throw chunk_format_error("Single quote does not close", i-1); + *val_size=j; + *end=i+j+1; + return name; + } + if(in[i] == '{') + { + *start=i; + j=1; + int counter=0; + while( i+j < in_size && !( counter == 0 && in[i+j]=='}') ) + { + if(i+j+1= in_size) //reached end without closing + throw chunk_format_error("Brace does not close", i); + j++; + *val_size=j; + *end=i+j; + return name; + } + if(in[i] == '[') + { + *start=i; + j=1; + int counter=0; + while( i+j < in_size && !( counter == 0 && in[i+j]==']') ) + { + if(i+j+1= in_size) //reached end without closing + throw chunk_format_error("Bracket does not close", i); + j++; + *val_size=j; + *end=i+j; + return name; + } + { // no encapsulation: go to end of line + *start=i; //value starts + j=0; //size + while(i+j < in_size && in[i+j]!='\n') + { + if(in[i]+j=='\\') + j++; + j++; + } + while( !_isRead(in[i+j]) ) + j--; + *val_size=j+1; + *end=i+j+1; + return name; + } + + return name; } -void Chunk::set(std::string const& in) +std::string _getlist(const char* in, const int in_size, int* start, int* end) { - this->clear(); - std::string str=ridUnread(in); - if(str[0]=='[') + int i=0; + std::string ret; + + while(i vstr = readList(in); - ChunkList* cl = new ChunkList(); - for(auto it : vstr) - { - cl->list.push_back(new Chunk(it)); - } - m_achunk=cl; + if(i+1= in_size) //ends without value { - str=decapsulate(str); - std::pair pstr; - int ti=0; - DataChunk* dc = new DataChunk(); - while(str.size()>0 && ti>=0) - { - pstr = readValue(str,0,&ti); - if(ti>=0) - { - dc->values.insert(std::make_pair(pstr.first , new Chunk(pstr.second))); - if(ti > (int) str.size()) - throw std::runtime_error("Wrong file format at:\n" + str); - str=str.substr(ti,str.size()-ti); - } - } - m_achunk=dc; + *end = in_size; + return ""; } - else + if(in[i] == ',') //value is empty + { + *end=i+1; + return ""; + } + + int j=0; + if(in[i] == '\"') //"" val + { + i++; + j=0; //size + while(i+j < in_size && in[i+j]!='\"') + { + if(in[i]+j=='\\') + j++; + j++; + } + if(i+j >= in_size) // no closing " + throw chunk_format_error("Double quote does not close", i-1); + ret = std::string(in+i, j); + *end=i+j+1; + } + else if(in[i] == '\'') //"" val + { + i++; + j=0; //size + while(i+j < in_size && in[i+j]!='\'') + { + if(in[i]+j=='\\') + j++; + j++; + } + if(i+j >= in_size) // no closing ' + throw chunk_format_error("Single quote does not close", i-1); + ret = std::string(in+i, j); + *end=i+j+1; + } + else if(in[i] == '{') + { + j=1; + int counter=0; + while( i+j < in_size && !( counter == 0 && in[i+j]=='}') ) + { + if(i+j+1= in_size) //reached end without closing + throw chunk_format_error("Brace does not close", i); + j++; + ret = std::string(in+i, j); + *end=i+j; + } + else if(in[i] == '[') + { + j=1; + int counter=0; + while( i+j < in_size && !( counter == 0 && in[i+j]==']') ) + { + if(i+j+1= in_size) //reached end without closing + throw chunk_format_error("Bracket does not close", i); + j++; + ret = std::string(in+i, j); + *end=i+j; + } + else // no encapsulation: go to next , + { + j=0; //size + while(i+j < in_size && in[i+j]!=',') + { + if(in[i+j]=='\\') + j++; + j++; + } + if(i+j < in_size) + { + while( !_isRead(in[i+j]) ) + j--; + } + ret = std::string(in+i,j); + *end=i+j; + } + + i = *end; + while(i < in_size && !_isRead(in[i])) + i++; + if( i>= in_size ) //last char + { + *end=i; + return ret; + } + else if(in[i] ==',') //comma as expected + { + *end=i+1; + return ret; + } + else //Unexpected char + throw chunk_format_error("Expecting comma", i); + +} + +void Chunk::set(const char* in, const int in_size, int offset, Filedat* data) +{ + this->clear(); //reset everything + this->m_parent=data; + this->m_offset=offset; + + int i=0; + + while(i= in_size) //empty: make an empty strval { DataVal* cv = new DataVal(); - cv->val = in; m_achunk=cv; + cv->val = ""; + return; + } + else if( in[i] == '{') + { + i++; + int val_end=in_size-1; + while(!_isRead(in[val_end])) //skip unread char + val_end--; + if(in[val_end] != '}') + throw chunk_format_error("Expecting closing brace", val_end-1); + + DataChunk* tch = new DataChunk(); + m_achunk = tch; + + std::string name; + std::string val; + while(i < val_end) + { + int start=0; + int _size=0; + int end=0; + + std::string newstr=std::string(in+i, val_end-i); + try + { + name = _getname(newstr.c_str(), newstr.size(), &start, &_size, &end); + val = newstr.substr(start, _size); + } + catch(chunk_format_error& e) + { + throw chunk_format_error(e.what(), e.where()+i); + } + + if( name == "" ) //no more values + break; + + try + { + tch->values.insert(std::make_pair(name, new Chunk(val.c_str(),val.size(), offset + start+i, m_parent) )); + } + catch(chunk_format_error& e) + { + throw chunk_format_error(e.what(), e.where() + start + i ); + } + + i += end; + } + + return; + + } + else if( in[i] == '[') + { + i++; + int val_end=in_size-1; + while(!_isRead(in[val_end])) //skip unread char + val_end--; + if(in[val_end] != ']') + throw chunk_format_error("Expecting closing bracket", val_end-1); + + ChunkList* tch = new ChunkList(); + m_achunk = tch; + + int end=0,start=0; + while( i < val_end ) + { + std::string val; + std::string newstr=std::string(in+i, val_end-i); + try + { + val = _getlist(newstr.c_str(), newstr.size(), &start, &end); + } + catch(chunk_format_error& e) + { + throw chunk_format_error(e.what(), e.where()+i); + } + + try + { + tch->list.push_back(new Chunk(val.c_str(),val.size(), offset + start+i, m_parent) ); + } + catch(chunk_format_error& e) + { + throw chunk_format_error(e.what(), e.where() + start + i ); + } + + i+=end; + } + + return; + + } + else // string value + { + int val_end=in_size; + val_end--; + while(!_isRead(in[val_end])) //skip unread char + val_end--; + + DataVal* tch = new DataVal(); + m_achunk = tch; + + tch->val = std::string(in+i,val_end-i+1); + + return; + } } @@ -175,7 +600,7 @@ bool Chunk::addToList(std::vector const& vec) if(this->type()!=AbstractChunk::chunk && this->type()!=AbstractChunk::none) return false; for(auto it : vec) - this->addToList(it); + this->add(it); return true; } @@ -192,14 +617,14 @@ std::string Chunk::strval(unsigned int alignment, std::string const& aligner) co std::string ret="{\n"; for(auto it : cp->values) { - ret += repeatString(aligner,alignment+1); + ret += _repeatString(aligner,alignment+1); ret += it.first; ret += '='; if(it.second!=nullptr) ret += it.second->strval(alignment+1, aligner); ret += '\n'; } - ret += repeatString(aligner, alignment); + ret += _repeatString(aligner, alignment); ret += '}'; return ret; } @@ -209,13 +634,13 @@ std::string Chunk::strval(unsigned int alignment, std::string const& aligner) co std::string ret="[\n"; for(auto it : lp->list) { - ret += repeatString(aligner, alignment+1); + ret += _repeatString(aligner, alignment+1); if(it!=nullptr) ret += it->strval(alignment+1, aligner); ret += ",\n"; } ret.erase(ret.end()-2); - ret += repeatString(aligner, alignment); + ret += _repeatString(aligner, alignment); ret += ']'; return ret; } @@ -265,21 +690,57 @@ Chunk* Chunk::subChunkPtr(unsigned int a) const Chunk& Chunk::subChunkRef(std::string const& in) const { if(this->type()!=AbstractChunk::chunk) - throw std::runtime_error("Chunk isn't a {}:" + this->strval()); + { + if(m_parent != nullptr) + { + throw file_format_error("Element isn't a {}", m_parent->filePath(), m_parent->c_data(), m_offset ); + } + else + { + throw chunk_format_error("Chunk isn't a {}", m_offset); + } + } DataChunk* dc = dynamic_cast(m_achunk); auto fi = dc->values.find(in); if(fi == dc->values.end()) - throw std::runtime_error("Chunk doesn't have '" + in + "' flag:\n" + this->strval()); + { + if(m_parent != nullptr) + { + throw file_format_error("Chunk doesn't have '" + in + "' flag", m_parent->filePath(), m_parent->c_data(), m_offset ); + } + else + { + throw chunk_format_error("Chunk doesn't have '" + in + "' flag", m_offset ); + } + } return *fi->second; } Chunk& Chunk::subChunkRef(unsigned int a) const { if(this->type()!=AbstractChunk::list) - throw std::runtime_error("Chunk isn't a []:" + this->strval()); + { + if(m_parent != nullptr) + { + throw file_format_error("Element isn't a {}", m_parent->filePath(), m_parent->c_data(), m_offset ); + } + else + { + throw chunk_format_error("Chunk isn't a {}", m_offset); + } + } ChunkList* cl = dynamic_cast(m_achunk); if(a >= cl->list.size()) - throw std::runtime_error("List size is below " + std::to_string(a) + ":\n" + this->strval()); + { + if(m_parent != nullptr) + { + throw file_format_error("List size is below " + std::to_string(a), m_parent->filePath(), m_parent->c_data(), m_offset ); + } + else + { + throw chunk_format_error("List size is below " + std::to_string(a), m_offset ); + } + } return *cl->list[a]; } diff --git a/src/main.cpp b/src/main.cpp index 250751b..ce3cc97 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,8 @@ #include "Filedat.hpp" #include "options.hpp" +#include + int main(int argc, char* argv[]) { @@ -39,25 +41,16 @@ int main(int argc, char* argv[]) } printf("Loading config file '%s'\n", arg[0].c_str()); - bool import_ok; try { - import_ok = file.importFile(); + file.importFile(); - if(import_ok) + for(int i=0 ; iimport_chunk(file.chunk()[i]); - device_list.push_back(newDevice); - printf("Loaded %d commands for device '%s'\n", newDevice->nb_command, newDevice->name.c_str()); - } - } - else - { - fprintf(stderr, "Unknown config file error\n"); - return 2; + Device *newDevice = new Device; + newDevice->import_chunk(file[i]); + device_list.push_back(newDevice); + printf("Loaded %d commands for device '%s'\n", newDevice->nb_command, newDevice->name.c_str()); } printf("Starting scan for devices\n"); @@ -69,6 +62,16 @@ int main(int argc, char* argv[]) } } + catch (file_format_error& e) + { + printErrorIndex(e.data(), e.where(), e.what(), e.origin()); + return 11; + } + catch (chunk_format_error& e) + { + std::cerr << "Chunk Error: " << e.what() << std::endl ; + return 11; + } catch (std::exception& e) { std::cerr << "Exception: " << e.what() << std::endl;