You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

100 lines
3.9 KiB

// ©2008-2017 despair <>
#include "CFGFileParser.h"
#include <climits>
#include "slre.h"
CFGFileParser::CFGFileParser(const char* filename){
// Open the config file, get its size,
// allocate the buffer, read it into
// the buffer, close the file
cfg_file = fopen(filename, "rb"); // On NT, opening in text mode translates \n into \r\n
stat(filename, cfg_fileinfo);
buffer = static_cast<char*>(tlsf_malloc(cfg_fileinfo->st_size & INT_MAX, sizeof(char) & INT_MAX));
cfg = new BrowserConfiguration();
bytesRead = fread(buffer, sizeof(char) & INT_MAX, cfg_fileinfo->st_size & INT_MAX, cfg_file);
if (!bytesRead) {
std::cout << "no config" << std::endl;
// clean up!
// This buffer has been written to and co-opted by
// libc itself, but it stands that we initially allocated
// it. So we'll free it ourselves.
// We've already copied the collected config directives
// into a global class, we can safely delete the scratch
// object, and its file info block.
delete cfg;
bool CFGFileParser::ParseText() {
// Initial buffer for pass 1. tmp holds the config file (fmr. buffer)
// token holds the actual token. pass1_length to keep track of memory,
// reallocate as needed.
char *tmp, *token;
size_t pass1_length = 0;
struct slre regex; // final validation before adding to second-pass buffer
// Second pass. All comments and .tags stripped out. Starts off at 512 bytes, increases if necessary.
char* directives = static_cast<char*>(tlsf_malloc(512, sizeof(char)));
char* tmp2;
token = strtok_r(buffer, "\n", &tmp);
while (tmp != nullptr) {
if (token[0] == '#' || token[0] == '.' || token[0] == '\n'){ // Comment, Perl directive, or single <LF> found, skip over
token = strtok_r(nullptr, "\n", &tmp);
else if (!isalpha(token[0])){ // The leading character is NOT a #, ., <LF>, or a letter. NB: Leading numbers are also invalid.
printf("ERROR: Config file is invalid\n");
return false;
else {
if (!slre_compile(&regex, "([A-Z0-9_]*)(:)(\\[.*\\])")){
printf("Internal error.\n");
return false;
if (!slre_match(&regex, token, strlen(token), nullptr)){
printf("Invalid directive: %s\n", token); // regex matches DIREC[_]T[I1]VE:[token] but NOT directive:[token] - the directive MUST be all caps
return false;
// Config directive found, add to second-pass buffer
// once we reach the 0.5KB mark, realloc exactly enough to keep going
if (pass1_length >= 512){
tlsf_realloc(directives, pass1_length+strlen(token)+2);
strcat(directives, token);
strcat(directives, "\t");
pass1_length = strlen(directives);
token = strtok_r(nullptr, "\n", &tmp); // continue
// First pass complete, second pass: break up into single directives and <key, value> pairs
token = strtok_r(directives, "\t", &tmp);
ntr::fast_string key, value;
while (tmp != nullptr){
key=strtok_r(token, ":", &tmp2);
value=strtok_r(nullptr, "[]", &tmp2);
// strip ending bracket
token = strtok_r(nullptr, "\t", &tmp);
#ifdef DEBUG
std::cout << "Settings stringmap contains, in no particular order:\n";
for ( auto it = cfg->Settings.begin(); it != cfg->Settings.end(); ++it ){
std::cout << it->first << ":" << it->second << std::endl;
std::cout << std::endl;
// clean up, we're done parsing
return true;
// Writes the fully parsed configuration to a BrowserConfiguration
// object supplied by the user.
void CFGFileParser::WriteConfig(BrowserConfiguration &config){
config.Settings = cfg->Settings;