Improvements to BSD-licensed DTC.
- Numerous crash and bug fixes - Improved warning and error messages - Permit multiple labels on nodes and properties - Fix node@address references - Add support for /delete-node/ - Consume whitespace after a node - Read the next token before the second /memreserve/ - Fix parsing of whitespace - Clean up /delete-node/ and add support for /delete-property/ - Handle /delete-node/ specifying a unit address Obtained from: https://github.com/davidchisnall/dtc @df5ede4
This commit is contained in:
parent
f71d08566c
commit
bbe31b709a
@ -33,7 +33,7 @@
|
||||
#include "checking.hh"
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace dtc
|
||||
{
|
||||
@ -44,6 +44,30 @@ namespace checking
|
||||
|
||||
namespace
|
||||
{
|
||||
struct deleted_node_checker : public checker
|
||||
{
|
||||
deleted_node_checker(const char *name) : checker(name) {}
|
||||
virtual bool check_node(device_tree *, const node_ptr &n)
|
||||
{
|
||||
auto &deleted = n->deleted_child_nodes();
|
||||
if (deleted.empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool plural = deleted.size() > 1;
|
||||
string errmsg("Attempts to delete ");
|
||||
errmsg += plural ? "nodes" : "node";
|
||||
errmsg += " that ";
|
||||
errmsg += plural ? "were" : "was";
|
||||
errmsg += " not added in merge: ";
|
||||
for (auto &d : deleted)
|
||||
{
|
||||
errmsg += d;
|
||||
}
|
||||
report_error(errmsg.c_str());
|
||||
return false;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Checker that verifies that every node that has children has
|
||||
* #address-cells and #size-cells properties.
|
||||
@ -73,16 +97,16 @@ namespace
|
||||
}
|
||||
if (found_size && found_address)
|
||||
{
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_address)
|
||||
{
|
||||
report_error("Missing #address-cells property");
|
||||
report_error("Missing #address-cells property");
|
||||
}
|
||||
if (!found_size)
|
||||
{
|
||||
report_error("Missing #size-cells property");
|
||||
report_error("Missing #size-cells property");
|
||||
}
|
||||
return found_address && found_size;
|
||||
}
|
||||
@ -126,11 +150,11 @@ checker::report_error(const char *errmsg)
|
||||
for (auto &p : path)
|
||||
{
|
||||
putc('/', stderr);
|
||||
p.first.dump();
|
||||
puts(p.first.c_str());
|
||||
if (!(p.second.empty()))
|
||||
{
|
||||
putc('@', stderr);
|
||||
p.second.dump();
|
||||
puts(p.second.c_str());
|
||||
}
|
||||
}
|
||||
fprintf(stderr, " [-W%s]\n", checker_name);
|
||||
@ -167,7 +191,7 @@ property_size_checker::check(device_tree *, const node_ptr &, property_ptr p)
|
||||
|
||||
template<property_value::value_type T>
|
||||
void
|
||||
check_manager::add_property_type_checker(const char *name, string prop)
|
||||
check_manager::add_property_type_checker(const char *name, const string &prop)
|
||||
{
|
||||
checkers.insert(std::make_pair(string(name),
|
||||
new property_type_checker<T>(name, prop)));
|
||||
@ -175,7 +199,7 @@ check_manager::add_property_type_checker(const char *name, string prop)
|
||||
|
||||
void
|
||||
check_manager::add_property_size_checker(const char *name,
|
||||
string prop,
|
||||
const string &prop,
|
||||
uint32_t size)
|
||||
{
|
||||
checkers.insert(std::make_pair(string(name),
|
||||
@ -207,6 +231,8 @@ check_manager::check_manager()
|
||||
add_property_size_checker("type-phandle", string("phandle"), 4);
|
||||
disabled_checkers.insert(std::make_pair(string("cells-attributes"),
|
||||
new address_cells_checker("cells-attributes")));
|
||||
checkers.insert(std::make_pair(string("deleted-nodes"),
|
||||
new deleted_node_checker("deleted-nodes")));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -225,7 +251,7 @@ check_manager::run_checks(device_tree *tree, bool keep_going)
|
||||
}
|
||||
|
||||
bool
|
||||
check_manager::disable_checker(string name)
|
||||
check_manager::disable_checker(const string &name)
|
||||
{
|
||||
auto checker = checkers.find(name);
|
||||
if (checker != checkers.end())
|
||||
@ -239,7 +265,7 @@ check_manager::disable_checker(string name)
|
||||
}
|
||||
|
||||
bool
|
||||
check_manager::enable_checker(string name)
|
||||
check_manager::enable_checker(const string &name)
|
||||
{
|
||||
auto checker = disabled_checkers.find(name);
|
||||
if (checker != disabled_checkers.end())
|
||||
|
@ -32,7 +32,7 @@
|
||||
|
||||
#ifndef _CHECKING_HH_
|
||||
#define _CHECKING_HH_
|
||||
#include "string.hh"
|
||||
#include <string>
|
||||
#include "fdt.hh"
|
||||
|
||||
namespace dtc
|
||||
@ -58,7 +58,7 @@ class checker
|
||||
/**
|
||||
* The name of the checker. This is used for printing error messages
|
||||
* and for enabling / disabling specific checkers from the command
|
||||
* line.
|
||||
* line.
|
||||
*/
|
||||
const char *checker_name;
|
||||
/**
|
||||
@ -118,18 +118,18 @@ class property_checker : public checker
|
||||
/**
|
||||
* The name of the property that this checker is looking for.
|
||||
*/
|
||||
string key;
|
||||
std::string key;
|
||||
public:
|
||||
/**
|
||||
* Implementation of the generic property-checking method that checks
|
||||
* for a property with the name specified in the constructor
|
||||
* for a property with the name specified in the constructor.
|
||||
*/
|
||||
virtual bool check_property(device_tree *tree, const node_ptr &n, property_ptr p);
|
||||
/**
|
||||
* Constructor. Takes the name of the checker and the name of the
|
||||
* property to check.
|
||||
*/
|
||||
property_checker(const char* name, string property_name)
|
||||
property_checker(const char* name, const std::string &property_name)
|
||||
: checker(name), key(property_name) {}
|
||||
/**
|
||||
* The check method, which subclasses should implement.
|
||||
@ -147,7 +147,7 @@ struct property_type_checker : public property_checker
|
||||
* Constructor, takes the name of the checker and the name of the
|
||||
* property to check as arguments.
|
||||
*/
|
||||
property_type_checker(const char* name, string property_name) :
|
||||
property_type_checker(const char* name, const std::string &property_name) :
|
||||
property_checker(name, property_name) {}
|
||||
virtual bool check(device_tree *tree, const node_ptr &n, property_ptr p) = 0;
|
||||
};
|
||||
@ -158,7 +158,7 @@ struct property_type_checker : public property_checker
|
||||
template<>
|
||||
struct property_type_checker <property_value::EMPTY> : public property_checker
|
||||
{
|
||||
property_type_checker(const char* name, string property_name) :
|
||||
property_type_checker(const char* name, const std::string &property_name) :
|
||||
property_checker(name, property_name) {}
|
||||
virtual bool check(device_tree *, const node_ptr &, property_ptr p)
|
||||
{
|
||||
@ -173,7 +173,7 @@ struct property_type_checker <property_value::EMPTY> : public property_checker
|
||||
template<>
|
||||
struct property_type_checker <property_value::STRING> : public property_checker
|
||||
{
|
||||
property_type_checker(const char* name, string property_name) :
|
||||
property_type_checker(const char* name, const std::string &property_name) :
|
||||
property_checker(name, property_name) {}
|
||||
virtual bool check(device_tree *, const node_ptr &, property_ptr p)
|
||||
{
|
||||
@ -188,7 +188,7 @@ template<>
|
||||
struct property_type_checker <property_value::STRING_LIST> :
|
||||
public property_checker
|
||||
{
|
||||
property_type_checker(const char* name, string property_name) :
|
||||
property_type_checker(const char* name, const std::string &property_name) :
|
||||
property_checker(name, property_name) {}
|
||||
virtual bool check(device_tree *, const node_ptr &, property_ptr p)
|
||||
{
|
||||
@ -211,11 +211,11 @@ struct property_type_checker <property_value::STRING_LIST> :
|
||||
template<>
|
||||
struct property_type_checker <property_value::PHANDLE> : public property_checker
|
||||
{
|
||||
property_type_checker(const char* name, string property_name) :
|
||||
property_type_checker(const char* name, const std::string &property_name) :
|
||||
property_checker(name, property_name) {}
|
||||
virtual bool check(device_tree *tree, const node_ptr &, property_ptr p)
|
||||
{
|
||||
return (p->begin() + 1 == p->end()) &&
|
||||
return (p->begin() + 1 == p->end()) &&
|
||||
(tree->referenced_node(*p->begin()) != 0);
|
||||
}
|
||||
};
|
||||
@ -234,7 +234,9 @@ struct property_size_checker : public property_checker
|
||||
* Constructor, takes the name of the checker, the name of the property
|
||||
* to check, and its expected size as arguments.
|
||||
*/
|
||||
property_size_checker(const char* name, string property_name, uint32_t bytes)
|
||||
property_size_checker(const char* name,
|
||||
const std::string &property_name,
|
||||
uint32_t bytes)
|
||||
: property_checker(name, property_name), size(bytes) {}
|
||||
/**
|
||||
* Check, validates that the property has the correct size.
|
||||
@ -254,26 +256,26 @@ class check_manager
|
||||
* disabling checkers from the command line. When this manager runs,
|
||||
* it will only run the checkers from this map.
|
||||
*/
|
||||
std::unordered_map<string, checker*> checkers;
|
||||
std::unordered_map<std::string, checker*> checkers;
|
||||
/**
|
||||
* The disabled checkers. Moving checkers to this list disables them,
|
||||
* but allows them to be easily moved back.
|
||||
*/
|
||||
std::unordered_map<string, checker*> disabled_checkers;
|
||||
std::unordered_map<std::string, checker*> disabled_checkers;
|
||||
/**
|
||||
* Helper function for adding a property value checker.
|
||||
*/
|
||||
template<property_value::value_type T>
|
||||
void add_property_type_checker(const char *name, string prop);
|
||||
void add_property_type_checker(const char *name, const std::string &prop);
|
||||
/**
|
||||
* Helper function for adding a simple type checker.
|
||||
*/
|
||||
void add_property_type_checker(const char *name, string prop);
|
||||
void add_property_type_checker(const char *name, const std::string &prop);
|
||||
/**
|
||||
* Helper function for adding a property value checker.
|
||||
*/
|
||||
void add_property_size_checker(const char *name,
|
||||
string prop,
|
||||
const std::string &prop,
|
||||
uint32_t size);
|
||||
public:
|
||||
/**
|
||||
@ -292,11 +294,11 @@ class check_manager
|
||||
/**
|
||||
* Disables the named checker.
|
||||
*/
|
||||
bool disable_checker(string name);
|
||||
bool disable_checker(const std::string &name);
|
||||
/**
|
||||
* Enables the named checker.
|
||||
* Enables the named checker.
|
||||
*/
|
||||
bool enable_checker(string name);
|
||||
bool enable_checker(const std::string &name);
|
||||
};
|
||||
|
||||
} // namespace checking
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace dtc
|
||||
{
|
||||
@ -51,9 +52,9 @@ void output_writer::write_data(byte_buffer b)
|
||||
}
|
||||
|
||||
void
|
||||
binary_writer::write_string(string name)
|
||||
binary_writer::write_string(const string &name)
|
||||
{
|
||||
name.push_to_buffer(buffer);
|
||||
push_string(buffer, name);
|
||||
// Trailing nul
|
||||
buffer.push_back(0);
|
||||
}
|
||||
@ -97,15 +98,6 @@ binary_writer::size()
|
||||
return buffer.size();
|
||||
}
|
||||
|
||||
void
|
||||
asm_writer::write_string(const char *c)
|
||||
{
|
||||
while (*c)
|
||||
{
|
||||
buffer.push_back((uint8_t)*(c++));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
asm_writer::write_line(const char *c)
|
||||
{
|
||||
@ -142,34 +134,44 @@ asm_writer::write_byte(uint8_t b)
|
||||
}
|
||||
|
||||
void
|
||||
asm_writer::write_label(string name)
|
||||
asm_writer::write_label(const string &name)
|
||||
{
|
||||
write_line("\t.globl ");
|
||||
name.push_to_buffer(buffer);
|
||||
push_string(buffer, name);
|
||||
buffer.push_back('\n');
|
||||
name.push_to_buffer(buffer);
|
||||
push_string(buffer, name);
|
||||
buffer.push_back(':');
|
||||
buffer.push_back('\n');
|
||||
buffer.push_back('_');
|
||||
name.push_to_buffer(buffer);
|
||||
push_string(buffer, name);
|
||||
buffer.push_back(':');
|
||||
buffer.push_back('\n');
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
asm_writer::write_comment(string name)
|
||||
asm_writer::write_comment(const string &name)
|
||||
{
|
||||
write_line("\t/* ");
|
||||
name.push_to_buffer(buffer);
|
||||
push_string(buffer, name);
|
||||
write_string(" */\n");
|
||||
}
|
||||
|
||||
void
|
||||
asm_writer::write_string(string name)
|
||||
asm_writer::write_string(const char *c)
|
||||
{
|
||||
while (*c)
|
||||
{
|
||||
buffer.push_back((uint8_t)*(c++));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
asm_writer::write_string(const string &name)
|
||||
{
|
||||
write_line("\t.string \"");
|
||||
name.push_to_buffer(buffer);
|
||||
push_string(buffer, name);
|
||||
write_line("\"\n");
|
||||
bytes_written += name.size() + 1;
|
||||
}
|
||||
@ -231,8 +233,8 @@ asm_writer::size()
|
||||
void
|
||||
header::write(output_writer &out)
|
||||
{
|
||||
out.write_label(string("dt_blob_start"));
|
||||
out.write_label(string("dt_header"));
|
||||
out.write_label("dt_blob_start");
|
||||
out.write_label("dt_header");
|
||||
out.write_comment("magic");
|
||||
out.write_data(magic);
|
||||
out.write_comment("totalsize");
|
||||
@ -275,7 +277,7 @@ header::read_dtb(input_buffer &input)
|
||||
input.consume_binary(size_dt_struct);
|
||||
}
|
||||
uint32_t
|
||||
string_table::add_string(string str)
|
||||
string_table::add_string(const string &str)
|
||||
{
|
||||
auto old = string_offsets.find(str);
|
||||
if (old == string_offsets.end())
|
||||
@ -296,13 +298,13 @@ string_table::add_string(string str)
|
||||
void
|
||||
string_table::write(dtb::output_writer &writer)
|
||||
{
|
||||
writer.write_comment(string("Strings table."));
|
||||
writer.write_label(string("dt_strings_start"));
|
||||
writer.write_comment("Strings table.");
|
||||
writer.write_label("dt_strings_start");
|
||||
for (auto &i : strings)
|
||||
{
|
||||
writer.write_string(i);
|
||||
}
|
||||
writer.write_label(string("dt_strings_end"));
|
||||
writer.write_label("dt_strings_end");
|
||||
}
|
||||
|
||||
} // namespace dtb
|
||||
|
@ -33,10 +33,13 @@
|
||||
#ifndef _DTB_HH_
|
||||
#define _DTB_HH_
|
||||
#include <map>
|
||||
#include "string.hh"
|
||||
#include <string>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "input_buffer.hh"
|
||||
#include "util.hh"
|
||||
|
||||
namespace dtc
|
||||
{
|
||||
/**
|
||||
@ -121,16 +124,16 @@ struct output_writer
|
||||
* assembly output, where the labels become symbols that can be
|
||||
* resolved at link time.
|
||||
*/
|
||||
virtual void write_label(string name) = 0;
|
||||
virtual void write_label(const std::string &name) = 0;
|
||||
/**
|
||||
* Writes a comment into the output stream. Useful only when debugging
|
||||
* the output.
|
||||
*/
|
||||
virtual void write_comment(string name) = 0;
|
||||
virtual void write_comment(const std::string &name) = 0;
|
||||
/**
|
||||
* Writes a string. A nul terminator is implicitly added.
|
||||
*/
|
||||
virtual void write_string(string name) = 0;
|
||||
virtual void write_string(const std::string &name) = 0;
|
||||
/**
|
||||
* Writes a single 8-bit value.
|
||||
*/
|
||||
@ -186,12 +189,12 @@ class binary_writer : public output_writer
|
||||
* The binary format does not support labels, so this method
|
||||
* does nothing.
|
||||
*/
|
||||
virtual void write_label(string) {}
|
||||
virtual void write_label(const std::string &) {}
|
||||
/**
|
||||
* Comments are ignored by the binary writer.
|
||||
*/
|
||||
virtual void write_comment(string) {}
|
||||
virtual void write_string(string name);
|
||||
virtual void write_comment(const std::string&) {}
|
||||
virtual void write_string(const std::string &name);
|
||||
virtual void write_data(uint8_t v);
|
||||
virtual void write_data(uint32_t v);
|
||||
virtual void write_data(uint64_t v);
|
||||
@ -224,10 +227,14 @@ class asm_writer : public output_writer
|
||||
uint32_t bytes_written;
|
||||
|
||||
/**
|
||||
* Writes a C string directly to the output as-is. This is mainly used
|
||||
* for writing directives.
|
||||
* Writes a string directly to the output as-is. This is the function that
|
||||
* performs the real output.
|
||||
*/
|
||||
void write_string(const char *c);
|
||||
/**
|
||||
* Write a string to the output.
|
||||
*/
|
||||
void write_string(const std::string &c);
|
||||
/**
|
||||
* Writes the string, starting on a new line.
|
||||
*/
|
||||
@ -239,9 +246,8 @@ class asm_writer : public output_writer
|
||||
void write_byte(uint8_t b);
|
||||
public:
|
||||
asm_writer() : byte_count(0), bytes_written(0) {}
|
||||
virtual void write_label(string name);
|
||||
virtual void write_comment(string name);
|
||||
virtual void write_string(string name);
|
||||
virtual void write_label(const std::string &name);
|
||||
virtual void write_comment(const std::string &name);
|
||||
virtual void write_data(uint8_t v);
|
||||
virtual void write_data(uint32_t v);
|
||||
virtual void write_data(uint64_t v);
|
||||
@ -328,14 +334,14 @@ class string_table {
|
||||
/**
|
||||
* Map from strings to their offset.
|
||||
*/
|
||||
std::map<string, uint32_t> string_offsets;
|
||||
std::map<std::string, uint32_t> string_offsets;
|
||||
/**
|
||||
* The strings, in the order in which they should be written to the
|
||||
* output. The order must be stable - adding another string must not
|
||||
* change the offset of any that we have already referenced - and so we
|
||||
* simply write the strings in the order that they are passed.
|
||||
*/
|
||||
std::vector<string> strings;
|
||||
std::vector<std::string> strings;
|
||||
/**
|
||||
* The current size of the strings section.
|
||||
*/
|
||||
@ -351,7 +357,7 @@ class string_table {
|
||||
* will return its existing offset, otherwise it will return a new
|
||||
* offset.
|
||||
*/
|
||||
uint32_t add_string(string str);
|
||||
uint32_t add_string(const std::string &str);
|
||||
/**
|
||||
* Writes the strings table to the specified output.
|
||||
*/
|
||||
|
@ -237,6 +237,10 @@ Checks that all nodes with children have both
|
||||
and
|
||||
.Va #size-cells
|
||||
properties.
|
||||
.It deleted-nodes
|
||||
Checks that all
|
||||
.Va /delete-node/
|
||||
statements refer to nodes that are merged.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
The command:
|
||||
|
@ -42,8 +42,10 @@
|
||||
|
||||
#include "fdt.hh"
|
||||
#include "checking.hh"
|
||||
#include "util.hh"
|
||||
|
||||
using namespace dtc;
|
||||
using std::string;
|
||||
|
||||
/**
|
||||
* The current major version of the tool.
|
||||
@ -58,7 +60,7 @@ int version_minor = 4;
|
||||
*/
|
||||
int version_patch = 0;
|
||||
|
||||
static void usage(const char* argv0)
|
||||
static void usage(const string &argv0)
|
||||
{
|
||||
fprintf(stderr, "Usage:\n"
|
||||
"\t%s\t[-fhsv] [-b boot_cpu_id] [-d dependency_file]"
|
||||
@ -67,7 +69,7 @@ static void usage(const char* argv0)
|
||||
"[-O output_format]\n"
|
||||
"\t\t[-o output_file] [-R entries] [-S bytes] [-p bytes]"
|
||||
"[-V blob_version]\n"
|
||||
"\t\t-W [no-]checker_name] input_file\n", basename((char*)argv0));
|
||||
"\t\t-W [no-]checker_name] input_file\n", basename(argv0).c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,9 +92,8 @@ main(int argc, char **argv)
|
||||
const char *in_file = "-";
|
||||
FILE *depfile = 0;
|
||||
bool debug_mode = false;
|
||||
void (device_tree::*write_fn)(int) = &device_tree::write_binary;
|
||||
void (device_tree::*read_fn)(const char*, FILE*) =
|
||||
&device_tree::parse_dts;
|
||||
auto write_fn = &device_tree::write_binary;
|
||||
auto read_fn = &device_tree::parse_dts;
|
||||
uint32_t boot_cpu;
|
||||
bool boot_cpu_specified = false;
|
||||
bool keep_going = false;
|
||||
@ -115,12 +116,12 @@ main(int argc, char **argv)
|
||||
return EXIT_SUCCESS;
|
||||
case 'I':
|
||||
{
|
||||
string arg = string(optarg);
|
||||
if (arg == string("dtb"))
|
||||
string arg(optarg);
|
||||
if (arg == "dtb")
|
||||
{
|
||||
read_fn = &device_tree::parse_dtb;
|
||||
}
|
||||
else if (arg == string("dts"))
|
||||
else if (arg == "dts")
|
||||
{
|
||||
read_fn = &device_tree::parse_dts;
|
||||
}
|
||||
@ -133,16 +134,16 @@ main(int argc, char **argv)
|
||||
}
|
||||
case 'O':
|
||||
{
|
||||
string arg = string(optarg);
|
||||
if (arg == string("dtb"))
|
||||
string arg(optarg);
|
||||
if (arg == "dtb")
|
||||
{
|
||||
write_fn = &device_tree::write_binary;
|
||||
}
|
||||
else if (arg == string("asm"))
|
||||
else if (arg == "asm")
|
||||
{
|
||||
write_fn = &device_tree::write_asm;
|
||||
}
|
||||
else if (arg == string("dts"))
|
||||
else if (arg == "dts")
|
||||
{
|
||||
write_fn = &device_tree::write_dts;
|
||||
}
|
||||
@ -168,7 +169,7 @@ main(int argc, char **argv)
|
||||
debug_mode = true;
|
||||
break;
|
||||
case 'V':
|
||||
if (string(optarg) != string("17"))
|
||||
if (string(optarg) != "17")
|
||||
{
|
||||
fprintf(stderr, "Unknown output format version: %s\n", optarg);
|
||||
return EXIT_FAILURE;
|
||||
@ -180,7 +181,7 @@ main(int argc, char **argv)
|
||||
{
|
||||
fclose(depfile);
|
||||
}
|
||||
if (string(optarg) == string("-"))
|
||||
if (string(optarg) == "-")
|
||||
{
|
||||
depfile = stdout;
|
||||
}
|
||||
@ -197,16 +198,16 @@ main(int argc, char **argv)
|
||||
}
|
||||
case 'H':
|
||||
{
|
||||
string arg = string(optarg);
|
||||
if (arg == string("both"))
|
||||
string arg(optarg);
|
||||
if (arg == "both")
|
||||
{
|
||||
tree.set_phandle_format(device_tree::BOTH);
|
||||
}
|
||||
else if (arg == string("epapr"))
|
||||
else if (arg == "epapr")
|
||||
{
|
||||
tree.set_phandle_format(device_tree::EPAPR);
|
||||
}
|
||||
else if (arg == string("linux"))
|
||||
else if (arg == "linux")
|
||||
{
|
||||
tree.set_phandle_format(device_tree::LINUX);
|
||||
}
|
||||
@ -229,7 +230,7 @@ main(int argc, char **argv)
|
||||
case 'W':
|
||||
case 'E':
|
||||
{
|
||||
string arg = string(optarg);
|
||||
string arg(optarg);
|
||||
if ((arg.size() > 3) && (strncmp(optarg, "no-", 3) == 0))
|
||||
{
|
||||
arg = string(optarg+3);
|
||||
@ -307,7 +308,7 @@ main(int argc, char **argv)
|
||||
}
|
||||
if (!(tree.is_valid() || keep_going))
|
||||
{
|
||||
fprintf(stderr, "Failed to parse tree. Unhappy face!\n");
|
||||
fprintf(stderr, "Failed to parse tree.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
clock_t c2 = clock();
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "dtb.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
@ -48,6 +49,8 @@
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace dtc
|
||||
{
|
||||
|
||||
@ -78,7 +81,7 @@ property_value::push_to_buffer(byte_buffer &buffer)
|
||||
}
|
||||
else
|
||||
{
|
||||
string_data.push_to_buffer(buffer, true);
|
||||
push_string(buffer, string_data, true);
|
||||
// Trailing nul
|
||||
buffer.push_back(0);
|
||||
}
|
||||
@ -172,7 +175,7 @@ property_value::write_as_string(FILE *file)
|
||||
putc('"', file);
|
||||
if (byte_data.empty())
|
||||
{
|
||||
string_data.print(file);
|
||||
fputs(string_data.c_str(), file);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -240,31 +243,32 @@ property_value::write_as_bytes(FILE *file)
|
||||
}
|
||||
|
||||
void
|
||||
property::parse_string(input_buffer &input)
|
||||
property::parse_string(text_input_buffer &input)
|
||||
{
|
||||
property_value v;
|
||||
assert(input[0] == '"');
|
||||
assert(*input == '"');
|
||||
++input;
|
||||
const char *start = (const char*)input;
|
||||
int length = 0;
|
||||
while (char c = input[0])
|
||||
std::vector<char> bytes;
|
||||
bool isEscaped = false;
|
||||
while (char c = *input)
|
||||
{
|
||||
if (c == '"' && input[-1] != '\\')
|
||||
if (c == '"' && !isEscaped)
|
||||
{
|
||||
input.consume('"');
|
||||
break;
|
||||
}
|
||||
isEscaped = (c == '\\');
|
||||
bytes.push_back(c);
|
||||
++input;
|
||||
++length;
|
||||
}
|
||||
v.string_data = string(start, length);
|
||||
v.string_data = string(bytes.begin(), bytes.end());
|
||||
values.push_back(v);
|
||||
}
|
||||
|
||||
void
|
||||
property::parse_cells(input_buffer &input, int cell_size)
|
||||
property::parse_cells(text_input_buffer &input, int cell_size)
|
||||
{
|
||||
assert(input[0] == '<');
|
||||
assert(*input == '<');
|
||||
++input;
|
||||
property_value v;
|
||||
input.next_token();
|
||||
@ -282,9 +286,18 @@ property::parse_cells(input_buffer &input, int cell_size)
|
||||
return;
|
||||
}
|
||||
input.next_token();
|
||||
// FIXME: We should support full paths here, but we
|
||||
// don't.
|
||||
string referenced = string::parse_node_name(input);
|
||||
bool isPath = false;
|
||||
string referenced;
|
||||
if (!input.consume('{'))
|
||||
{
|
||||
referenced = input.parse_node_name();
|
||||
}
|
||||
else
|
||||
{
|
||||
referenced = input.parse_to('}');
|
||||
input.consume('}');
|
||||
isPath = true;
|
||||
}
|
||||
if (referenced.empty())
|
||||
{
|
||||
input.parse_error("Expected node name");
|
||||
@ -343,9 +356,9 @@ property::parse_cells(input_buffer &input, int cell_size)
|
||||
}
|
||||
|
||||
void
|
||||
property::parse_bytes(input_buffer &input)
|
||||
property::parse_bytes(text_input_buffer &input)
|
||||
{
|
||||
assert(input[0] == '[');
|
||||
assert(*input == '[');
|
||||
++input;
|
||||
property_value v;
|
||||
input.next_token();
|
||||
@ -370,13 +383,13 @@ property::parse_bytes(input_buffer &input)
|
||||
}
|
||||
|
||||
void
|
||||
property::parse_reference(input_buffer &input)
|
||||
property::parse_reference(text_input_buffer &input)
|
||||
{
|
||||
assert(input[0] == '&');
|
||||
assert(*input == '&');
|
||||
++input;
|
||||
input.next_token();
|
||||
property_value v;
|
||||
v.string_data = string::parse_node_name(input);
|
||||
v.string_data = input.parse_node_name();
|
||||
if (v.string_data.empty())
|
||||
{
|
||||
input.parse_error("Expected node name");
|
||||
@ -400,7 +413,7 @@ property::property(input_buffer &structs, input_buffer &strings)
|
||||
}
|
||||
// Find the name
|
||||
input_buffer name_buffer = strings.buffer_from_offset(name_offset);
|
||||
if (name_buffer.empty())
|
||||
if (name_buffer.finished())
|
||||
{
|
||||
fprintf(stderr, "Property name offset %" PRIu32
|
||||
" is past the end of the strings table\n",
|
||||
@ -408,7 +421,7 @@ property::property(input_buffer &structs, input_buffer &strings)
|
||||
valid = false;
|
||||
return;
|
||||
}
|
||||
key = string(name_buffer);
|
||||
key = name_buffer.parse_to(0);
|
||||
|
||||
// If we're empty, do not push anything as value.
|
||||
if (!length)
|
||||
@ -429,7 +442,7 @@ property::property(input_buffer &structs, input_buffer &strings)
|
||||
values.push_back(v);
|
||||
}
|
||||
|
||||
void property::parse_define(input_buffer &input, define_map *defines)
|
||||
void property::parse_define(text_input_buffer &input, define_map *defines)
|
||||
{
|
||||
input.consume('$');
|
||||
if (!defines)
|
||||
@ -438,7 +451,7 @@ void property::parse_define(input_buffer &input, define_map *defines)
|
||||
valid = false;
|
||||
return;
|
||||
}
|
||||
string name = string::parse_property_name(input);
|
||||
string name = input.parse_property_name();
|
||||
define_map::iterator found;
|
||||
if ((name == string()) ||
|
||||
((found = defines->find(name)) == defines->end()))
|
||||
@ -450,15 +463,15 @@ void property::parse_define(input_buffer &input, define_map *defines)
|
||||
values.push_back((*found).second->values[0]);
|
||||
}
|
||||
|
||||
property::property(input_buffer &input,
|
||||
string k,
|
||||
string l,
|
||||
property::property(text_input_buffer &input,
|
||||
string &&k,
|
||||
string_set &&l,
|
||||
bool semicolonTerminated,
|
||||
define_map *defines) : key(k), label(l), valid(true)
|
||||
define_map *defines) : key(k), labels(l), valid(true)
|
||||
{
|
||||
do {
|
||||
input.next_token();
|
||||
switch (input[0])
|
||||
switch (*input)
|
||||
{
|
||||
case '$':
|
||||
{
|
||||
@ -487,7 +500,7 @@ property::property(input_buffer &input,
|
||||
}
|
||||
if (!valid) return;
|
||||
input.next_token();
|
||||
if (input[0] != '<')
|
||||
if (*input != '<')
|
||||
{
|
||||
input.parse_error("/bits/ directive is only valid on arrays");
|
||||
valid = false;
|
||||
@ -534,10 +547,14 @@ property::parse_dtb(input_buffer &structs, input_buffer &strings)
|
||||
}
|
||||
|
||||
property_ptr
|
||||
property::parse(input_buffer &input, string key, string label,
|
||||
property::parse(text_input_buffer &input, string &&key, string_set &&label,
|
||||
bool semicolonTerminated, define_map *defines)
|
||||
{
|
||||
property_ptr p(new property(input, key, label, semicolonTerminated, defines));
|
||||
property_ptr p(new property(input,
|
||||
std::move(key),
|
||||
std::move(label),
|
||||
semicolonTerminated,
|
||||
defines));
|
||||
if (!p->valid)
|
||||
{
|
||||
p = nullptr;
|
||||
@ -596,14 +613,16 @@ property::write_dts(FILE *file, int indent)
|
||||
{
|
||||
putc('\t', file);
|
||||
}
|
||||
if (label != string())
|
||||
#ifdef PRINT_LABELS
|
||||
for (auto &l : labels)
|
||||
{
|
||||
label.print(file);
|
||||
fputs(l.c_str(), file);
|
||||
fputs(": ", file);
|
||||
}
|
||||
#endif
|
||||
if (key != string())
|
||||
{
|
||||
key.print(file);
|
||||
fputs(key.c_str(), file);
|
||||
}
|
||||
if (!values.empty())
|
||||
{
|
||||
@ -637,7 +656,7 @@ property::write_dts(FILE *file, int indent)
|
||||
}
|
||||
|
||||
string
|
||||
node::parse_name(input_buffer &input, bool &is_property, const char *error)
|
||||
node::parse_name(text_input_buffer &input, bool &is_property, const char *error)
|
||||
{
|
||||
if (!valid)
|
||||
{
|
||||
@ -646,9 +665,9 @@ node::parse_name(input_buffer &input, bool &is_property, const char *error)
|
||||
input.next_token();
|
||||
if (is_property)
|
||||
{
|
||||
return string::parse_property_name(input);
|
||||
return input.parse_property_name();
|
||||
}
|
||||
string n = string::parse_node_or_property_name(input, is_property);
|
||||
string n = input.parse_node_or_property_name(is_property);
|
||||
if (n.empty())
|
||||
{
|
||||
if (n.empty())
|
||||
@ -672,25 +691,23 @@ node::visit(std::function<void(node&)> fn)
|
||||
|
||||
node::node(input_buffer &structs, input_buffer &strings) : valid(true)
|
||||
{
|
||||
const char *name_start = (const char*)structs;
|
||||
int name_length = 0;
|
||||
std::vector<char> bytes;
|
||||
while (structs[0] != '\0' && structs[0] != '@')
|
||||
{
|
||||
name_length++;
|
||||
bytes.push_back(structs[0]);
|
||||
++structs;
|
||||
}
|
||||
name = string(name_start, name_length);
|
||||
name = string(bytes.begin(), bytes.end());
|
||||
bytes.clear();
|
||||
if (structs[0] == '@')
|
||||
{
|
||||
++structs;
|
||||
name_start = (const char*)structs;
|
||||
name_length = 0;
|
||||
while (structs[0] != '\0')
|
||||
{
|
||||
name_length++;
|
||||
bytes.push_back(structs[0]);
|
||||
++structs;
|
||||
}
|
||||
unit_address = string(name_start, name_length);
|
||||
unit_address = string(bytes.begin(), bytes.end());
|
||||
}
|
||||
++structs;
|
||||
uint32_t token;
|
||||
@ -747,8 +764,12 @@ node::node(input_buffer &structs, input_buffer &strings) : valid(true)
|
||||
return;
|
||||
}
|
||||
|
||||
node::node(input_buffer &input, string n, string l, string a, define_map *defines) :
|
||||
label(l), name(n), unit_address(a), valid(true)
|
||||
node::node(text_input_buffer &input,
|
||||
string &&n,
|
||||
std::unordered_set<string> &&l,
|
||||
string &&a,
|
||||
define_map *defines)
|
||||
: labels(l), name(n), unit_address(a), valid(true)
|
||||
{
|
||||
if (!input.consume('{'))
|
||||
{
|
||||
@ -760,15 +781,60 @@ node::node(input_buffer &input, string n, string l, string a, define_map *define
|
||||
// flag set if we find any characters that are only in
|
||||
// the property name character set, not the node
|
||||
bool is_property = false;
|
||||
string child_name, child_label, child_address;
|
||||
string child_name, child_address;
|
||||
std::unordered_set<string> child_labels;
|
||||
auto parse_delete = [&](const char *expected, bool at)
|
||||
{
|
||||
if (child_name == string())
|
||||
{
|
||||
input.parse_error(expected);
|
||||
valid = false;
|
||||
return;
|
||||
}
|
||||
input.next_token();
|
||||
if (at && input.consume('@'))
|
||||
{
|
||||
child_name += '@';
|
||||
child_name += parse_name(input, is_property, "Expected unit address");
|
||||
}
|
||||
if (!input.consume(';'))
|
||||
{
|
||||
input.parse_error("Expected semicolon");
|
||||
valid = false;
|
||||
return;
|
||||
}
|
||||
input.next_token();
|
||||
};
|
||||
if (input.consume("/delete-node/"))
|
||||
{
|
||||
input.next_token();
|
||||
child_name = input.parse_node_name();
|
||||
parse_delete("Expected node name", true);
|
||||
if (valid)
|
||||
{
|
||||
deleted_children.insert(child_name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (input.consume("/delete-property/"))
|
||||
{
|
||||
input.next_token();
|
||||
child_name = input.parse_property_name();
|
||||
parse_delete("Expected property name", false);
|
||||
if (valid)
|
||||
{
|
||||
deleted_props.insert(child_name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
child_name = parse_name(input, is_property,
|
||||
"Expected property or node name");
|
||||
if (input.consume(':'))
|
||||
while (input.consume(':'))
|
||||
{
|
||||
// Node labels can contain any characters? The
|
||||
// spec doesn't say, so we guess so...
|
||||
is_property = false;
|
||||
child_label = child_name;
|
||||
child_labels.insert(std::move(child_name));
|
||||
child_name = parse_name(input, is_property, "Expected property or node name");
|
||||
}
|
||||
if (input.consume('@'))
|
||||
@ -783,8 +849,8 @@ node::node(input_buffer &input, string n, string l, string a, define_map *define
|
||||
// If we're parsing a property, then we must actually do that.
|
||||
if (input.consume('='))
|
||||
{
|
||||
property_ptr p = property::parse(input, child_name,
|
||||
child_label, true, defines);
|
||||
property_ptr p = property::parse(input, std::move(child_name),
|
||||
std::move(child_labels), true, defines);
|
||||
if (p == 0)
|
||||
{
|
||||
valid = false;
|
||||
@ -794,10 +860,10 @@ node::node(input_buffer &input, string n, string l, string a, define_map *define
|
||||
props.push_back(p);
|
||||
}
|
||||
}
|
||||
else if (!is_property && input[0] == ('{'))
|
||||
else if (!is_property && *input == ('{'))
|
||||
{
|
||||
node_ptr child = node::parse(input, child_name,
|
||||
child_label, child_address, defines);
|
||||
node_ptr child = node::parse(input, std::move(child_name),
|
||||
std::move(child_labels), std::move(child_address), defines);
|
||||
if (child)
|
||||
{
|
||||
children.push_back(std::move(child));
|
||||
@ -809,15 +875,16 @@ node::node(input_buffer &input, string n, string l, string a, define_map *define
|
||||
}
|
||||
else if (input.consume(';'))
|
||||
{
|
||||
props.push_back(property_ptr(new property(child_name, child_label)));
|
||||
props.push_back(property_ptr(new property(std::move(child_name), std::move(child_labels))));
|
||||
}
|
||||
else
|
||||
{
|
||||
input.parse_error("Error parsing property.");
|
||||
input.parse_error("Error parsing property. Expected property value");
|
||||
valid = false;
|
||||
}
|
||||
input.next_token();
|
||||
}
|
||||
input.next_token();
|
||||
input.consume(';');
|
||||
}
|
||||
|
||||
@ -849,13 +916,17 @@ node::sort()
|
||||
}
|
||||
|
||||
node_ptr
|
||||
node::parse(input_buffer &input,
|
||||
string name,
|
||||
string label,
|
||||
string address,
|
||||
node::parse(text_input_buffer &input,
|
||||
string &&name,
|
||||
string_set &&label,
|
||||
string &&address,
|
||||
define_map *defines)
|
||||
{
|
||||
node_ptr n(new node(input, name, label, address, defines));
|
||||
node_ptr n(new node(input,
|
||||
std::move(name),
|
||||
std::move(label),
|
||||
std::move(address),
|
||||
defines));
|
||||
if (!n->valid)
|
||||
{
|
||||
n = 0;
|
||||
@ -875,7 +946,7 @@ node::parse_dtb(input_buffer &structs, input_buffer &strings)
|
||||
}
|
||||
|
||||
property_ptr
|
||||
node::get_property(string key)
|
||||
node::get_property(const string &key)
|
||||
{
|
||||
for (auto &i : props)
|
||||
{
|
||||
@ -890,9 +961,9 @@ node::get_property(string key)
|
||||
void
|
||||
node::merge_node(node_ptr other)
|
||||
{
|
||||
if (!other->label.empty())
|
||||
for (auto &l : other->labels)
|
||||
{
|
||||
label = other->label;
|
||||
labels.insert(l);
|
||||
}
|
||||
// Note: this is an O(n*m) operation. It might be sensible to
|
||||
// optimise this if we find that there are nodes with very
|
||||
@ -933,6 +1004,30 @@ node::merge_node(node_ptr other)
|
||||
children.push_back(std::move(c));
|
||||
}
|
||||
}
|
||||
children.erase(std::remove_if(children.begin(), children.end(),
|
||||
[&](const node_ptr &p) {
|
||||
string full_name = p->name;
|
||||
if (p->unit_address != string())
|
||||
{
|
||||
full_name += '@';
|
||||
full_name += p->unit_address;
|
||||
}
|
||||
if (other->deleted_children.count(full_name) > 0)
|
||||
{
|
||||
other->deleted_children.erase(full_name);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}), children.end());
|
||||
props.erase(std::remove_if(props.begin(), props.end(),
|
||||
[&](const property_ptr &p) {
|
||||
if (other->deleted_props.count(p->get_key()) > 0)
|
||||
{
|
||||
other->deleted_props.erase(p->get_key());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}), props.end());
|
||||
}
|
||||
|
||||
void
|
||||
@ -940,11 +1035,11 @@ node::write(dtb::output_writer &writer, dtb::string_table &strings)
|
||||
{
|
||||
writer.write_token(dtb::FDT_BEGIN_NODE);
|
||||
byte_buffer name_buffer;
|
||||
name.push_to_buffer(name_buffer);
|
||||
push_string(name_buffer, name);
|
||||
if (unit_address != string())
|
||||
{
|
||||
name_buffer.push_back('@');
|
||||
unit_address.push_to_buffer(name_buffer);
|
||||
push_string(name_buffer, unit_address);
|
||||
}
|
||||
writer.write_comment(name);
|
||||
writer.write_data(name_buffer);
|
||||
@ -968,20 +1063,19 @@ node::write_dts(FILE *file, int indent)
|
||||
putc('\t', file);
|
||||
}
|
||||
#ifdef PRINT_LABELS
|
||||
if (label != string())
|
||||
for (auto &label : labels)
|
||||
{
|
||||
label.print(file);
|
||||
fputs(": ", file);
|
||||
fprintf(file, "%s: ", label.c_str());
|
||||
}
|
||||
#endif
|
||||
if (name != string())
|
||||
{
|
||||
name.print(file);
|
||||
fputs(name.c_str(), file);
|
||||
}
|
||||
if (unit_address != string())
|
||||
{
|
||||
putc('@', file);
|
||||
unit_address.print(file);
|
||||
fputs(unit_address.c_str(), file);
|
||||
}
|
||||
fputs(" {\n\n", file);
|
||||
for (auto p : properties())
|
||||
@ -1002,26 +1096,27 @@ node::write_dts(FILE *file, int indent)
|
||||
void
|
||||
device_tree::collect_names_recursive(node_ptr &n, node_path &path)
|
||||
{
|
||||
string name = n->label;
|
||||
path.push_back(std::make_pair(n->name, n->unit_address));
|
||||
if (name != string())
|
||||
for (const string &name : n->labels)
|
||||
{
|
||||
if (node_names.find(name) == node_names.end())
|
||||
if (name != string())
|
||||
{
|
||||
node_names.insert(std::make_pair(name, n.get()));
|
||||
node_paths.insert(std::make_pair(name, path));
|
||||
}
|
||||
else
|
||||
{
|
||||
node_names[name] = (node*)-1;
|
||||
auto i = node_paths.find(name);
|
||||
if (i != node_paths.end())
|
||||
auto iter = node_names.find(name);
|
||||
if (iter == node_names.end())
|
||||
{
|
||||
node_paths.erase(name);
|
||||
node_names.insert(std::make_pair(name, n.get()));
|
||||
node_paths.insert(std::make_pair(name, path));
|
||||
}
|
||||
else
|
||||
{
|
||||
node_names.erase(iter);
|
||||
auto i = node_paths.find(name);
|
||||
if (i != node_paths.end())
|
||||
{
|
||||
node_paths.erase(name);
|
||||
}
|
||||
fprintf(stderr, "Label not unique: %s. References to this label will not be resolved.\n", name.c_str());
|
||||
}
|
||||
fprintf(stderr, "Label not unique: ");
|
||||
name.dump();
|
||||
fprintf(stderr, ". References to this label will not be resolved.");
|
||||
}
|
||||
}
|
||||
for (auto &c : n->child_nodes())
|
||||
@ -1044,14 +1139,12 @@ device_tree::collect_names_recursive(node_ptr &n, node_path &path)
|
||||
cross_references.push_back(&v);
|
||||
}
|
||||
}
|
||||
if (p->get_key() == string("phandle") ||
|
||||
p->get_key() == string("linux,phandle"))
|
||||
if ((p->get_key() == "phandle") ||
|
||||
(p->get_key() == "linux,phandle"))
|
||||
{
|
||||
if (p->begin()->byte_data.size() != 4)
|
||||
{
|
||||
fprintf(stderr, "Invalid phandle value for node ");
|
||||
n->name.dump();
|
||||
fprintf(stderr, ". Should be a 4-byte value.\n");
|
||||
fprintf(stderr, "Invalid phandle value for node %s. Should be a 4-byte value.\n", n->name.c_str());
|
||||
valid = false;
|
||||
}
|
||||
else
|
||||
@ -1080,16 +1173,21 @@ device_tree::resolve_cross_references()
|
||||
for (auto *pv : cross_references)
|
||||
{
|
||||
node_path path = node_paths[pv->string_data];
|
||||
// Skip the first name in the path. It's always "", and implicitly /
|
||||
for (auto p=path.begin()+1, pe=path.end() ; p!=pe ; ++p)
|
||||
auto p = path.begin();
|
||||
auto pe = path.end();
|
||||
if (p != pe)
|
||||
{
|
||||
pv->byte_data.push_back('/');
|
||||
p->first.push_to_buffer(pv->byte_data);
|
||||
if (!(p->second.empty()))
|
||||
// Skip the first name in the path. It's always "", and implicitly /
|
||||
for (++p ; p!=pe ; ++p)
|
||||
{
|
||||
pv->byte_data.push_back('@');
|
||||
p->second.push_to_buffer(pv->byte_data);
|
||||
pv->byte_data.push_back(0);
|
||||
pv->byte_data.push_back('/');
|
||||
push_string(pv->byte_data, p->first);
|
||||
if (!(p->second.empty()))
|
||||
{
|
||||
pv->byte_data.push_back('@');
|
||||
push_string(pv->byte_data, p->second);
|
||||
pv->byte_data.push_back(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1117,12 +1215,72 @@ device_tree::resolve_cross_references()
|
||||
for (auto &i : sorted_phandles)
|
||||
{
|
||||
string target_name = i->string_data;
|
||||
node *target = node_names[target_name];
|
||||
if (target == 0)
|
||||
node *target = nullptr;
|
||||
string possible;
|
||||
// If the node name is a path, then look it up by following the path,
|
||||
// otherwise jump directly to the named node.
|
||||
if (target_name[0] == '/')
|
||||
{
|
||||
fprintf(stderr, "Failed to find node with label: ");
|
||||
target_name.dump();
|
||||
fprintf(stderr, "\n");
|
||||
std::string path;
|
||||
target = root.get();
|
||||
std::istringstream ss(target_name);
|
||||
string path_element;
|
||||
// Read the leading /
|
||||
std::getline(ss, path_element, '/');
|
||||
// Iterate over path elements
|
||||
while (!ss.eof())
|
||||
{
|
||||
path += '/';
|
||||
std::getline(ss, path_element, '/');
|
||||
std::istringstream nss(path_element);
|
||||
string node_name, node_address;
|
||||
std::getline(nss, node_name, '@');
|
||||
std::getline(nss, node_address, '@');
|
||||
node *next = nullptr;
|
||||
for (auto &c : target->child_nodes())
|
||||
{
|
||||
if (c->name == node_name)
|
||||
{
|
||||
if (c->unit_address == node_address)
|
||||
{
|
||||
next = c.get();
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
possible = path + c->name;
|
||||
if (c->unit_address != string())
|
||||
{
|
||||
possible += '@';
|
||||
possible += c->unit_address;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
path += node_name;
|
||||
if (node_address != string())
|
||||
{
|
||||
path += '@';
|
||||
path += node_address;
|
||||
}
|
||||
target = next;
|
||||
if (target == nullptr)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
target = node_names[target_name];
|
||||
}
|
||||
if (target == nullptr)
|
||||
{
|
||||
fprintf(stderr, "Failed to find node with label: %s\n", target_name.c_str());
|
||||
if (possible != string())
|
||||
{
|
||||
fprintf(stderr, "Possible intended match: %s\n", possible.c_str());
|
||||
}
|
||||
valid = 0;
|
||||
return;
|
||||
}
|
||||
@ -1153,13 +1311,13 @@ device_tree::resolve_cross_references()
|
||||
push_big_endian(v.byte_data, phandle++);
|
||||
if (phandle_node_name == BOTH || phandle_node_name == LINUX)
|
||||
{
|
||||
p.reset(new property(string("linux,phandle")));
|
||||
p.reset(new property("linux,phandle"));
|
||||
p->add_value(v);
|
||||
target->add_property(p);
|
||||
}
|
||||
if (phandle_node_name == BOTH || phandle_node_name == EPAPR)
|
||||
{
|
||||
p.reset(new property(string("phandle")));
|
||||
p.reset(new property("phandle"));
|
||||
p->add_value(v);
|
||||
target->add_property(p);
|
||||
}
|
||||
@ -1169,87 +1327,10 @@ device_tree::resolve_cross_references()
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
device_tree::parse_include(input_buffer &input,
|
||||
const std::string &dir,
|
||||
std::vector<node_ptr> &roots,
|
||||
FILE *depfile,
|
||||
bool &read_header)
|
||||
{
|
||||
if (!input.consume("/include/"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool reallyInclude = true;
|
||||
if (input.consume("if "))
|
||||
{
|
||||
input.next_token();
|
||||
string name = string::parse_property_name(input);
|
||||
// XXX: Error handling
|
||||
if (defines.find(name) == defines.end())
|
||||
{
|
||||
reallyInclude = false;
|
||||
}
|
||||
input.consume('/');
|
||||
}
|
||||
input.next_token();
|
||||
if (!input.consume('"'))
|
||||
{
|
||||
input.parse_error("Expected quoted filename");
|
||||
valid = false;
|
||||
return false;
|
||||
}
|
||||
int length = 0;
|
||||
while (input[length] != '"') length++;
|
||||
|
||||
std::string file((const char*)input, length);
|
||||
std::string include_file = dir + '/' + file;
|
||||
input.consume(file.c_str());
|
||||
if (!reallyInclude)
|
||||
{
|
||||
input.consume('"');
|
||||
input.next_token();
|
||||
return true;
|
||||
}
|
||||
|
||||
input_buffer *include_buffer = buffer_for_file(include_file.c_str(), false);
|
||||
|
||||
if (include_buffer == 0)
|
||||
{
|
||||
for (auto i : include_paths)
|
||||
{
|
||||
include_file = i + '/' + file;
|
||||
include_buffer = buffer_for_file(include_file.c_str());
|
||||
if (include_buffer != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (depfile != 0)
|
||||
{
|
||||
putc(' ', depfile);
|
||||
fputs(include_file.c_str(), depfile);
|
||||
}
|
||||
if (include_buffer == 0)
|
||||
{
|
||||
input.parse_error("Unable to locate input file");
|
||||
input.consume('"');
|
||||
input.next_token();
|
||||
valid = false;
|
||||
return true;
|
||||
}
|
||||
input.consume('"');
|
||||
input.next_token();
|
||||
parse_file(*include_buffer, dir, roots, depfile, read_header);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
device_tree::parse_file(input_buffer &input,
|
||||
const std::string &dir,
|
||||
device_tree::parse_file(text_input_buffer &input,
|
||||
std::vector<node_ptr> &roots,
|
||||
FILE *depfile,
|
||||
bool &read_header)
|
||||
{
|
||||
input.next_token();
|
||||
@ -1264,9 +1345,8 @@ device_tree::parse_file(input_buffer &input,
|
||||
{
|
||||
input.parse_error("Expected /dts-v1/; version string");
|
||||
}
|
||||
while(parse_include(input, dir, roots, depfile, read_header)) {}
|
||||
// Read any memory reservations
|
||||
while(input.consume("/memreserve/"))
|
||||
while (input.consume("/memreserve/"))
|
||||
{
|
||||
unsigned long long start, len;
|
||||
input.next_token();
|
||||
@ -1280,23 +1360,22 @@ device_tree::parse_file(input_buffer &input,
|
||||
input.next_token();
|
||||
input.consume(';');
|
||||
reservations.push_back(reservation(start, len));
|
||||
input.next_token();
|
||||
}
|
||||
input.next_token();
|
||||
while(parse_include(input, dir, roots, depfile, read_header)) {}
|
||||
while (valid && !input.finished())
|
||||
{
|
||||
node_ptr n;
|
||||
if (input.consume('/'))
|
||||
{
|
||||
input.next_token();
|
||||
n = node::parse(input, string(), string(), string(), &defines);
|
||||
n = node::parse(input, string(), string_set(), string(), &defines);
|
||||
}
|
||||
else if (input.consume('&'))
|
||||
{
|
||||
input.next_token();
|
||||
string name = string::parse_node_name(input);
|
||||
string name = input.parse_node_name();
|
||||
input.next_token();
|
||||
n = node::parse(input, name, string(), string(), &defines);
|
||||
n = node::parse(input, std::move(name), string_set(), string(), &defines);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1311,52 +1390,9 @@ device_tree::parse_file(input_buffer &input,
|
||||
valid = false;
|
||||
}
|
||||
input.next_token();
|
||||
while(parse_include(input, dir, roots, depfile, read_header)) {}
|
||||
}
|
||||
}
|
||||
|
||||
input_buffer*
|
||||
device_tree::buffer_for_file(const char *path, bool warn)
|
||||
{
|
||||
if (string(path) == string("-"))
|
||||
{
|
||||
input_buffer *b = new stream_input_buffer();
|
||||
if (b)
|
||||
{
|
||||
std::unique_ptr<input_buffer> ptr(b);
|
||||
buffers.push_back(std::move(ptr));
|
||||
}
|
||||
return b;
|
||||
}
|
||||
int source = open(path, O_RDONLY);
|
||||
if (source == -1)
|
||||
{
|
||||
if (warn)
|
||||
{
|
||||
fprintf(stderr, "Unable to open file '%s'. %s\n", path, strerror(errno));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
struct stat st;
|
||||
if (fstat(source, &st) == 0 && S_ISDIR(st.st_mode))
|
||||
{
|
||||
fprintf(stderr, "File %s is a directory\n", path);
|
||||
close(source);
|
||||
return 0;
|
||||
}
|
||||
input_buffer *b = new mmap_input_buffer(source);
|
||||
// Keep the buffer that owns the memory around for the lifetime
|
||||
// of this FDT. Ones simply referring to it may have shorter
|
||||
// lifetimes.
|
||||
if (b)
|
||||
{
|
||||
std::unique_ptr<input_buffer> ptr(b);
|
||||
buffers.push_back(std::move(ptr));
|
||||
}
|
||||
close(source);
|
||||
return b;
|
||||
}
|
||||
|
||||
template<class writer> void
|
||||
device_tree::write(int fd)
|
||||
{
|
||||
@ -1475,9 +1511,9 @@ device_tree::write_dts(int fd)
|
||||
}
|
||||
|
||||
void
|
||||
device_tree::parse_dtb(const char *fn, FILE *)
|
||||
device_tree::parse_dtb(const string &fn, FILE *)
|
||||
{
|
||||
input_buffer *in = buffer_for_file(fn);
|
||||
auto in = input_buffer::buffer_for_file(fn);
|
||||
if (in == 0)
|
||||
{
|
||||
valid = false;
|
||||
@ -1532,19 +1568,27 @@ device_tree::parse_dtb(const char *fn, FILE *)
|
||||
}
|
||||
|
||||
void
|
||||
device_tree::parse_dts(const char *fn, FILE *depfile)
|
||||
device_tree::parse_dts(const string &fn, FILE *depfile)
|
||||
{
|
||||
input_buffer *in = buffer_for_file(fn);
|
||||
std::string dir(dirname((char*)fn));
|
||||
if (in == 0)
|
||||
auto in = input_buffer::buffer_for_file(fn);
|
||||
if (!in)
|
||||
{
|
||||
valid = false;
|
||||
return;
|
||||
}
|
||||
std::vector<node_ptr> roots;
|
||||
input_buffer &input = *in;
|
||||
std::unordered_set<string> defnames;
|
||||
for (auto &i : defines)
|
||||
{
|
||||
defnames.insert(i.first);
|
||||
}
|
||||
text_input_buffer input(std::move(in),
|
||||
std::move(defnames),
|
||||
std::vector<string>(include_paths),
|
||||
dirname(fn),
|
||||
depfile);
|
||||
bool read_header = false;
|
||||
parse_file(input, dir, roots, depfile, read_header);
|
||||
parse_file(input, roots, read_header);
|
||||
switch (roots.size())
|
||||
{
|
||||
case 0:
|
||||
@ -1575,11 +1619,12 @@ device_tree::parse_dts(const char *fn, FILE *depfile)
|
||||
}
|
||||
if (existing == node_names.end())
|
||||
{
|
||||
fprintf(stderr, "Unable to merge node: ");
|
||||
name.dump();
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Unable to merge node: %s\n", name.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
existing->second->merge_node(std::move(node));
|
||||
}
|
||||
existing->second->merge_node(std::move(node));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1602,9 +1647,15 @@ bool device_tree::parse_define(const char *def)
|
||||
return false;
|
||||
}
|
||||
string name(def, val-def);
|
||||
string name_copy = name;
|
||||
val++;
|
||||
input_buffer in = input_buffer(val, strlen(val));
|
||||
property_ptr p = property::parse(in, name, string(), false);
|
||||
std::unique_ptr<input_buffer> raw(new input_buffer(val, strlen(val)));
|
||||
text_input_buffer in(std::move(raw),
|
||||
std::unordered_set<string>(),
|
||||
std::vector<string>(),
|
||||
std::string(),
|
||||
nullptr);
|
||||
property_ptr p = property::parse(in, std::move(name_copy), string_set(), false);
|
||||
if (p)
|
||||
defines[name] = p;
|
||||
return (bool)p;
|
||||
|
@ -39,7 +39,7 @@
|
||||
#include <functional>
|
||||
|
||||
#include "util.hh"
|
||||
#include "string.hh"
|
||||
#include "input_buffer.hh"
|
||||
|
||||
namespace dtc
|
||||
{
|
||||
@ -65,7 +65,11 @@ typedef std::unique_ptr<node> node_ptr;
|
||||
/**
|
||||
* Map from macros to property pointers.
|
||||
*/
|
||||
typedef std::unordered_map<string, property_ptr> define_map;
|
||||
typedef std::unordered_map<std::string, property_ptr> define_map;
|
||||
/**
|
||||
* Set of strings used for label names.
|
||||
*/
|
||||
typedef std::unordered_set<std::string> string_set;
|
||||
/**
|
||||
* Properties may contain a number of different value, each with a different
|
||||
* label. This class encapsulates a single value.
|
||||
@ -75,12 +79,12 @@ struct property_value
|
||||
/**
|
||||
* The label for this data. This is usually empty.
|
||||
*/
|
||||
string label;
|
||||
std::string label;
|
||||
/**
|
||||
* If this value is a string, or something resolved from a string (a
|
||||
* reference) then this contains the source string.
|
||||
*/
|
||||
string string_data;
|
||||
std::string string_data;
|
||||
/**
|
||||
* The data that should be written to the final output.
|
||||
*/
|
||||
@ -186,7 +190,7 @@ struct property_value
|
||||
/**
|
||||
* Default constructor, specifying the label of the value.
|
||||
*/
|
||||
property_value(string l=string()) : label(l), type(UNKNOWN) {}
|
||||
property_value(std::string l=std::string()) : label(l), type(UNKNOWN) {}
|
||||
/**
|
||||
* Writes the data for this value into an output buffer.
|
||||
*/
|
||||
@ -250,11 +254,11 @@ class property
|
||||
/**
|
||||
* The name of this property.
|
||||
*/
|
||||
string key;
|
||||
std::string key;
|
||||
/**
|
||||
* An optional label.
|
||||
* Zero or more labels.
|
||||
*/
|
||||
string label;
|
||||
string_set labels;
|
||||
/**
|
||||
* The values in this property.
|
||||
*/
|
||||
@ -267,15 +271,15 @@ class property
|
||||
/**
|
||||
* Parses a string property value, i.e. a value enclosed in double quotes.
|
||||
*/
|
||||
void parse_string(input_buffer &input);
|
||||
void parse_string(text_input_buffer &input);
|
||||
/**
|
||||
* Parses one or more 32-bit values enclosed in angle brackets.
|
||||
*/
|
||||
void parse_cells(input_buffer &input, int cell_size);
|
||||
void parse_cells(text_input_buffer &input, int cell_size);
|
||||
/**
|
||||
* Parses an array of bytes, contained within square brackets.
|
||||
*/
|
||||
void parse_bytes(input_buffer &input);
|
||||
void parse_bytes(text_input_buffer &input);
|
||||
/**
|
||||
* Parses a reference. This is a node label preceded by an ampersand
|
||||
* symbol, which should expand to the full path to that node.
|
||||
@ -284,11 +288,11 @@ class property
|
||||
* a node name, however dtc assumes that it is a label, and so we
|
||||
* follow their interpretation for compatibility.
|
||||
*/
|
||||
void parse_reference(input_buffer &input);
|
||||
void parse_reference(text_input_buffer &input);
|
||||
/**
|
||||
* Parse a predefined macro definition for a property.
|
||||
*/
|
||||
void parse_define(input_buffer &input, define_map *defines);
|
||||
void parse_define(text_input_buffer &input, define_map *defines);
|
||||
/**
|
||||
* Constructs a new property from two input buffers, pointing to the
|
||||
* struct and strings tables in the device tree blob, respectively.
|
||||
@ -299,21 +303,21 @@ class property
|
||||
/**
|
||||
* Parses a new property from the input buffer.
|
||||
*/
|
||||
property(input_buffer &input,
|
||||
string k,
|
||||
string l,
|
||||
property(text_input_buffer &input,
|
||||
std::string &&k,
|
||||
string_set &&l,
|
||||
bool terminated,
|
||||
define_map *defines);
|
||||
public:
|
||||
/**
|
||||
* Creates an empty property.
|
||||
*/
|
||||
property(string k, string l=string()) : key(k), label(l), valid(true)
|
||||
{}
|
||||
property(std::string &&k, string_set &&l=string_set())
|
||||
: key(k), labels(l), valid(true) {}
|
||||
/**
|
||||
* Copy constructor.
|
||||
*/
|
||||
property(property &p) : key(p.key), label(p.label), values(p.values),
|
||||
property(property &p) : key(p.key), labels(p.labels), values(p.values),
|
||||
valid(p.valid) {}
|
||||
/**
|
||||
* Factory method for constructing a new property. Attempts to parse a
|
||||
@ -321,15 +325,15 @@ class property
|
||||
* error, this will return 0.
|
||||
*/
|
||||
static property_ptr parse_dtb(input_buffer &structs,
|
||||
input_buffer &strings);
|
||||
input_buffer &strings);
|
||||
/**
|
||||
* Factory method for constructing a new property. Attempts to parse a
|
||||
* property from the input, and returns it on success. On any parse
|
||||
* error, this will return 0.
|
||||
*/
|
||||
static property_ptr parse(input_buffer &input,
|
||||
string key,
|
||||
string label=string(),
|
||||
static property_ptr parse(text_input_buffer &input,
|
||||
std::string &&key,
|
||||
string_set &&labels=string_set(),
|
||||
bool semicolonTerminated=true,
|
||||
define_map *defines=0);
|
||||
/**
|
||||
@ -360,7 +364,7 @@ class property
|
||||
/**
|
||||
* Returns the key for this property.
|
||||
*/
|
||||
inline string get_key()
|
||||
inline std::string get_key()
|
||||
{
|
||||
return key;
|
||||
}
|
||||
@ -386,19 +390,19 @@ class node
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* The label for this node, if any. Node labels are used as the
|
||||
* The labels for this node, if any. Node labels are used as the
|
||||
* targets for cross references.
|
||||
*/
|
||||
string label;
|
||||
std::unordered_set<std::string> labels;
|
||||
/**
|
||||
* The name of the node.
|
||||
*/
|
||||
string name;
|
||||
std::string name;
|
||||
/**
|
||||
* The unit address of the node, which is optionally written after the
|
||||
* name followed by an at symbol.
|
||||
*/
|
||||
string unit_address;
|
||||
std::string unit_address;
|
||||
/**
|
||||
* The type for the property vector.
|
||||
*/
|
||||
@ -438,6 +442,14 @@ class node
|
||||
* The children of this node.
|
||||
*/
|
||||
std::vector<node_ptr> children;
|
||||
/**
|
||||
* Children that should be deleted from this node when merging.
|
||||
*/
|
||||
std::unordered_set<std::string> deleted_children;
|
||||
/**
|
||||
* Properties that should be deleted from this node when merging.
|
||||
*/
|
||||
std::unordered_set<std::string> deleted_props;
|
||||
/**
|
||||
* A flag indicating whether this node is valid. This is set to false
|
||||
* if an error occurs during parsing.
|
||||
@ -447,9 +459,9 @@ class node
|
||||
* Parses a name inside a node, writing the string passed as the last
|
||||
* argument as an error if it fails.
|
||||
*/
|
||||
string parse_name(input_buffer &input,
|
||||
bool &is_property,
|
||||
const char *error);
|
||||
std::string parse_name(text_input_buffer &input,
|
||||
bool &is_property,
|
||||
const char *error);
|
||||
/**
|
||||
* Constructs a new node from two input buffers, pointing to the struct
|
||||
* and strings tables in the device tree blob, respectively.
|
||||
@ -461,7 +473,11 @@ class node
|
||||
* node. The name, and optionally label and unit address, should have
|
||||
* already been parsed.
|
||||
*/
|
||||
node(input_buffer &input, string n, string l, string a, define_map*);
|
||||
node(text_input_buffer &input,
|
||||
std::string &&n,
|
||||
std::unordered_set<std::string> &&l,
|
||||
std::string &&a,
|
||||
define_map*);
|
||||
/**
|
||||
* Comparison function for properties, used when sorting the properties
|
||||
* vector. Orders the properties based on their names.
|
||||
@ -498,10 +514,32 @@ class node
|
||||
{
|
||||
return children.end();
|
||||
}
|
||||
/**
|
||||
* Returns a range suitable for use in a range-based for loop describing
|
||||
* the children of this node.
|
||||
*/
|
||||
inline child_range child_nodes()
|
||||
{
|
||||
return child_range(*this);
|
||||
}
|
||||
/**
|
||||
* Accessor for the deleted children.
|
||||
*/
|
||||
inline const std::unordered_set<std::string> &deleted_child_nodes()
|
||||
{
|
||||
return deleted_children;
|
||||
}
|
||||
/**
|
||||
* Accessor for the deleted properties
|
||||
*/
|
||||
inline const std::unordered_set<std::string> &deleted_properties()
|
||||
{
|
||||
return deleted_props;
|
||||
}
|
||||
/**
|
||||
* Returns a range suitable for use in a range-based for loop describing
|
||||
* the properties of this node.
|
||||
*/
|
||||
inline property_range properties()
|
||||
{
|
||||
return property_range(*this);
|
||||
@ -527,10 +565,10 @@ class node
|
||||
* cursor on the open brace of the property, after the name and so on
|
||||
* have been parsed.
|
||||
*/
|
||||
static node_ptr parse(input_buffer &input,
|
||||
string name,
|
||||
string label=string(),
|
||||
string address=string(),
|
||||
static node_ptr parse(text_input_buffer &input,
|
||||
std::string &&name,
|
||||
std::unordered_set<std::string> &&label=std::unordered_set<std::string>(),
|
||||
std::string &&address=std::string(),
|
||||
define_map *defines=0);
|
||||
/**
|
||||
* Factory method for constructing a new node. Attempts to parse a
|
||||
@ -544,7 +582,7 @@ class node
|
||||
* Returns a property corresponding to the specified key, or 0 if this
|
||||
* node does not contain a property of that name.
|
||||
*/
|
||||
property_ptr get_property(string key);
|
||||
property_ptr get_property(const std::string &key);
|
||||
/**
|
||||
* Adds a new property to this node.
|
||||
*/
|
||||
@ -588,7 +626,7 @@ class device_tree
|
||||
* Type used for node paths. A node path is sequence of names and unit
|
||||
* addresses.
|
||||
*/
|
||||
typedef std::vector<std::pair<string,string> > node_path;
|
||||
typedef std::vector<std::pair<std::string,std::string> > node_path;
|
||||
/**
|
||||
* Name that we should use for phandle nodes.
|
||||
*/
|
||||
@ -630,13 +668,13 @@ class device_tree
|
||||
* Mapping from names to nodes. Only unambiguous names are recorded,
|
||||
* duplicate names are stored as (node*)-1.
|
||||
*/
|
||||
std::unordered_map<string, node*> node_names;
|
||||
std::unordered_map<std::string, node*> node_names;
|
||||
/**
|
||||
* A map from labels to node paths. When resolving cross references,
|
||||
* we look up referenced nodes in this and replace the cross reference
|
||||
* with the full path to its target.
|
||||
*/
|
||||
std::unordered_map<string, node_path> node_paths;
|
||||
std::unordered_map<std::string, node_path> node_paths;
|
||||
/**
|
||||
* A collection of property values that are references to other nodes.
|
||||
* These should be expanded to the full path of their targets.
|
||||
@ -651,7 +689,7 @@ class device_tree
|
||||
/**
|
||||
* The names of nodes that target phandles.
|
||||
*/
|
||||
std::unordered_set<string> phandle_targets;
|
||||
std::unordered_set<std::string> phandle_targets;
|
||||
/**
|
||||
* A collection of input buffers that we are using. These input
|
||||
* buffers are the ones that own their memory, and so we must preserve
|
||||
@ -717,31 +755,15 @@ class device_tree
|
||||
* phandle value.
|
||||
*/
|
||||
void resolve_cross_references();
|
||||
/**
|
||||
* Parse a top-level include directive.
|
||||
*/
|
||||
bool parse_include(input_buffer &input,
|
||||
const std::string &dir,
|
||||
std::vector<node_ptr> &roots,
|
||||
FILE *depfile,
|
||||
bool &read_header);
|
||||
/**
|
||||
* Parses a dts file in the given buffer and adds the roots to the parsed
|
||||
* set. The `read_header` argument indicates whether the header has
|
||||
* already been read. Some dts files place the header in an include,
|
||||
* rather than in the top-level file.
|
||||
*/
|
||||
void parse_file(input_buffer &input,
|
||||
const std::string &dir,
|
||||
void parse_file(text_input_buffer &input,
|
||||
std::vector<node_ptr> &roots,
|
||||
FILE *depfile,
|
||||
bool &read_header);
|
||||
/**
|
||||
* Allocates a new mmap()'d input buffer for use in parsing. This
|
||||
* object then keeps a reference to it, ensuring that it is not
|
||||
* deallocated until the device tree is destroyed.
|
||||
*/
|
||||
input_buffer *buffer_for_file(const char *path, bool warn=true);
|
||||
/**
|
||||
* Template function that writes a dtb blob using the specified writer.
|
||||
* The writer defines the output format (assembly, blob).
|
||||
@ -779,12 +801,12 @@ class device_tree
|
||||
* Constructs a device tree from the specified file name, referring to
|
||||
* a file that contains a device tree blob.
|
||||
*/
|
||||
void parse_dtb(const char *fn, FILE *depfile);
|
||||
void parse_dtb(const std::string &fn, FILE *depfile);
|
||||
/**
|
||||
* Constructs a device tree from the specified file name, referring to
|
||||
* a file that contains device tree source.
|
||||
*/
|
||||
void parse_dts(const char *fn, FILE *depfile);
|
||||
void parse_dts(const std::string &fn, FILE *depfile);
|
||||
/**
|
||||
* Returns whether this tree is valid.
|
||||
*/
|
||||
|
@ -46,37 +46,215 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef MAP_PREFAULT_READ
|
||||
#define MAP_PREFAULT_READ 0
|
||||
#endif
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace
|
||||
{
|
||||
/**
|
||||
* Subclass of input_buffer that mmap()s a file and owns the resulting memory.
|
||||
* When this object is destroyed, the memory is unmapped.
|
||||
*/
|
||||
struct mmap_input_buffer : public dtc::input_buffer
|
||||
{
|
||||
string fn;
|
||||
const string &filename() const override
|
||||
{
|
||||
return fn;
|
||||
}
|
||||
/**
|
||||
* Constructs a new buffer from the file passed in as a file
|
||||
* descriptor.
|
||||
*/
|
||||
mmap_input_buffer(int fd, string &&filename);
|
||||
/**
|
||||
* Unmaps the buffer, if one exists.
|
||||
*/
|
||||
virtual ~mmap_input_buffer();
|
||||
};
|
||||
/**
|
||||
* Input buffer read from standard input. This is used for reading device tree
|
||||
* blobs and source from standard input. It reads the entire input into
|
||||
* malloc'd memory, so will be very slow for large inputs. DTS and DTB files
|
||||
* are very rarely more than 10KB though, so this is probably not a problem.
|
||||
*/
|
||||
struct stream_input_buffer : public dtc::input_buffer
|
||||
{
|
||||
const string &filename() const override
|
||||
{
|
||||
static string n = "<standard input>";
|
||||
return n;
|
||||
}
|
||||
/**
|
||||
* The buffer that will store the data read from the standard input.
|
||||
*/
|
||||
std::vector<char> b;
|
||||
/**
|
||||
* Constructs a new buffer from the standard input.
|
||||
*/
|
||||
stream_input_buffer();
|
||||
};
|
||||
|
||||
mmap_input_buffer::mmap_input_buffer(int fd, std::string &&filename)
|
||||
: input_buffer(0, 0), fn(filename)
|
||||
{
|
||||
struct stat sb;
|
||||
if (fstat(fd, &sb))
|
||||
{
|
||||
perror("Failed to stat file");
|
||||
}
|
||||
size = sb.st_size;
|
||||
buffer = (const char*)mmap(0, size, PROT_READ, MAP_PRIVATE |
|
||||
MAP_PREFAULT_READ, fd, 0);
|
||||
if (buffer == MAP_FAILED)
|
||||
{
|
||||
perror("Failed to mmap file");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
mmap_input_buffer::~mmap_input_buffer()
|
||||
{
|
||||
if (buffer != 0)
|
||||
{
|
||||
munmap((void*)buffer, size);
|
||||
}
|
||||
}
|
||||
|
||||
stream_input_buffer::stream_input_buffer() : input_buffer(0, 0)
|
||||
{
|
||||
int c;
|
||||
while ((c = fgetc(stdin)) != EOF)
|
||||
{
|
||||
b.push_back(c);
|
||||
}
|
||||
buffer = b.data();
|
||||
size = b.size();
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
|
||||
namespace dtc
|
||||
{
|
||||
|
||||
void
|
||||
input_buffer::skip_spaces()
|
||||
input_buffer::skip_to(char c)
|
||||
{
|
||||
if (cursor >= size) { return; }
|
||||
if (cursor < 0) { return; }
|
||||
char c = buffer[cursor];
|
||||
while ((cursor < size) && (buffer[cursor] != c))
|
||||
{
|
||||
cursor++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
text_input_buffer::skip_to(char c)
|
||||
{
|
||||
while (!finished() && (*(*this) != c))
|
||||
{
|
||||
++(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
text_input_buffer::skip_spaces()
|
||||
{
|
||||
if (finished()) { return; }
|
||||
char c = *(*this);
|
||||
bool last_nl = false;
|
||||
while ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\f')
|
||||
|| (c == '\v') || (c == '\r'))
|
||||
{
|
||||
cursor++;
|
||||
if (cursor > size)
|
||||
last_nl = ((c == '\n') || (c == '\r'));
|
||||
++(*this);
|
||||
if (finished())
|
||||
{
|
||||
c = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
c = buffer[cursor];
|
||||
c = *(*this);
|
||||
}
|
||||
}
|
||||
// Skip C preprocessor leftovers
|
||||
if ((c == '#') && ((cursor == 0) || last_nl))
|
||||
{
|
||||
skip_to('\n');
|
||||
skip_spaces();
|
||||
}
|
||||
if (consume("/include/"))
|
||||
{
|
||||
handle_include();
|
||||
skip_spaces();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
text_input_buffer::handle_include()
|
||||
{
|
||||
bool reallyInclude = true;
|
||||
if (consume("if "))
|
||||
{
|
||||
next_token();
|
||||
string name = parse_property_name();
|
||||
if (defines.count(name) > 0)
|
||||
{
|
||||
reallyInclude = true;
|
||||
}
|
||||
consume('/');
|
||||
}
|
||||
next_token();
|
||||
if (!consume('"'))
|
||||
{
|
||||
parse_error("Expected quoted filename");
|
||||
return;
|
||||
}
|
||||
string file = parse_to('"');
|
||||
consume('"');
|
||||
if (!reallyInclude)
|
||||
{
|
||||
return;
|
||||
}
|
||||
string include_file = dir + '/' + file;
|
||||
auto include_buffer = input_buffer::buffer_for_file(include_file, false);
|
||||
if (include_buffer == 0)
|
||||
{
|
||||
for (auto i : include_paths)
|
||||
{
|
||||
include_file = i + '/' + file;
|
||||
include_buffer = input_buffer::buffer_for_file(include_file, false);
|
||||
if (include_buffer != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (depfile)
|
||||
{
|
||||
putc(' ', depfile);
|
||||
fputs(include_file.c_str(), depfile);
|
||||
}
|
||||
if (!include_buffer)
|
||||
{
|
||||
parse_error("Unable to locate input file");
|
||||
return;
|
||||
}
|
||||
input_stack.push(std::move(include_buffer));
|
||||
}
|
||||
|
||||
input_buffer
|
||||
input_buffer::buffer_from_offset(int offset, int s)
|
||||
{
|
||||
if (offset < 0)
|
||||
{
|
||||
return input_buffer();
|
||||
}
|
||||
if (s == 0)
|
||||
{
|
||||
s = size - offset;
|
||||
@ -104,7 +282,7 @@ input_buffer::consume(const char *str)
|
||||
{
|
||||
for (int i=0 ; i<len ; ++i)
|
||||
{
|
||||
if (str[i] != buffer[cursor + i])
|
||||
if (str[i] != (*this)[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -124,7 +302,7 @@ input_buffer::consume_integer(unsigned long long &outInt)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
char *end=0;
|
||||
char *end= const_cast<char*>(&buffer[size]);
|
||||
outInt = strtoull(&buffer[cursor], &end, 0);
|
||||
if (end == &buffer[cursor])
|
||||
{
|
||||
@ -146,15 +324,27 @@ typedef unsigned long long valty;
|
||||
*/
|
||||
struct expression
|
||||
{
|
||||
typedef text_input_buffer::source_location source_location;
|
||||
/**
|
||||
* The type that is returned when computing the result. The boolean value
|
||||
* indicates whether this is a valid expression.
|
||||
*
|
||||
* FIXME: Once we can use C++17, this should be `std::optional`.
|
||||
*/
|
||||
typedef std::pair<valty, bool> result;
|
||||
/**
|
||||
* Evaluate this node, taking into account operator precedence.
|
||||
*/
|
||||
virtual valty operator()() = 0;
|
||||
virtual result operator()() = 0;
|
||||
/**
|
||||
* Returns the precedence of this node. Lower values indicate higher
|
||||
* precedence.
|
||||
*/
|
||||
virtual int precedence() = 0;
|
||||
/**
|
||||
* Constructs an expression, storing the location where it was created.
|
||||
*/
|
||||
expression(source_location l) : loc(l) {}
|
||||
virtual ~expression() {}
|
||||
#ifndef NDEBUG
|
||||
/**
|
||||
@ -163,7 +353,8 @@ struct expression
|
||||
*/
|
||||
void dump(bool nl=false)
|
||||
{
|
||||
if (this == nullptr)
|
||||
void *ptr = this;
|
||||
if (ptr == nullptr)
|
||||
{
|
||||
std::cerr << "{nullptr}\n";
|
||||
return;
|
||||
@ -180,6 +371,8 @@ struct expression
|
||||
*/
|
||||
virtual void dump_impl() = 0;
|
||||
#endif
|
||||
protected:
|
||||
source_location loc;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -194,9 +387,9 @@ class terminal_expr : public expression
|
||||
/**
|
||||
* Evaluate. Trivially returns the value that this class wraps.
|
||||
*/
|
||||
valty operator()() override
|
||||
result operator()() override
|
||||
{
|
||||
return val;
|
||||
return {val, true};
|
||||
}
|
||||
int precedence() override
|
||||
{
|
||||
@ -206,7 +399,7 @@ class terminal_expr : public expression
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
terminal_expr(valty v) : val(v) {}
|
||||
terminal_expr(source_location l, valty v) : expression(l), val(v) {}
|
||||
#ifndef NDEBUG
|
||||
void dump_impl() override { std::cerr << val; }
|
||||
#endif
|
||||
@ -224,7 +417,8 @@ struct paren_expression : public expression
|
||||
/**
|
||||
* Constructor. Takes the child expression as the only argument.
|
||||
*/
|
||||
paren_expression(expression_ptr p) : subexpr(std::move(p)) {}
|
||||
paren_expression(source_location l, expression_ptr p) : expression(l),
|
||||
subexpr(std::move(p)) {}
|
||||
int precedence() override
|
||||
{
|
||||
return 0;
|
||||
@ -232,7 +426,7 @@ struct paren_expression : public expression
|
||||
/**
|
||||
* Evaluate - just forwards to the underlying expression.
|
||||
*/
|
||||
valty operator()() override
|
||||
result operator()() override
|
||||
{
|
||||
return (*subexpr)();
|
||||
}
|
||||
@ -260,10 +454,15 @@ class unary_operator : public expression
|
||||
* The subexpression for this unary operator.
|
||||
*/
|
||||
expression_ptr subexpr;
|
||||
valty operator()() override
|
||||
result operator()() override
|
||||
{
|
||||
Op op;
|
||||
return op((*subexpr)());
|
||||
result s = (*subexpr)();
|
||||
if (!s.second)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
return {op(s.first), true};
|
||||
}
|
||||
/**
|
||||
* All unary operators have the same precedence. They are all evaluated
|
||||
@ -274,7 +473,8 @@ class unary_operator : public expression
|
||||
return 3;
|
||||
}
|
||||
public:
|
||||
unary_operator(expression_ptr p) : subexpr(std::move(p)) {}
|
||||
unary_operator(source_location l, expression_ptr p) :
|
||||
expression(l), subexpr(std::move(p)) {}
|
||||
#ifndef NDEBUG
|
||||
void dump_impl() override
|
||||
{
|
||||
@ -290,6 +490,7 @@ class unary_operator : public expression
|
||||
*/
|
||||
struct binary_operator_base : public expression
|
||||
{
|
||||
using expression::expression;
|
||||
/**
|
||||
* The left side of the expression.
|
||||
*/
|
||||
@ -323,10 +524,16 @@ struct binary_operator_base : public expression
|
||||
template<int Precedence, class Op>
|
||||
struct binary_operator : public binary_operator_base
|
||||
{
|
||||
valty operator()() override
|
||||
result operator()() override
|
||||
{
|
||||
Op op;
|
||||
return op((*lhs)(), (*rhs)());
|
||||
result l = (*lhs)();
|
||||
result r = (*rhs)();
|
||||
if (!(l.second && r.second))
|
||||
{
|
||||
return {0, false};
|
||||
}
|
||||
return {op(l.first, r.first), true};
|
||||
}
|
||||
int precedence() override
|
||||
{
|
||||
@ -337,10 +544,11 @@ struct binary_operator : public binary_operator_base
|
||||
* Constructor. Takes the name of the operator as an argument, for
|
||||
* debugging. Only stores it in debug mode.
|
||||
*/
|
||||
binary_operator(const char *) {}
|
||||
binary_operator(source_location l, const char *) : expression(l) {}
|
||||
#else
|
||||
const char *opName;
|
||||
binary_operator(const char *o) : opName(o) {}
|
||||
binary_operator(source_location l, const char *o) :
|
||||
binary_operator_base(l), opName(o) {}
|
||||
void dump_impl() override
|
||||
{
|
||||
lhs->dump();
|
||||
@ -368,9 +576,16 @@ class ternary_conditional_operator : public expression
|
||||
* The expression that this evaluates to if the condition is false.
|
||||
*/
|
||||
expression_ptr rhs;
|
||||
valty operator()() override
|
||||
result operator()() override
|
||||
{
|
||||
return (*cond)() ? (*lhs)() : (*rhs)();
|
||||
result c = (*cond)();
|
||||
result l = (*lhs)();
|
||||
result r = (*rhs)();
|
||||
if (!(l.second && r.second && c.second))
|
||||
{
|
||||
return {0, false};
|
||||
}
|
||||
return c.first ? l : r;
|
||||
}
|
||||
int precedence() override
|
||||
{
|
||||
@ -390,10 +605,12 @@ class ternary_conditional_operator : public expression
|
||||
}
|
||||
#endif
|
||||
public:
|
||||
ternary_conditional_operator(expression_ptr c,
|
||||
ternary_conditional_operator(source_location sl,
|
||||
expression_ptr c,
|
||||
expression_ptr l,
|
||||
expression_ptr r) :
|
||||
cond(std::move(c)), lhs(std::move(l)), rhs(std::move(r)) {}
|
||||
expression(sl), cond(std::move(c)), lhs(std::move(l)),
|
||||
rhs(std::move(r)) {}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@ -430,36 +647,53 @@ struct bit_not
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct divmod : public binary_operator<5, T>
|
||||
{
|
||||
using binary_operator<5, T>::binary_operator;
|
||||
using binary_operator_base::result;
|
||||
result operator()() override
|
||||
{
|
||||
result r = (*binary_operator_base::rhs)();
|
||||
if (r.second && (r.first == 0))
|
||||
{
|
||||
expression::loc.report_error("Division by zero");
|
||||
return {0, false};
|
||||
}
|
||||
return binary_operator<5, T>::operator()();
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
expression_ptr input_buffer::parse_binary_expression(expression_ptr lhs)
|
||||
expression_ptr text_input_buffer::parse_binary_expression(expression_ptr lhs)
|
||||
{
|
||||
next_token();
|
||||
binary_operator_base *expr = nullptr;
|
||||
char op = ((*this)[0]);
|
||||
char op = *(*this);
|
||||
source_location l = location();
|
||||
switch (op)
|
||||
{
|
||||
default:
|
||||
return lhs;
|
||||
case '+':
|
||||
expr = new binary_operator<6, std::plus<valty>>("+");
|
||||
expr = new binary_operator<6, std::plus<valty>>(l, "+");
|
||||
break;
|
||||
case '-':
|
||||
expr = new binary_operator<6, std::minus<valty>>("-");
|
||||
expr = new binary_operator<6, std::minus<valty>>(l, "-");
|
||||
break;
|
||||
case '%':
|
||||
expr = new binary_operator<5, std::modulus<valty>>("%");
|
||||
expr = new divmod<std::modulus<valty>>(l, "/");
|
||||
break;
|
||||
case '*':
|
||||
expr = new binary_operator<5, std::multiplies<valty>>("*");
|
||||
expr = new binary_operator<5, std::multiplies<valty>>(l, "*");
|
||||
break;
|
||||
case '/':
|
||||
expr = new binary_operator<5, std::divides<valty>>("/");
|
||||
expr = new divmod<std::divides<valty>>(l, "/");
|
||||
break;
|
||||
case '<':
|
||||
cursor++;
|
||||
switch ((*this)[0])
|
||||
switch (peek())
|
||||
{
|
||||
default:
|
||||
parse_error("Invalid operator");
|
||||
@ -467,20 +701,20 @@ expression_ptr input_buffer::parse_binary_expression(expression_ptr lhs)
|
||||
case ' ':
|
||||
case '(':
|
||||
case '0'...'9':
|
||||
cursor--;
|
||||
expr = new binary_operator<8, std::less<valty>>("<");
|
||||
expr = new binary_operator<8, std::less<valty>>(l, "<");
|
||||
break;
|
||||
case '=':
|
||||
expr = new binary_operator<8, std::less_equal<valty>>("<=");
|
||||
++(*this);
|
||||
expr = new binary_operator<8, std::less_equal<valty>>(l, "<=");
|
||||
break;
|
||||
case '<':
|
||||
expr = new binary_operator<7, lshift<valty>>("<<");
|
||||
++(*this);
|
||||
expr = new binary_operator<7, lshift<valty>>(l, "<<");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '>':
|
||||
cursor++;
|
||||
switch ((*this)[0])
|
||||
switch (peek())
|
||||
{
|
||||
default:
|
||||
parse_error("Invalid operator");
|
||||
@ -488,54 +722,54 @@ expression_ptr input_buffer::parse_binary_expression(expression_ptr lhs)
|
||||
case '(':
|
||||
case ' ':
|
||||
case '0'...'9':
|
||||
cursor--;
|
||||
expr = new binary_operator<8, std::greater<valty>>(">");
|
||||
expr = new binary_operator<8, std::greater<valty>>(l, ">");
|
||||
break;
|
||||
case '=':
|
||||
expr = new binary_operator<8, std::greater_equal<valty>>(">=");
|
||||
++(*this);
|
||||
expr = new binary_operator<8, std::greater_equal<valty>>(l, ">=");
|
||||
break;
|
||||
case '>':
|
||||
expr = new binary_operator<7, rshift<valty>>(">>");
|
||||
++(*this);
|
||||
expr = new binary_operator<7, rshift<valty>>(l, ">>");
|
||||
break;
|
||||
return lhs;
|
||||
}
|
||||
break;
|
||||
case '=':
|
||||
if ((*this)[1] != '=')
|
||||
if (peek() != '=')
|
||||
{
|
||||
parse_error("Invalid operator");
|
||||
return nullptr;
|
||||
}
|
||||
consume('=');
|
||||
expr = new binary_operator<9, std::equal_to<valty>>("==");
|
||||
expr = new binary_operator<9, std::equal_to<valty>>(l, "==");
|
||||
break;
|
||||
case '!':
|
||||
if ((*this)[1] != '=')
|
||||
if (peek() != '=')
|
||||
{
|
||||
parse_error("Invalid operator");
|
||||
return nullptr;
|
||||
}
|
||||
cursor++;
|
||||
expr = new binary_operator<9, std::not_equal_to<valty>>("!=");
|
||||
expr = new binary_operator<9, std::not_equal_to<valty>>(l, "!=");
|
||||
break;
|
||||
case '&':
|
||||
if ((*this)[1] == '&')
|
||||
if (peek() == '&')
|
||||
{
|
||||
expr = new binary_operator<13, std::logical_and<valty>>("&&");
|
||||
expr = new binary_operator<13, std::logical_and<valty>>(l, "&&");
|
||||
}
|
||||
else
|
||||
{
|
||||
expr = new binary_operator<10, std::bit_and<valty>>("&");
|
||||
expr = new binary_operator<10, std::bit_and<valty>>(l, "&");
|
||||
}
|
||||
break;
|
||||
case '|':
|
||||
if ((*this)[1] == '|')
|
||||
if (peek() == '|')
|
||||
{
|
||||
expr = new binary_operator<12, std::logical_or<valty>>("||");
|
||||
expr = new binary_operator<12, std::logical_or<valty>>(l, "||");
|
||||
}
|
||||
else
|
||||
{
|
||||
expr = new binary_operator<14, std::bit_or<valty>>("|");
|
||||
expr = new binary_operator<14, std::bit_or<valty>>(l, "|");
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
@ -554,11 +788,11 @@ expression_ptr input_buffer::parse_binary_expression(expression_ptr lhs)
|
||||
parse_error("Expected false condition for ternary operator");
|
||||
return nullptr;
|
||||
}
|
||||
return expression_ptr(new ternary_conditional_operator(std::move(lhs),
|
||||
return expression_ptr(new ternary_conditional_operator(l, std::move(lhs),
|
||||
std::move(true_case), std::move(false_case)));
|
||||
}
|
||||
}
|
||||
cursor++;
|
||||
++(*this);
|
||||
next_token();
|
||||
expression_ptr e(expr);
|
||||
expression_ptr rhs(parse_expression());
|
||||
@ -584,19 +818,20 @@ expression_ptr input_buffer::parse_binary_expression(expression_ptr lhs)
|
||||
return e;
|
||||
}
|
||||
|
||||
expression_ptr input_buffer::parse_expression(bool stopAtParen)
|
||||
expression_ptr text_input_buffer::parse_expression(bool stopAtParen)
|
||||
{
|
||||
next_token();
|
||||
unsigned long long leftVal;
|
||||
expression_ptr lhs;
|
||||
switch ((*this)[0])
|
||||
source_location l = location();
|
||||
switch (*(*this))
|
||||
{
|
||||
case '0'...'9':
|
||||
if (!consume_integer(leftVal))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
lhs.reset(new terminal_expr(leftVal));
|
||||
lhs.reset(new terminal_expr(l, leftVal));
|
||||
break;
|
||||
case '(':
|
||||
{
|
||||
@ -606,7 +841,7 @@ expression_ptr input_buffer::parse_expression(bool stopAtParen)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
lhs.reset(new paren_expression(std::move(subexpr)));
|
||||
lhs.reset(new paren_expression(l, std::move(subexpr)));
|
||||
if (!consume(')'))
|
||||
{
|
||||
return nullptr;
|
||||
@ -625,7 +860,7 @@ expression_ptr input_buffer::parse_expression(bool stopAtParen)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
lhs.reset(new unary_operator<'+', unary_plus<valty>>(std::move(subexpr)));
|
||||
lhs.reset(new unary_operator<'+', unary_plus<valty>>(l, std::move(subexpr)));
|
||||
break;
|
||||
}
|
||||
case '-':
|
||||
@ -636,7 +871,7 @@ expression_ptr input_buffer::parse_expression(bool stopAtParen)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
lhs.reset(new unary_operator<'-', std::negate<valty>>(std::move(subexpr)));
|
||||
lhs.reset(new unary_operator<'-', std::negate<valty>>(l, std::move(subexpr)));
|
||||
break;
|
||||
}
|
||||
case '!':
|
||||
@ -647,7 +882,7 @@ expression_ptr input_buffer::parse_expression(bool stopAtParen)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
lhs.reset(new unary_operator<'!', std::logical_not<valty>>(std::move(subexpr)));
|
||||
lhs.reset(new unary_operator<'!', std::logical_not<valty>>(l, std::move(subexpr)));
|
||||
break;
|
||||
}
|
||||
case '~':
|
||||
@ -658,7 +893,7 @@ expression_ptr input_buffer::parse_expression(bool stopAtParen)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
lhs.reset(new unary_operator<'~', bit_not<valty>>(std::move(subexpr)));
|
||||
lhs.reset(new unary_operator<'~', bit_not<valty>>(l, std::move(subexpr)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -670,9 +905,9 @@ expression_ptr input_buffer::parse_expression(bool stopAtParen)
|
||||
}
|
||||
|
||||
bool
|
||||
input_buffer::consume_integer_expression(unsigned long long &outInt)
|
||||
text_input_buffer::consume_integer_expression(unsigned long long &outInt)
|
||||
{
|
||||
switch ((*this)[0])
|
||||
switch (*(*this))
|
||||
{
|
||||
case '(':
|
||||
{
|
||||
@ -681,8 +916,13 @@ input_buffer::consume_integer_expression(unsigned long long &outInt)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
outInt = (*e)();
|
||||
return true;
|
||||
auto r = (*e)();
|
||||
if (r.second)
|
||||
{
|
||||
outInt = r.first;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case '0'...'9':
|
||||
return consume_integer(outInt);
|
||||
@ -703,58 +943,80 @@ input_buffer::consume_hex_byte(uint8_t &outByte)
|
||||
return true;
|
||||
}
|
||||
|
||||
input_buffer&
|
||||
input_buffer::next_token()
|
||||
text_input_buffer&
|
||||
text_input_buffer::next_token()
|
||||
{
|
||||
auto &self = *this;
|
||||
int start;
|
||||
do {
|
||||
start = cursor;
|
||||
skip_spaces();
|
||||
if (finished())
|
||||
{
|
||||
return self;
|
||||
}
|
||||
// Parse /* comments
|
||||
if ((*this)[0] == '/' && (*this)[1] == '*')
|
||||
if (*self == '/' && peek() == '*')
|
||||
{
|
||||
// eat the start of the comment
|
||||
++(*this);
|
||||
++(*this);
|
||||
++self;
|
||||
++self;
|
||||
do {
|
||||
// Find the ending * of */
|
||||
while ((**this != '\0') && (**this != '*'))
|
||||
while ((*self != '\0') && (*self != '*') && !finished())
|
||||
{
|
||||
++(*this);
|
||||
++self;
|
||||
}
|
||||
// Eat the *
|
||||
++(*this);
|
||||
} while ((**this != '\0') && (**this != '/'));
|
||||
++self;
|
||||
} while ((*self != '\0') && (*self != '/') && !finished());
|
||||
// Eat the /
|
||||
++(*this);
|
||||
++self;
|
||||
}
|
||||
// Parse // comments
|
||||
if (((*this)[0] == '/' && (*this)[1] == '/'))
|
||||
if ((*self == '/' && peek() == '/'))
|
||||
{
|
||||
// eat the start of the comment
|
||||
++(*this);
|
||||
++(*this);
|
||||
++self;
|
||||
++self;
|
||||
// Find the ending of the line
|
||||
while (**this != '\n')
|
||||
while (*self != '\n' && !finished())
|
||||
{
|
||||
++(*this);
|
||||
++self;
|
||||
}
|
||||
// Eat the \n
|
||||
++(*this);
|
||||
++self;
|
||||
}
|
||||
} while (start != cursor);
|
||||
return *this;
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
input_buffer::parse_error(const char *msg)
|
||||
text_input_buffer::parse_error(const char *msg)
|
||||
{
|
||||
if (input_stack.empty())
|
||||
{
|
||||
fprintf(stderr, "Error: %s\n", msg);
|
||||
return;
|
||||
}
|
||||
input_buffer &b = *input_stack.top();
|
||||
parse_error(msg, b, b.cursor);
|
||||
}
|
||||
void
|
||||
text_input_buffer::parse_error(const char *msg,
|
||||
input_buffer &b,
|
||||
int loc)
|
||||
{
|
||||
int line_count = 1;
|
||||
int line_start = 0;
|
||||
int line_end = cursor;
|
||||
for (int i=cursor ; i>0 ; --i)
|
||||
int line_end = loc;
|
||||
if (loc < 0 || loc > b.size)
|
||||
{
|
||||
if (buffer[i] == '\n')
|
||||
return;
|
||||
}
|
||||
for (int i=loc ; i>0 ; --i)
|
||||
{
|
||||
if (b.buffer[i] == '\n')
|
||||
{
|
||||
line_count++;
|
||||
if (line_start == 0)
|
||||
@ -763,20 +1025,20 @@ input_buffer::parse_error(const char *msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i=cursor+1 ; i<size ; ++i)
|
||||
for (int i=loc+1 ; i<b.size ; ++i)
|
||||
{
|
||||
if (buffer[i] == '\n')
|
||||
if (b.buffer[i] == '\n')
|
||||
{
|
||||
line_end = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Error on line %d: %s\n", line_count, msg);
|
||||
fwrite(&buffer[line_start], line_end-line_start, 1, stderr);
|
||||
fprintf(stderr, "Error at %s:%d:%d: %s\n", b.filename().c_str(), line_count, loc - line_start, msg);
|
||||
fwrite(&b.buffer[line_start], line_end-line_start, 1, stderr);
|
||||
putc('\n', stderr);
|
||||
for (int i=0 ; i<(cursor-line_start) ; ++i)
|
||||
for (int i=0 ; i<(loc-line_start) ; ++i)
|
||||
{
|
||||
char c = (buffer[i+line_start] == '\t') ? '\t' : ' ';
|
||||
char c = (b.buffer[i+line_start] == '\t') ? '\t' : ' ';
|
||||
putc(c, stderr);
|
||||
}
|
||||
putc('^', stderr);
|
||||
@ -791,40 +1053,168 @@ input_buffer::dump()
|
||||
}
|
||||
#endif
|
||||
|
||||
mmap_input_buffer::mmap_input_buffer(int fd) : input_buffer(0, 0)
|
||||
|
||||
namespace
|
||||
{
|
||||
struct stat sb;
|
||||
if (fstat(fd, &sb))
|
||||
/**
|
||||
* The source files are ASCII, so we provide a non-locale-aware version of
|
||||
* isalpha. This is a class so that it can be used with a template function
|
||||
* for parsing strings.
|
||||
*/
|
||||
struct is_alpha
|
||||
{
|
||||
static inline bool check(const char c)
|
||||
{
|
||||
perror("Failed to stat file");
|
||||
return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') &&
|
||||
(c <= 'Z'));
|
||||
}
|
||||
size = sb.st_size;
|
||||
buffer = (const char*)mmap(0, size, PROT_READ, MAP_PRIVATE |
|
||||
MAP_PREFAULT_READ, fd, 0);
|
||||
if (buffer == MAP_FAILED)
|
||||
};
|
||||
/**
|
||||
* Check whether a character is in the set allowed for node names. This is a
|
||||
* class so that it can be used with a template function for parsing strings.
|
||||
*/
|
||||
struct is_node_name_character
|
||||
{
|
||||
static inline bool check(const char c)
|
||||
{
|
||||
perror("Failed to mmap file");
|
||||
exit(EXIT_FAILURE);
|
||||
switch(c)
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
case 'a'...'z': case 'A'...'Z': case '0'...'9':
|
||||
case ',': case '.': case '+': case '-':
|
||||
case '_':
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Check whether a character is in the set allowed for property names. This is
|
||||
* a class so that it can be used with a template function for parsing strings.
|
||||
*/
|
||||
struct is_property_name_character
|
||||
{
|
||||
static inline bool check(const char c)
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
case 'a'...'z': case 'A'...'Z': case '0'...'9':
|
||||
case ',': case '.': case '+': case '-':
|
||||
case '_': case '#':
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
string parse(text_input_buffer &s)
|
||||
{
|
||||
std::vector<char> bytes;
|
||||
for (char c=*s ; T::check(c) ; c=*(++s))
|
||||
{
|
||||
bytes.push_back(c);
|
||||
}
|
||||
return string(bytes.begin(), bytes.end());
|
||||
}
|
||||
|
||||
mmap_input_buffer::~mmap_input_buffer()
|
||||
{
|
||||
if (buffer != 0)
|
||||
{
|
||||
munmap((void*)buffer, size);
|
||||
}
|
||||
}
|
||||
|
||||
stream_input_buffer::stream_input_buffer() : input_buffer(0, 0)
|
||||
string
|
||||
text_input_buffer::parse_node_name()
|
||||
{
|
||||
int c;
|
||||
while ((c = fgetc(stdin)) != EOF)
|
||||
return parse<is_node_name_character>(*this);
|
||||
}
|
||||
|
||||
string
|
||||
text_input_buffer::parse_property_name()
|
||||
{
|
||||
return parse<is_property_name_character>(*this);
|
||||
}
|
||||
|
||||
string
|
||||
text_input_buffer::parse_node_or_property_name(bool &is_property)
|
||||
{
|
||||
if (is_property)
|
||||
{
|
||||
b.push_back(c);
|
||||
return parse_property_name();
|
||||
}
|
||||
buffer = b.data();
|
||||
size = b.size();
|
||||
std::vector<char> bytes;
|
||||
for (char c=*(*this) ; is_node_name_character::check(c) ; c=*(++(*this)))
|
||||
{
|
||||
bytes.push_back(c);
|
||||
}
|
||||
for (char c=*(*this) ; is_property_name_character::check(c) ; c=*(++(*this)))
|
||||
{
|
||||
bytes.push_back(c);
|
||||
is_property = true;
|
||||
}
|
||||
return string(bytes.begin(), bytes.end());
|
||||
}
|
||||
|
||||
string
|
||||
input_buffer::parse_to(char stop)
|
||||
{
|
||||
std::vector<char> bytes;
|
||||
for (char c=*(*this) ; c != stop ; c=*(++(*this)))
|
||||
{
|
||||
bytes.push_back(c);
|
||||
}
|
||||
return string(bytes.begin(), bytes.end());
|
||||
}
|
||||
|
||||
string
|
||||
text_input_buffer::parse_to(char stop)
|
||||
{
|
||||
std::vector<char> bytes;
|
||||
for (char c=*(*this) ; c != stop ; c=*(++(*this)))
|
||||
{
|
||||
if (finished())
|
||||
{
|
||||
break;
|
||||
}
|
||||
bytes.push_back(c);
|
||||
}
|
||||
return string(bytes.begin(), bytes.end());
|
||||
}
|
||||
|
||||
char
|
||||
text_input_buffer::peek()
|
||||
{
|
||||
return (*input_stack.top())[1];
|
||||
}
|
||||
|
||||
std::unique_ptr<input_buffer>
|
||||
input_buffer::buffer_for_file(const string &path, bool warn)
|
||||
{
|
||||
if (path == "-")
|
||||
{
|
||||
std::unique_ptr<input_buffer> b(new stream_input_buffer());
|
||||
return b;
|
||||
}
|
||||
int source = open(path.c_str(), O_RDONLY);
|
||||
if (source == -1)
|
||||
{
|
||||
if (warn)
|
||||
{
|
||||
fprintf(stderr, "Unable to open file '%s'. %s\n", path.c_str(), strerror(errno));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
struct stat st;
|
||||
if (fstat(source, &st) == 0 && S_ISDIR(st.st_mode))
|
||||
{
|
||||
if (warn)
|
||||
{
|
||||
fprintf(stderr, "File %s is a directory\n", path.c_str());
|
||||
}
|
||||
close(source);
|
||||
return 0;
|
||||
}
|
||||
std::unique_ptr<input_buffer> b(new mmap_input_buffer(source, std::string(path)));
|
||||
close(source);
|
||||
return b;
|
||||
}
|
||||
|
||||
} // namespace dtc
|
||||
|
@ -34,6 +34,9 @@
|
||||
#define _INPUT_BUFFER_HH_
|
||||
#include "util.hh"
|
||||
#include <assert.h>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace dtc
|
||||
{
|
||||
@ -55,6 +58,7 @@ typedef std::unique_ptr<expression> expression_ptr;
|
||||
*/
|
||||
class input_buffer
|
||||
{
|
||||
friend class text_input_buffer;
|
||||
protected:
|
||||
/**
|
||||
* The buffer. This class doesn't own the buffer, but the
|
||||
@ -66,17 +70,6 @@ class input_buffer
|
||||
*/
|
||||
int size;
|
||||
private:
|
||||
/**
|
||||
* Parse an expression. If `stopAtParen` is set, then only parse a number
|
||||
* or a parenthetical expression, otherwise assume that either is the
|
||||
* left-hand side of a binary expression and try to parse the right-hand
|
||||
* side.
|
||||
*/
|
||||
expression_ptr parse_expression(bool stopAtParen=false);
|
||||
/**
|
||||
* Parse a binary expression, having already parsed the right-hand side.
|
||||
*/
|
||||
expression_ptr parse_binary_expression(expression_ptr lhs);
|
||||
/**
|
||||
* The current place in the buffer where we are reading. This class
|
||||
* keeps a separate size, pointer, and cursor so that we can move
|
||||
@ -90,12 +83,27 @@ class input_buffer
|
||||
*/
|
||||
input_buffer(const char* b, int s, int c) : buffer(b), size(s),
|
||||
cursor(c) {}
|
||||
/**
|
||||
* Reads forward past any spaces. The DTS format is not whitespace
|
||||
* sensitive and so we want to scan past whitespace when reading it.
|
||||
*/
|
||||
void skip_spaces();
|
||||
public:
|
||||
/**
|
||||
* Returns the file name associated with this buffer.
|
||||
*/
|
||||
virtual const std::string &filename() const
|
||||
{
|
||||
static std::string s;
|
||||
return s;
|
||||
}
|
||||
static std::unique_ptr<input_buffer> buffer_for_file(const std::string &path,
|
||||
bool warn=true);
|
||||
/**
|
||||
* Skips all characters in the input until the specified character is
|
||||
* encountered.
|
||||
*/
|
||||
void skip_to(char);
|
||||
/**
|
||||
* Parses up to a specified character and returns the intervening
|
||||
* characters as a string.
|
||||
*/
|
||||
std::string parse_to(char);
|
||||
/**
|
||||
* Return whether all input has been consumed.
|
||||
*/
|
||||
@ -124,13 +132,6 @@ class input_buffer
|
||||
* buffer that extends to the end of the available memory.
|
||||
*/
|
||||
input_buffer buffer_from_offset(int offset, int s=0);
|
||||
/**
|
||||
* Returns true if this buffer has no unconsumed space in it.
|
||||
*/
|
||||
inline bool empty()
|
||||
{
|
||||
return cursor >= size;
|
||||
}
|
||||
/**
|
||||
* Dereferencing operator, allows the buffer to be treated as a char*
|
||||
* and dereferenced to give a character. This returns a null byte if
|
||||
@ -163,16 +164,6 @@ class input_buffer
|
||||
cursor++;
|
||||
return *this;
|
||||
}
|
||||
/**
|
||||
* Cast to char* operator. Returns a pointer into the buffer that can
|
||||
* be used for constructing strings.
|
||||
*/
|
||||
inline operator const char*()
|
||||
{
|
||||
if (cursor >= size) { return 0; }
|
||||
if (cursor < 0) { return 0; }
|
||||
return &buffer[cursor];
|
||||
}
|
||||
/**
|
||||
* Consumes a character. Moves the cursor one character forward if the
|
||||
* next character matches the argument, returning true. If the current
|
||||
@ -180,7 +171,7 @@ class input_buffer
|
||||
*/
|
||||
inline bool consume(char c)
|
||||
{
|
||||
if ((*this)[0] == c)
|
||||
if (*(*this) == c)
|
||||
{
|
||||
++(*this);
|
||||
return true;
|
||||
@ -207,6 +198,13 @@ class input_buffer
|
||||
* operators), evaluates it, and returns the result.
|
||||
*/
|
||||
bool consume_integer_expression(unsigned long long &outInt);
|
||||
/**
|
||||
* Consumes two hex digits and return the resulting byte via the first
|
||||
* argument. If the next two characters are hex digits, returns true
|
||||
* and advances the cursor. If not, then returns false and leaves the
|
||||
* cursor in place.
|
||||
*/
|
||||
bool consume_hex_byte(uint8_t &outByte);
|
||||
/**
|
||||
* Template function that consumes a binary value in big-endian format
|
||||
* from the input stream. Returns true and advances the cursor if
|
||||
@ -232,28 +230,15 @@ class input_buffer
|
||||
out = 0;
|
||||
for (int i=0 ; i<type_size ; ++i)
|
||||
{
|
||||
if (size < cursor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
out <<= 8;
|
||||
out |= (((T)buffer[cursor++]) & 0xff);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Consumes two hex digits and return the resulting byte via the first
|
||||
* argument. If the next two characters are hex digits, returns true
|
||||
* and advances the cursor. If not, then returns false and leaves the
|
||||
* cursor in place.
|
||||
*/
|
||||
bool consume_hex_byte(uint8_t &outByte);
|
||||
/**
|
||||
* Advances the cursor to the start of the next token, skipping
|
||||
* comments and whitespace. If the cursor already points to the start
|
||||
* of a token, then this function does nothing.
|
||||
*/
|
||||
input_buffer &next_token();
|
||||
/**
|
||||
* Prints a message indicating the location of a parse error.
|
||||
*/
|
||||
void parse_error(const char *msg);
|
||||
#ifndef NDEBUG
|
||||
/**
|
||||
* Dumps the current cursor value and the unconsumed values in the
|
||||
@ -278,37 +263,273 @@ inline bool input_buffer::consume_binary(uint8_t &out)
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclass of input_buffer that mmap()s a file and owns the resulting memory.
|
||||
* When this object is destroyed, the memory is unmapped.
|
||||
* An input buffer subclass used for parsing DTS files. This manages a stack
|
||||
* of input buffers to handle /input/ operations.
|
||||
*/
|
||||
struct mmap_input_buffer : public input_buffer
|
||||
class text_input_buffer
|
||||
{
|
||||
std::unordered_set<std::string> defines;
|
||||
/**
|
||||
* Constructs a new buffer from the file passed in as a file
|
||||
* descriptor.
|
||||
* The cursor is the input into the input stream where we are currently reading.
|
||||
*/
|
||||
mmap_input_buffer(int fd);
|
||||
int cursor = 0;
|
||||
/**
|
||||
* Unmaps the buffer, if one exists.
|
||||
* The current stack of includes. The current input is always from the top
|
||||
* of the stack.
|
||||
*/
|
||||
virtual ~mmap_input_buffer();
|
||||
};
|
||||
/**
|
||||
* Input buffer read from standard input. This is used for reading device tree
|
||||
* blobs and source from standard input. It reads the entire input into
|
||||
* malloc'd memory, so will be very slow for large inputs. DTS and DTB files
|
||||
* are very rarely more than 10KB though, so this is probably not a problem.
|
||||
*/
|
||||
struct stream_input_buffer : public input_buffer
|
||||
{
|
||||
std::stack<std::shared_ptr<input_buffer>> input_stack;
|
||||
/**
|
||||
* The buffer that will store the data read from the standard input.
|
||||
*
|
||||
*/
|
||||
std::vector<char> b;
|
||||
const std::vector<std::string> include_paths;
|
||||
/**
|
||||
* Constructs a new buffer from the standard input.
|
||||
* Reads forward past any spaces. The DTS format is not whitespace
|
||||
* sensitive and so we want to scan past whitespace when reading it.
|
||||
*/
|
||||
stream_input_buffer();
|
||||
void skip_spaces();
|
||||
/**
|
||||
* Returns the character immediately after the current one.
|
||||
*
|
||||
* This method does not look between files.
|
||||
*/
|
||||
char peek();
|
||||
/**
|
||||
* If a /include/ token is encountered, then look up the corresponding
|
||||
* input file, push it onto the input stack, and continue.
|
||||
*/
|
||||
void handle_include();
|
||||
/**
|
||||
* The base directory for this file.
|
||||
*/
|
||||
const std::string dir;
|
||||
/**
|
||||
* The file where dependencies should be output.
|
||||
*/
|
||||
FILE *depfile;
|
||||
public:
|
||||
/**
|
||||
* Construct a new text input buffer with the specified buffer as the start
|
||||
* of parsing and the specified set of input paths for handling new
|
||||
* inclusions.
|
||||
*/
|
||||
text_input_buffer(std::unique_ptr<input_buffer> &&b,
|
||||
std::unordered_set<std::string> &&d,
|
||||
std::vector<std::string> &&i,
|
||||
const std::string directory,
|
||||
FILE *deps)
|
||||
: defines(d), include_paths(i), dir(directory), depfile(deps)
|
||||
{
|
||||
input_stack.push(std::move(b));
|
||||
}
|
||||
/**
|
||||
* Skips all characters in the input until the specified character is
|
||||
* encountered.
|
||||
*/
|
||||
void skip_to(char);
|
||||
/**
|
||||
* Parse an expression. If `stopAtParen` is set, then only parse a number
|
||||
* or a parenthetical expression, otherwise assume that either is the
|
||||
* left-hand side of a binary expression and try to parse the right-hand
|
||||
* side.
|
||||
*/
|
||||
expression_ptr parse_expression(bool stopAtParen=false);
|
||||
/**
|
||||
* Parse a binary expression, having already parsed the right-hand side.
|
||||
*/
|
||||
expression_ptr parse_binary_expression(expression_ptr lhs);
|
||||
/**
|
||||
* Return whether all input has been consumed.
|
||||
*/
|
||||
bool finished()
|
||||
{
|
||||
return input_stack.empty() ||
|
||||
((input_stack.size() == 1) && input_stack.top()->finished());
|
||||
}
|
||||
/**
|
||||
* Dereferencing operator. Returns the current character in the top input buffer.
|
||||
*/
|
||||
inline char operator*()
|
||||
{
|
||||
if (input_stack.empty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return *(*input_stack.top());
|
||||
}
|
||||
/**
|
||||
* Increments the cursor, iterating forward in the buffer.
|
||||
*/
|
||||
inline text_input_buffer &operator++()
|
||||
{
|
||||
if (input_stack.empty())
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
cursor++;
|
||||
auto &top = *input_stack.top();
|
||||
++top;
|
||||
if (top.finished())
|
||||
{
|
||||
input_stack.pop();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
/**
|
||||
* Consumes a character. Moves the cursor one character forward if the
|
||||
* next character matches the argument, returning true. If the current
|
||||
* character does not match the argument, returns false.
|
||||
*/
|
||||
inline bool consume(char c)
|
||||
{
|
||||
if (*(*this) == c)
|
||||
{
|
||||
++(*this);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Consumes a string. If the (null-terminated) string passed as the
|
||||
* argument appears in the input, advances the cursor to the end and
|
||||
* returns true. Returns false if the string does not appear at the
|
||||
* current point in the input.
|
||||
*
|
||||
* This method does not scan between files.
|
||||
*/
|
||||
bool consume(const char *str)
|
||||
{
|
||||
if (input_stack.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return input_stack.top()->consume(str);
|
||||
}
|
||||
/**
|
||||
* Reads an integer in base 8, 10, or 16. Returns true and advances
|
||||
* the cursor to the end of the integer if the cursor points to an
|
||||
* integer, returns false and does not move the cursor otherwise.
|
||||
*
|
||||
* The parsed value is returned via the argument.
|
||||
*
|
||||
* This method does not scan between files.
|
||||
*/
|
||||
bool consume_integer(unsigned long long &outInt)
|
||||
{
|
||||
if (input_stack.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return input_stack.top()->consume_integer(outInt);
|
||||
}
|
||||
/**
|
||||
* Reads an arithmetic expression (containing any of the normal C
|
||||
* operators), evaluates it, and returns the result.
|
||||
*/
|
||||
bool consume_integer_expression(unsigned long long &outInt);
|
||||
/**
|
||||
* Consumes two hex digits and return the resulting byte via the first
|
||||
* argument. If the next two characters are hex digits, returns true
|
||||
* and advances the cursor. If not, then returns false and leaves the
|
||||
* cursor in place.
|
||||
*
|
||||
* This method does not scan between files.
|
||||
*/
|
||||
bool consume_hex_byte(uint8_t &outByte)
|
||||
{
|
||||
if (input_stack.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return input_stack.top()->consume_hex_byte(outByte);
|
||||
}
|
||||
/**
|
||||
* Returns the longest string in the input buffer starting at the
|
||||
* current cursor and composed entirely of characters that are valid in
|
||||
* node names.
|
||||
*/
|
||||
std::string parse_node_name();
|
||||
/**
|
||||
* Returns the longest string in the input buffer starting at the
|
||||
* current cursor and composed entirely of characters that are valid in
|
||||
* property names.
|
||||
*/
|
||||
std::string parse_property_name();
|
||||
/**
|
||||
* Parses either a node or a property name. If is_property is true on
|
||||
* entry, then only property names are parsed. If it is false, then it
|
||||
* will be set, on return, to indicate whether the parsed name is only
|
||||
* valid as a property.
|
||||
*/
|
||||
std::string parse_node_or_property_name(bool &is_property);
|
||||
/**
|
||||
* Parses up to a specified character and returns the intervening
|
||||
* characters as a string.
|
||||
*/
|
||||
std::string parse_to(char);
|
||||
/**
|
||||
* Advances the cursor to the start of the next token, skipping
|
||||
* comments and whitespace. If the cursor already points to the start
|
||||
* of a token, then this function does nothing.
|
||||
*/
|
||||
text_input_buffer &next_token();
|
||||
/**
|
||||
* Location in the source file. This should never be interpreted by
|
||||
* anything other than error reporting functions of this class. It will
|
||||
* eventually become something more complex than an `int`.
|
||||
*/
|
||||
class source_location
|
||||
{
|
||||
friend class text_input_buffer;
|
||||
/**
|
||||
* The text buffer object that included `b`.
|
||||
*/
|
||||
text_input_buffer &buffer;
|
||||
/**
|
||||
* The underlying buffer that contains this location.
|
||||
*/
|
||||
std::shared_ptr<input_buffer> b;
|
||||
/**
|
||||
* The offset within the current buffer of the source location.
|
||||
*/
|
||||
int cursor;
|
||||
source_location(text_input_buffer &buf)
|
||||
: buffer(buf),
|
||||
b(buf.input_stack.empty() ? nullptr : buf.input_stack.top()),
|
||||
cursor(b ? b->cursor : 0) {}
|
||||
public:
|
||||
/**
|
||||
* Report an error at this location.
|
||||
*/
|
||||
void report_error(const char *msg)
|
||||
{
|
||||
if (b)
|
||||
{
|
||||
buffer.parse_error(msg, *b, cursor);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.parse_error(msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Returns the current source location.
|
||||
*/
|
||||
source_location location()
|
||||
{
|
||||
return { *this };
|
||||
}
|
||||
/**
|
||||
* Prints a message indicating the location of a parse error.
|
||||
*/
|
||||
void parse_error(const char *msg);
|
||||
private:
|
||||
/**
|
||||
* Prints a message indicating the location of a parse error, given a
|
||||
* specified location. This is used when input has already moved beyond
|
||||
* the location that caused the failure.
|
||||
*/
|
||||
void parse_error(const char *msg, input_buffer &b, int loc);
|
||||
};
|
||||
|
||||
} // namespace dtc
|
||||
|
@ -30,150 +30,29 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include "string.hh"
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <libgen.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
/**
|
||||
* The source files are ASCII, so we provide a non-locale-aware version of
|
||||
* isalpha. This is a class so that it can be used with a template function
|
||||
* for parsing strings.
|
||||
*/
|
||||
struct is_alpha
|
||||
{
|
||||
static inline bool check(const char c)
|
||||
{
|
||||
return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') &&
|
||||
(c <= 'Z'));
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Check whether a character is in the set allowed for node names. This is a
|
||||
* class so that it can be used with a template function for parsing strings.
|
||||
*/
|
||||
struct is_node_name_character
|
||||
{
|
||||
static inline bool check(const char c)
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
case 'a'...'z': case 'A'...'Z': case '0'...'9':
|
||||
case ',': case '.': case '+': case '-':
|
||||
case '_':
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Check whether a character is in the set allowed for property names. This is
|
||||
* a class so that it can be used with a template function for parsing strings.
|
||||
*/
|
||||
struct is_property_name_character
|
||||
{
|
||||
static inline bool check(const char c)
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
case 'a'...'z': case 'A'...'Z': case '0'...'9':
|
||||
case ',': case '.': case '+': case '-':
|
||||
case '_': case '#':
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
#include "util.hh"
|
||||
|
||||
}
|
||||
using std::string;
|
||||
|
||||
namespace dtc
|
||||
{
|
||||
|
||||
template<class T> string
|
||||
string::parse(input_buffer &s)
|
||||
{
|
||||
const char *start = s;
|
||||
int l=0;
|
||||
while (T::check(*s)) { l++; ++s; }
|
||||
return string(start, l);
|
||||
}
|
||||
|
||||
string::string(input_buffer &s) : start((const char*)s), length(0)
|
||||
{
|
||||
while(s[length] != '\0')
|
||||
{
|
||||
length++;
|
||||
}
|
||||
}
|
||||
|
||||
string
|
||||
string::parse_node_name(input_buffer &s)
|
||||
{
|
||||
return parse<is_node_name_character>(s);
|
||||
}
|
||||
|
||||
string
|
||||
string::parse_property_name(input_buffer &s)
|
||||
{
|
||||
return parse<is_property_name_character>(s);
|
||||
}
|
||||
string
|
||||
string::parse_node_or_property_name(input_buffer &s, bool &is_property)
|
||||
{
|
||||
if (is_property)
|
||||
{
|
||||
return parse_property_name(s);
|
||||
}
|
||||
const char *start = s;
|
||||
int l=0;
|
||||
while (is_node_name_character::check(*s))
|
||||
{
|
||||
l++;
|
||||
++s;
|
||||
}
|
||||
while (is_property_name_character::check(*s))
|
||||
{
|
||||
l++;
|
||||
++s;
|
||||
is_property = true;
|
||||
}
|
||||
return string(start, l);
|
||||
}
|
||||
|
||||
bool
|
||||
string::operator==(const string& other) const
|
||||
{
|
||||
return (length == other.length) &&
|
||||
(memcmp(start, other.start, length) == 0);
|
||||
}
|
||||
|
||||
bool
|
||||
string::operator==(const char *other) const
|
||||
{
|
||||
return strncmp(other, start, length) == 0;
|
||||
}
|
||||
|
||||
bool
|
||||
string::operator<(const string& other) const
|
||||
{
|
||||
if (length < other.length) { return true; }
|
||||
if (length > other.length) { return false; }
|
||||
return memcmp(start, other.start, length) < 0;
|
||||
}
|
||||
|
||||
void
|
||||
string::push_to_buffer(byte_buffer &buffer, bool escapes)
|
||||
push_string(byte_buffer &buffer, const string &s, bool escapes)
|
||||
{
|
||||
for (int i=0 ; i<length ; ++i)
|
||||
size_t length = s.size();
|
||||
for (size_t i=0 ; i<length ; ++i)
|
||||
{
|
||||
uint8_t c = start[i];
|
||||
uint8_t c = s[i];
|
||||
if (escapes && c == '\\' && i+1 < length)
|
||||
{
|
||||
c = start[++i];
|
||||
c = s[++i];
|
||||
switch (c)
|
||||
{
|
||||
// For now, we just ignore invalid escape sequences.
|
||||
@ -206,15 +85,15 @@ string::push_to_buffer(byte_buffer &buffer, bool escapes)
|
||||
case '0'...'7':
|
||||
{
|
||||
int v = digittoint(c);
|
||||
if (i+1 < length && start[i+1] <= '7' && start[i+1] >= '0')
|
||||
if (i+1 < length && s[i+1] <= '7' && s[i+1] >= '0')
|
||||
{
|
||||
v <<= 3;
|
||||
v |= digittoint(start[i+1]);
|
||||
v |= digittoint(s[i+1]);
|
||||
i++;
|
||||
if (i+1 < length && start[i+1] <= '7' && start[i+1] >= '0')
|
||||
if (i+1 < length && s[i+1] <= '7' && s[i+1] >= '0')
|
||||
{
|
||||
v <<= 3;
|
||||
v |= digittoint(start[i+1]);
|
||||
v |= digittoint(s[i+1]);
|
||||
}
|
||||
}
|
||||
c = (uint8_t)v;
|
||||
@ -227,11 +106,11 @@ string::push_to_buffer(byte_buffer &buffer, bool escapes)
|
||||
{
|
||||
break;
|
||||
}
|
||||
int v = digittoint(start[i]);
|
||||
if (i+1 < length && ishexdigit(start[i+1]))
|
||||
int v = digittoint(s[i]);
|
||||
if (i+1 < length && ishexdigit(s[i+1]))
|
||||
{
|
||||
v <<= 4;
|
||||
v |= digittoint(start[++i]);
|
||||
v |= digittoint(s[++i]);
|
||||
}
|
||||
c = (uint8_t)v;
|
||||
break;
|
||||
@ -242,17 +121,28 @@ string::push_to_buffer(byte_buffer &buffer, bool escapes)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
string::print(FILE *file)
|
||||
std::string dirname(const string &s)
|
||||
{
|
||||
fwrite(start, length, 1, file);
|
||||
if (s == string())
|
||||
{
|
||||
return string();
|
||||
}
|
||||
char *str = strdup(s.c_str());
|
||||
string dn(::dirname(str));
|
||||
free(str);
|
||||
return dn;
|
||||
}
|
||||
|
||||
void
|
||||
string::dump()
|
||||
std::string basename(const string &s)
|
||||
{
|
||||
print(stderr);
|
||||
if (s == string())
|
||||
{
|
||||
return string();
|
||||
}
|
||||
char *str = strdup(s.c_str());
|
||||
string bn(::basename(str));
|
||||
free(str);
|
||||
return bn;
|
||||
}
|
||||
|
||||
} // namespace dtc
|
||||
|
||||
|
@ -68,6 +68,8 @@ inline void push_big_endian(byte_buffer &v, T val)
|
||||
}
|
||||
}
|
||||
|
||||
void push_string(byte_buffer &v, const std::string &s, bool escapes=false);
|
||||
|
||||
/**
|
||||
* Simple inline non-locale-aware check that this is a valid ASCII
|
||||
* digit.
|
||||
@ -84,9 +86,30 @@ inline bool isdigit(char c)
|
||||
inline bool ishexdigit(char c)
|
||||
{
|
||||
return ((c >= '0') && (c <= '9')) || ((c >= 'a') && (c <= 'f')) ||
|
||||
((c >= 'A') && (c <= 'Z'));
|
||||
((c >= 'A') && (c <= 'F'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple inline non-locale-aware check that this is a valid ASCII
|
||||
* letter.
|
||||
*/
|
||||
inline bool isalpha(char c)
|
||||
{
|
||||
return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'));
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around dirname(3) that handles inconsistencies relating to memory
|
||||
* management between platforms and provides a std::string interface.
|
||||
*/
|
||||
std::string dirname(const std::string&);
|
||||
|
||||
/**
|
||||
* A wrapper around basename(3) that handles inconsistencies relating to memory
|
||||
* management between platforms and provides a std::string interface.
|
||||
*/
|
||||
std::string basename(const std::string&);
|
||||
|
||||
}// namespace dtc
|
||||
|
||||
#endif // !_UTIL_HH_
|
||||
|
Loading…
Reference in New Issue
Block a user