Better handling of bad file format

This commit is contained in:
zawz 2019-07-22 16:20:06 +02:00
parent 4e4225f29d
commit fad215ee3c
3 changed files with 611 additions and 89 deletions

View file

@ -1,15 +1,46 @@
#ifndef FILEDAT_H
#define FILEDAT_H
#include "stringTools.hpp"
#include <string>
#include <vector>
#include <map>
#include <iostream>
#include <fstream>
//TESTS NEEDED
#include <cstring>
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<std::string, Chunk> const& pair) { return add(pair.first, pair.second); } //adds if datachunk
bool addToChunk(std::vector<std::pair<std::string, Chunk>> const& vec); //adds if datachunk
bool addToList(Chunk const& val); //adds if list
bool addToList(std::vector<Chunk> 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<std::string, Chunk> const& a) { return addToChunk(a); }
inline bool operator+=(std::vector<std::pair<std::string, Chunk>> const& a) { return addToChunk(a); }
inline bool operator+=(Chunk const& a) { return addToList(a); }
inline bool operator+=(std::vector<Chunk> const& a) { return addToList(a); }
inline bool operator+=(std::pair<std::string, Chunk> const& a) { return add(a); }
inline bool operator+=(std::vector<std::pair<std::string, Chunk>> const& a) { return add(a); }
inline bool operator+=(Chunk const& a) { return add(a); }
inline bool operator+=(std::vector<Chunk> 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;
};

View file

@ -2,6 +2,51 @@
#include <exception>
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 ; i<n ; i++)
ret += str;
return ret;
}
void printFileException(file_format_error& exc)
{
printErrorIndex(exc.data(), exc.where(), exc.what(), exc.origin());
}
void printErrorIndex(const char* in, const int index, const std::string& message, const std::string& origin)
{
int i=0, j=0; // j: last newline
int line=1; //n: line #
int in_size=strlen(in);
while(i < in_size && i < index)
{
if(in[i] == '\n')
{
line++;
j=i+1;
}
i++;
}
while(i < in_size && in[i]!='\n')
{
i++;
}
std::cerr << origin << ": Error\nLine " << line << " col " << index-j << ": " << message << std::endl;
std::cerr << std::string(in+j, i-j) << std::endl;
std::cerr << _repeatString(" ", index-j) << '^' << std::endl;
}
Filedat::Filedat()
{
m_dataChunk = nullptr;
@ -15,10 +60,7 @@ Filedat::Filedat(std::string const& in)
Filedat::~Filedat()
{
if(m_dataChunk!=nullptr)
{
delete m_dataChunk;
}
this->clear();
}
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)
{
if(i+1<in_size && in[i] == '/' && in[i+1] == '/')
while(i<in_size && in[i] != '\n')
i++;
if(_isRead(in[i]))
break;
i++;
}
if(i >= in_size) //ends without value
return "";
int j=i; //name start
while(i<in_size && in[i] != '=') //skip to =
i++;
if(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 && in[i+j] == '/' && in[i+j+1] == '/')
while(i+j<in_size && in[i+j] != '\n')
j++;
if(in[i+j]=='\\')
j++;
if(in[i+j]=='{')
counter++;
if(in[i+j]=='}')
counter--;
j++;
}
if(i+j >= 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 && in[i+j] == '/' && in[i+j+1] == '/')
while(i+j<in_size && in[i+j] != '\n')
j++;
if(in[i+j]=='\\')
j++;
if(in[i+j]=='[')
counter++;
if(in[i+j]==']')
counter--;
j++;
}
if(i+j >= 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<in_size)
{
std::vector<std::string> 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 && in[i] == '/' && in[i+1] == '/')
while(i<in_size && in[i] != '\n')
i++;
if(_isRead(in[i]))
break;
i++;
}
else if(str[0]=='{')
*start=i;
if(i >= in_size) //ends without value
{
str=decapsulate(str);
std::pair<std::string, std::string> 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 && in[i+j] == '/' && in[i+j+1] == '/')
while(i+j<in_size && in[i+j] != '\n')
j++;
if(in[i+j]=='\\')
j++;
if(in[i+j]=='{')
counter++;
if(in[i+j]=='}')
counter--;
j++;
}
if(i+j >= 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 && in[i+j] == '/' && in[i+j+1] == '/')
while(i+j<in_size && in[i+j] != '\n')
j++;
if(in[i+j]=='\\')
j++;
if(in[i+j]=='[')
counter++;
if(in[i+j]==']')
counter--;
j++;
}
if(i+j >= 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 && !_isRead(in[i])) //skip unread char
i++;
if(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<Chunk> 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<DataChunk*>(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<ChunkList*>(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];
}

View file

@ -7,6 +7,8 @@
#include "Filedat.hpp"
#include "options.hpp"
#include <cstring>
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 ; i<file.chunk().listSize() ; i++)
{
for(int i=0 ; i<file.chunk().listSize() ; i++)
{
Device *newDevice = new Device;
newDevice->import_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;