Skip to content

Instantly share code, notes, and snippets.

@ariajanke
Created February 14, 2022 17:05
Show Gist options
  • Save ariajanke/5f18c8902e5b819ac1bbbaf911fd8117 to your computer and use it in GitHub Desktop.
Save ariajanke/5f18c8902e5b819ac1bbbaf911fd8117 to your computer and use it in GitHub Desktop.
/****************************************************************************
MIT License
Copyright (c) 2021 Aria Janke
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*****************************************************************************/
// Just something I'm working on
// You're free to snoop in on it if you wish
//
// Just be warned though, it's hardly tested n.-
#include <iostream>
#include <fstream>
#include <limits>
#include <common/StringUtil.hpp>
#include <common/ParseOptions.hpp>
#include <cstring>
#include <cassert>
using namespace cul::exceptions_abbr;
// ----------------------------- program options ------------------------------
struct ProgramOptions {
static constexpr const int k_no_size = -1;
enum class Mode { reset, continue_, invalid };
int job_size = k_no_size;
Mode mode = Mode::invalid;
std::string target_file = "";
};
namespace program_modes {
constexpr const auto k_reset = ProgramOptions::Mode::reset;
constexpr const auto k_continue = ProgramOptions::Mode::continue_;
constexpr const auto k_invalid = ProgramOptions::Mode::invalid;
} // end of program_modes namespace
void parse_job_size(ProgramOptions &, char **, char **);
void mode_is_continue(ProgramOptions &, char **, char **);
void mode_is_reset(ProgramOptions &, char **, char **);
void set_target(ProgramOptions &, char **, char **);
ProgramOptions verify_complete_options(ProgramOptions &&);
// ----------------------------- regular methods ------------------------------
void prepare_fstream(const ProgramOptions &, std::ifstream &);
void write_progress(const ProgramOptions &, std::ifstream & target_file);
template <typename Func>
void do_at_most(int n, Func && f) {
for (int i = 0; i != n; ++i) {
if (!f()) return;
}
}
std::string process_line(const std::string &, std::string && = std::string{});
int main(int argc, char ** argv) {
// still need to add filtering... which is where it gets interesting
using std::cout, std::endl, std::cerr;
try {
auto options = verify_complete_options(
cul::parse_options<ProgramOptions>(argc, argv,
{
{ "input" , 'i', set_target },
{ "continue", 'c', mode_is_continue },
{ "reset" , 'r', mode_is_reset },
{ "job_size", 'j', parse_job_size }
}));
std::ifstream fin;
prepare_fstream(options, fin);
std::string line, output, product;
do_at_most(options.job_size, [&fin, &line, &output, &product] {
bool has_line = bool(std::getline(fin, line));
if (has_line) {
product += (output = process_line(line, std::move(output))) + ",\n";
}
return has_line;
});
product.pop_back();
product.pop_back();
product += ";";
cout << product << endl;
write_progress(options, fin);
} catch (std::exception & exp) {
cerr << exp.what() << endl;
return ~0;
}
return 0;
}
// ----------------------------------------------------------------------------
constexpr const auto k_progress_file_suffix = "tph-progress";
ProgramOptions & verify_mode_option
(ProgramOptions & opts, char ** beg, char ** end);
inline std::string make_progress_file(const std::string & target)
{ return target + "." + k_progress_file_suffix; }
inline bool is_tab(char c) { return c == '\t'; }
void parse_job_size(ProgramOptions & opts, char ** beg, char ** end) {
static constexpr const auto k_one_arg_msg = "Job size takes exactly one numeric argument.";
if (opts.job_size != ProgramOptions::k_no_size) {
throw InvArg("Job size may only be specified once.");
} else if (end - beg != 1) {
throw InvArg(k_one_arg_msg);
} else if (/* conditional has side effects when true returned */
!cul::string_to_number(*beg, *beg + strlen(*beg), opts.job_size))
{
throw InvArg(k_one_arg_msg + std::string(":\"") + (*beg) + "\" is not numeric.");
}
}
void mode_is_continue(ProgramOptions & opts, char ** beg, char ** end) {
using namespace program_modes;
verify_mode_option(opts, beg, end).mode = k_continue;
}
void mode_is_reset(ProgramOptions & opts, char ** beg, char ** end) {
using namespace program_modes;
verify_mode_option(opts, beg, end).mode = k_reset;
}
void set_target(ProgramOptions & opts, char ** beg, char ** end) {
if (end - beg != 1) {
throw InvArg("May only specify one target file.");
}
opts.target_file = *beg;
}
ProgramOptions verify_complete_options(ProgramOptions && opts) {
if (opts.mode == program_modes::k_invalid) {
throw RtError("Mode (reset/continue) must be specified.");
} else if (opts.job_size == ProgramOptions::k_no_size) {
throw RtError("Job size must be specified.");
} else if (opts.target_file.empty()) {
throw RtError("Target file must be specified.");
}
return std::move(opts);
}
void prepare_fstream(const ProgramOptions & opts, std::ifstream & fin) {
using namespace program_modes;
fin.open(opts.target_file.c_str());
fin.exceptions(std::ifstream::badbit);
switch (opts.mode) {
case k_continue: {
std::ifstream progress_file( make_progress_file(opts.target_file).c_str() );
progress_file.exceptions(std::ifstream::badbit);
std::size_t last_position;
if (!(progress_file >> last_position)) {
throw RtError("Failed to read progress file.");
}
fin.seekg(last_position, std::ios_base::beg);
break;
}
case k_reset:
fin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
break;
default: assert(false && "impossible branch");
}
}
void write_progress(const ProgramOptions & opts, std::ifstream & target_file) {
std::size_t position = target_file.tellg();
std::ofstream progress_file( make_progress_file(opts.target_file).c_str() );
progress_file.exceptions(std::ifstream::badbit);
progress_file << position;
}
std::string process_line(const std::string & line, std::string && rv) {
using StrIter = std::string::const_iterator;
static const auto is_numeric = [] (StrIter beg, StrIter end) {
int64_t o;
return cul::string_to_number(beg, end, o);
};
rv.clear();
rv = "(";
cul::for_split<is_tab>(
line.begin(), line.end(), [&rv] (StrIter beg, StrIter end)
{
bool num = is_numeric(beg, end);
if (!num) rv += '\'';
for (auto itr = beg; itr != end; ++itr) {
if (*itr == '\'') rv += '\'';
rv += *itr;
}
if (!num) rv += '\'';
rv += ',';
});
rv.pop_back();
rv += ")";
return std::move(rv);
}
// ----------------------------------------------------------------------------
ProgramOptions & verify_mode_option
(ProgramOptions & opts, char ** beg, char ** end)
{
if (opts.mode != program_modes::k_invalid) {
throw InvArg("Only one mode maybe specified.");
} else if (end - beg != 0) {
throw InvArg("Mode takes no arguments.");
}
return opts;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment