#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <vector>
#include "vscore.h"
enum ParserStates {
WANT_KEY_START,
WANT_KEY_CHAR,
WANT_DELIMITER,
WANT_VALUE_START,
WANT_VALUE_CHAR
};
static inline bool isAlphaNumUnderscore(char c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
}
VSMap *readSettings(const std::string &path) {
VSMap *settings = vs_internal_vsapi.createMap();
std::string err;
FILE *f = fopen(path.c_str(), "rb");
if (!f) {
if (errno != ENOENT) {
err.append("Couldn't open '").append(path).append("' for reading. Error: ").append(strerror(errno));
vs_internal_vsapi.mapSetError(settings, err.c_str());
}
return settings;
}
if (fseek(f, 0, SEEK_END)) {
err.append("Couldn't find the size of '").append(path).append("' by seeking to its end. Error: ").append(strerror(errno));
vs_internal_vsapi.mapSetError(settings, err.c_str());
fclose(f);
return settings;
}
long size = ftell(f);
if (size == -1) {
err.append("Couldn't find the size of '").append(path).append("'. ftell failed with the error: ").append(strerror(errno));
vs_internal_vsapi.mapSetError(settings, err.c_str());
fclose(f);
return settings;
}
if (size > 100*1024) {
err.append("Configuration file '").append(path).append("' is ridiculously large. Ignoring.");
vs_internal_vsapi.mapSetError(settings, err.c_str());
fclose(f);
return settings;
}
rewind(f);
std::vector<char> buffer(size);
if (fread(buffer.data(), 1, size, f) != static_cast<size_t>(size)) {
err.append("Didn't read the expected number of bytes from '").append(path).append("'.");
vs_internal_vsapi.mapSetError(settings, err.c_str());
fclose(f);
return settings;
}
fclose(f);
buffer.push_back('\n');
int key_start = 0, key_end = 0, value_start = 0, value_end = 0;
err.append("Error while parsing '").append(path).append("': ");
ParserStates state = WANT_KEY_START;
int line = 1;
std::string line_str(std::string("Line ").append(std::to_string(line)).append(": "));
for (int i = 0; i < size; i++) {
switch (state) {
case WANT_KEY_START:
if (isAlphaNumUnderscore(buffer[i])) {
key_start = i;
state = WANT_KEY_CHAR;
}
break;
case WANT_KEY_CHAR:
if (buffer[i] == '\n') {
err.append(line_str).append("No delimiter found before reaching the end of the line.");
vs_internal_vsapi.mapSetError(settings, err.c_str());
return settings;
} else if (buffer[i] == ' ') {
key_end = i - 1;
state = WANT_DELIMITER;
} else if (buffer[i] == '=') {
key_end = i - 1;
state = WANT_VALUE_START;
} else if (!isAlphaNumUnderscore(buffer[i])) {
err.append(line_str).append("Garbage found inside key.");
vs_internal_vsapi.mapSetError(settings, err.c_str());
return settings;
}
break;
case WANT_DELIMITER:
if (buffer[i] == '=') {
state = WANT_VALUE_START;
} else if (buffer[i] == '\n') {
err.append(line_str).append("No delimiter found before reaching the end of the line.");
vs_internal_vsapi.mapSetError(settings, err.c_str());
return settings;
} else {
err.append(line_str).append("Expected '=' but found garbage instead.");
vs_internal_vsapi.mapSetError(settings, err.c_str());
return settings;
}
break;
case WANT_VALUE_START:
if (buffer[i] == '\n') {
err.append(line_str).append("No value found for key before reaching the end of the line.");
vs_internal_vsapi.mapSetError(settings, err.c_str());
return settings;
}
if (buffer[i] != ' ') {
value_start = i;
state = WANT_VALUE_CHAR;
}
break;
case WANT_VALUE_CHAR:
if (buffer[i] == '\n') {
value_end = i - 1;
std::string key(&buffer[key_start], key_end - key_start + 1);
std::string value(&buffer[value_start], value_end - value_start + 1);
vs_internal_vsapi.mapSetData(settings, key.c_str(), value.c_str(), value.size(), dtUtf8, maReplace);
state = WANT_KEY_START;
}
break;
default:
err.append(line_str).append("Shit broke. This should never happen.");
vs_internal_vsapi.mapSetError(settings, err.c_str());
return settings;
break;
}
if (buffer[i] == '\n') {
line++;
line_str = std::string("Line ").append(std::to_string(line)).append(": ");
}
}
return settings;
}