summaryrefslogtreecommitdiff
path: root/hscript/script.cc
diff options
context:
space:
mode:
authorA. Wilcox <AWilcox@Wilcox-Tech.com>2023-10-10 23:30:18 -0500
committerA. Wilcox <AWilcox@Wilcox-Tech.com>2023-10-10 23:30:18 -0500
commit615278f3365087e436ee5ea13e0d15bd60718038 (patch)
tree9b67e19184a743c0ee3672b6266b281a6d7e8799 /hscript/script.cc
parent8bb3f48cb970a410d93940c675c4a086af9fbcdd (diff)
downloadhorizon-615278f3365087e436ee5ea13e0d15bd60718038.tar.gz
horizon-615278f3365087e436ee5ea13e0d15bd60718038.tar.bz2
horizon-615278f3365087e436ee5ea13e0d15bd60718038.tar.xz
horizon-615278f3365087e436ee5ea13e0d15bd60718038.zip
hscript: Allow multiple inheritance
This allows a HorizonScript to inherit from multiple files. Files are parsed in a system-defined, unspecified order, but all scripts of a given depth are guaranteed to be parsed before the next depth.
Diffstat (limited to 'hscript/script.cc')
-rw-r--r--hscript/script.cc114
1 files changed, 78 insertions, 36 deletions
diff --git a/hscript/script.cc b/hscript/script.cc
index 59a799a..d86216d 100644
--- a/hscript/script.cc
+++ b/hscript/script.cc
@@ -203,8 +203,6 @@ Script *Script::load(const std::string &path, const ScriptOptions &opts) {
}
-Script *Script::load(std::istream &sstream, const ScriptOptions &opts,
- const std::string &name) {
#define PARSER_ERROR(err_str) \
errors++;\
output_error(pos, err_str, "");\
@@ -216,24 +214,16 @@ Script *Script::load(std::istream &sstream, const ScriptOptions &opts,
warnings++;\
output_warning(pos, warn_str, "");
- using namespace Horizon::Keys;
- Script *the_script = new Script;
- the_script->opts = opts;
-
- int lineno = 0;
+std::pair< bool, std::vector<std::string> >
+Script::readFromStream(std::istream *my_stream, Script *the_script,
+ const ScriptOptions &opts, const std::string &curr_name,
+ int &errors, int &warnings, bool inherit) {
char nextline[SCRIPT_LINE_MAX];
+ int lineno = 0;
+ const int error_start = errors, warning_start = warnings;
const std::string delim(" \t");
- int errors = 0, warnings = 0;
- std::string curr_name;
- if(name == "/dev/stdin") {
- curr_name = "<stdin>";
- } else {
- curr_name = fs::canonical(fs::path(name)).native();
- }
- std::set<std::string> seen = {curr_name};
- bool inherit = false;
- std::istream *my_stream = &sstream;
+ std::vector<std::string> inherits;
while(my_stream->getline(nextline, sizeof(nextline))) {
lineno++;
@@ -245,15 +235,14 @@ Script *Script::load(std::istream &sstream, const ScriptOptions &opts,
const ScriptLocation pos(curr_name, lineno, inherit);
const std::string line(nextline);
std::string key;
- std::string::size_type start, key_end, value_begin;
- start = line.find_first_not_of(delim);
+ const std::string::size_type start{line.find_first_not_of(delim)},
+ key_end{line.find_first_of(delim, start)},
+ value_begin{line.find_first_not_of(delim, key_end)};
if(start == std::string::npos) {
/* This is a blank line; ignore it. */
continue;
}
- key_end = line.find_first_of(delim, start);
- value_begin = line.find_first_not_of(delim, key_end);
key = line.substr(start, (key_end - start));
if(key_end == std::string::npos || value_begin == std::string::npos) {
/* Key without value */
@@ -273,21 +262,17 @@ Script *Script::load(std::istream &sstream, const ScriptOptions &opts,
next_name = fs::absolute(better_path).native();
}
- if(seen.find(next_name) != seen.end()) {
- PARSER_ERROR("attempt to inherit from already inherited file")
- break;
- }
- seen.insert(next_name);
-
if(!fs::exists(next_name)) {
- PARSER_ERROR("attempt to inherit from non-existent file")
- break;
- } else {
- if(my_stream != &sstream) delete my_stream;
- curr_name = next_name;
- inherit = true;
- my_stream = new std::ifstream(curr_name);
+ errors++;
+ output_error(pos, "attempt to inherit from non-existent file",
+ next_name);
+ if(!opts.test(ScriptOptionFlags::KeepGoing)) {
+ break;
+ }
+ continue;
}
+
+ inherits.push_back(next_name);
continue;
}
@@ -329,6 +314,63 @@ Script *Script::load(std::istream &sstream, const ScriptOptions &opts,
errors++;
}
+ return std::make_pair((errors == error_start && warnings == warning_start),
+ inherits);
+}
+
+
+Script *Script::load(std::istream &sstream, const ScriptOptions &opts,
+ const std::string &name) {
+ using namespace Horizon::Keys;
+
+ Script *the_script = new Script;
+ the_script->opts = opts;
+
+ int errors = 0, warnings = 0;
+ std::string curr_name;
+ if(name == "/dev/stdin") {
+ curr_name = "<stdin>";
+ } else {
+ curr_name = fs::canonical(fs::path(name)).native();
+ }
+ std::set<std::string> seen = {curr_name};
+ std::set<std::string> this_depth = {curr_name};
+ std::set<std::string> next_depth;
+ bool inherit = false, keep_going = true;
+ std::istream *my_stream = &sstream;
+
+ while(keep_going) {
+ auto result = readFromStream(my_stream, the_script, opts, curr_name,
+ errors, warnings, inherit);
+ seen.insert(curr_name);
+ for(const auto &next_name: result.second) {
+ if (seen.find(next_name) != seen.end()) {
+ output_error(curr_name, "attempt to inherit from already "\
+ "inherited file", next_name);
+ keep_going = false;
+ break;
+ }
+ next_depth.insert(next_name);
+ }
+
+ this_depth.erase(curr_name);
+ if(this_depth.empty()) {
+ /* We've processed all scripts at this depth; let's go deeper. */
+ this_depth = next_depth;
+ next_depth = {};
+ }
+
+ if(this_depth.empty()) {
+ /* We are finished. */
+ break;
+ }
+
+ if(my_stream != &sstream) delete my_stream;
+ curr_name = *this_depth.begin();
+ inherit = true;
+ my_stream = new std::ifstream(curr_name);
+ }
+
/* Ensure all required keys are present. */
#define MISSING_ERROR(key) \
output_error(curr_name, "expected value for key '" + std::string(key) + "'",\
@@ -342,13 +384,13 @@ Script *Script::load(std::istream &sstream, const ScriptOptions &opts,
if(!the_script->internal->hostname) {
MISSING_ERROR("hostname")
}
- if(the_script->internal->packages.size() == 0) {
+ if(the_script->internal->packages.empty()) {
MISSING_ERROR("pkginstall")
}
if(!the_script->internal->rootpw) {
MISSING_ERROR("rootpw")
}
- if(the_script->internal->mounts.size() == 0 && !opts.test(ImageOnly)) {
+ if(the_script->internal->mounts.empty() && !opts.test(ImageOnly)) {
MISSING_ERROR("mount")
}
}