21 : separator_(1, separator), values_(cols) {
25 : separator_(1, separator) {
40 while (prev_pos < line.size()) {
42 sep_pos = line.find_first_of(separator_, prev_pos);
43 if (sep_pos == std::string::npos) {
48 len = sep_pos - prev_pos;
49 values_.push_back(line.substr(prev_pos, len));
52 prev_pos = sep_pos + 1;
56 len = line.size() - prev_pos;
57 values_.push_back(line.substr(prev_pos, len));
74 for (
size_t i = 0; i < values_.size(); ++i) {
98 values_.resize(values_.size() - count);
107CSVRow::checkIndex(
const size_t at)
const {
108 if (at >= values_.size()) {
109 isc_throw(CSVFileError,
"value index '" << at <<
"' of the CSV row"
110 " is out of bounds; maximal index is '"
111 << (values_.size() - 1) <<
"'");
116 : filename_(filename), fs_(), cols_(0), read_msg_() {
135 std::ifstream fs(filename_.c_str());
136 const bool file_exists = fs.good();
138 return (file_exists);
143 checkStreamStatusAndReset(
"flush");
160 if (std::find(cols_.begin(), cols_.end(), col_name) != cols_.end()) {
164 cols_.push_back(col_name);
169 checkStreamStatusAndReset(
"append");
185 fs_->seekp(0, std::ios_base::end);
186 fs_->seekg(0, std::ios_base::end);
189 std::string text = row.
render();
190 *fs_ << text << std::endl;
191 auto sav_err = errno;
193 std::stringstream ss;
194 ss <<
"failed to write CSV row '"
195 << text <<
"' to the file '" << filename_ <<
"'"
196 <<
" fail(): " << fs_->fail()
197 <<
" bad(): " << fs_->bad()
198 <<
" errno: " << sav_err
199 <<
" reason: " << strerror(sav_err);
200 auto error_str = ss.str();
213CSVFile::checkStreamStatusAndReset(
const std::string& operation)
const {
216 << operation <<
"' on file '" << filename_ <<
"'");
218 }
else if (!fs_->is_open()) {
220 isc_throw(CSVFileError,
"closed stream when performing '"
221 << operation <<
"' on file '" << filename_ <<
"'");
229CSVFile::size()
const {
230 std::ifstream fs(filename_.c_str());
238 std::ifstream::pos_type pos;
242 fs.seekg(0, std::ifstream::end);
245 }
catch (
const std::exception&) {
253 for (
size_t i = 0; i < cols_.size(); ++i) {
254 if (cols_[i] == col_name) {
263 if (col_index >= cols_.size()) {
265 " CSV file '" << filename_ <<
"' is out of range; the CSV"
266 " file has only " << cols_.size() <<
" columns ");
268 return (cols_[col_index]);
280 checkStreamStatusAndReset(
"get next row");
289 while (fs_->good() && line.empty()) {
290 std::getline(*fs_, line);
300 }
else if (!fs_->good()) {
303 setReadMsg(
"error reading a row from CSV file '"
304 + std::string(filename_) +
"'");
313 return (skip_validation ?
true :
validate(row));
319 if (size() ==
static_cast<std::streampos
>(0)) {
324 fs_.reset(
new std::fstream(filename_.c_str()));
331 if (!fs_->is_open()) {
339 << filename_ <<
"'");
344 if (!
next(header,
true)) {
346 " CSV file '" << filename_ <<
"': "
353 <<
"' in CSV file '" << filename_ <<
"': "
368 fs_->seekp(0, std::ios_base::end);
369 fs_->seekg(0, std::ios_base::end);
372 " CSV file '" << filename_ <<
"'");
377 }
catch (
const std::exception&) {
390 " created CSV file '" << filename_ <<
"'");
395 fs_.reset(
new std::fstream(filename_.c_str(), std::fstream::out));
396 if (!fs_->is_open()) {
406 *fs_ << header << std::endl;
408 }
catch (
const std::exception& ex) {
420 std::ostringstream s;
421 s <<
"the size of the row '" << row <<
"' doesn't match the number of"
447const std::string CSVRow::escape_tag(
"&#x");
451 auto escape_it = [](
char c,
char s,
char e) ->
bool {
452 return ((c < 0x20) || (c > 0x7e) || c == s || c == e);
457 for (
char c : orig_str) {
458 if (escape_it(c, separator, escape_tag[0])) {
470 esc_str.reserve(orig_str.size() + escapes * (escape_tag.size() + 1));
472 for (
char c : orig_str) {
473 if (escape_it(c, separator, escape_tag[0])) {
474 esc_str.append(escape_tag);
477 esc_str.push_back(c);
487 size_t start_pos = 0;
490 esc_pos = escaped_str.find(escape_tag, start_pos);
491 if (esc_pos == std::string::npos) {
497 std::stringstream ss;
498 while (esc_pos < escaped_str.size()) {
500 ss << escaped_str.substr(start_pos, esc_pos - start_pos);
504 unsigned int escaped_char = 0;
505 bool converted =
true;
506 size_t dig_pos = esc_pos + escape_tag.size();
507 if (dig_pos <= escaped_str.size() - 2) {
508 for (
int i = 0; i < 2; ++i) {
509 uint8_t digit = escaped_str[dig_pos];
510 if (digit >=
'0' && digit <=
'9') {
513 else if (digit >=
'a' && digit <=
'f') {
514 digit = digit -
'a' + 10;
515 }
else if (digit >=
'A' && digit <=
'F') {
516 digit = digit -
'A' + 10;
523 escaped_char = digit << 4;
525 escaped_char |= digit;
534 ss << static_cast<unsigned char>(escaped_char);
541 esc_pos += escape_tag.size();
548 esc_pos = escaped_str.find(escape_tag, start_pos);
551 if (esc_pos == std::string::npos) {
553 ss << escaped_str.substr(start_pos, esc_pos - start_pos);
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
Exception thrown when an error occurs during CSV file processing.
Exception thrown when an unrecoverable error occurs such as disk-full on write.
std::string getColumnName(const size_t col_index) const
Returns the name of the column.
void close()
Closes the CSV file.
size_t getColumnCount() const
Returns the number of columns in the file.
virtual ~CSVFile()
Destructor.
bool exists() const
Checks if the CSV file exists and can be opened for reading.
virtual bool validate(const CSVRow &row)
Validate the row read from a file.
static CSVRow EMPTY_ROW()
Represents empty row.
void setReadMsg(const std::string &read_msg)
Sets error message after row validation.
CSVFile(const std::string &filename)
Constructor.
std::string getFilename() const
Returns the path to the CSV file.
void flush() const
Flushes a file.
virtual bool validateHeader(const CSVRow &header)
This function validates the header of the CSV file.
void addColumnInternal(const std::string &col_name)
Adds a column regardless if the file is open or not.
virtual void recreate()
Creates a new CSV file.
std::string getReadMsg() const
Returns the description of the last error returned by the CSVFile::next function.
void append(const CSVRow &row) const
Writes the CSV row into the file.
void addColumn(const std::string &col_name)
Adds new column name.
size_t getColumnIndex(const std::string &col_name) const
Returns the index of the column having specified name.
virtual void open(const bool seek_to_end=false)
Opens existing file or creates a new one.
bool next(CSVRow &row, const bool skip_validation=false)
Reads next row from CSV file.
Represents a single row of the CSV file.
static std::string escapeCharacters(const std::string &orig_str, const char separator)
Returns a copy of a string with special characters escaped.
std::string render() const
Creates a text representation of the CSV file row.
static std::string unescapeCharacters(const std::string &escaped_str)
Returns a copy of a string with special characters unescaped.
std::string readAtEscaped(const size_t at) const
Retrieves a value from the internal container, free of escaped characters.
size_t getValuesCount() const
Returns number of values in a CSV row.
void trim(const size_t count)
Trims a given number of elements from the end of a row.
CSVRow(const size_t cols=0, const char separator=',')
Constructor, creates the raw to be used for output.
void writeAt(const size_t at, const char *value)
Replaces the value at specified index.
std::string readAt(const size_t at) const
Retrieves a value from the internal container.
void writeAtEscaped(const size_t at, const std::string &value)
Replaces the value at the specified index with a value that has had special characters escaped.
void parse(const std::string &line)
Parse the CSV file row.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
const std::string & byteToHex(uint8_t byte)
Converts a byte to a two hex digit string.
std::ostream & operator<<(std::ostream &os, const CSVRow &row)
Overrides standard output stream operator for CSVRow object.
Defines the logger used by the top-level component of kea-lfc.