#include <Debug.h>
#include <Directory.h>
#include <Entry.h>
#include <File.h>
#include <FindDirectory.h>
#include <Path.h>
#include <StopWatch.h>
#include <alloca.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "SettingsHandler.h"
ArgvParser::ArgvParser(const char* name)
:
fFile(0),
fBuffer(NULL),
fPos(-1),
fArgc(0),
fCurrentArgv(0),
fCurrentArgsPos(-1),
fSawBackslash(false),
fEatComment(false),
fInDoubleQuote(false),
fInSingleQuote(false),
fLineNo(0),
fFileName(name)
{
fFile = fopen(fFileName, "r");
if (!fFile) {
PRINT(("Error opening %s\n", fFileName));
return;
}
fBuffer = new char [kBufferSize];
fCurrentArgv = new char * [1024];
}
ArgvParser::~ArgvParser()
{
delete[] fBuffer;
MakeArgvEmpty();
delete[] fCurrentArgv;
if (fFile)
fclose(fFile);
}
void
ArgvParser::MakeArgvEmpty()
{
for (int32 index = 0; index < fArgc; index++)
delete[] fCurrentArgv[index];
fArgc = 0;
}
status_t
ArgvParser::SendArgv(ArgvHandler argvHandlerFunc, void* passThru)
{
if (fArgc) {
NextArgv();
fCurrentArgv[fArgc] = 0;
const char* result = (argvHandlerFunc)(fArgc, fCurrentArgv, passThru);
if (result != NULL) {
printf("File %s; Line %" B_PRId32 " # %s", fFileName, fLineNo,
result);
}
MakeArgvEmpty();
if (result != NULL)
return B_ERROR;
}
return B_OK;
}
void
ArgvParser::NextArgv()
{
if (fSawBackslash) {
fCurrentArgs[++fCurrentArgsPos] = '\\';
fSawBackslash = false;
}
fCurrentArgs[++fCurrentArgsPos] = '\0';
fCurrentArgv[fArgc] = new char [strlen(fCurrentArgs) + 1];
strcpy(fCurrentArgv[fArgc], fCurrentArgs);
fCurrentArgsPos = -1;
fArgc++;
}
void
ArgvParser::NextArgvIfNotEmpty()
{
if (!fSawBackslash && fCurrentArgsPos < 0)
return;
NextArgv();
}
int
ArgvParser::GetCh()
{
if (fPos < 0 || fBuffer[fPos] == 0) {
if (fFile == 0)
return EOF;
if (fgets(fBuffer, kBufferSize, fFile) == 0)
return EOF;
fPos = 0;
}
return fBuffer[fPos++];
}
status_t
ArgvParser::EachArgv(const char* name, ArgvHandler argvHandlerFunc,
void* passThru)
{
ArgvParser parser(name);
return parser.EachArgvPrivate(name, argvHandlerFunc, passThru);
}
status_t
ArgvParser::EachArgvPrivate(const char* name, ArgvHandler argvHandlerFunc,
void* passThru)
{
status_t result;
for (;;) {
int ch = GetCh();
if (ch == EOF) {
if (fInDoubleQuote || fInSingleQuote) {
printf("File %s # unterminated quote at end of file\n", name);
result = B_ERROR;
break;
}
result = SendArgv(argvHandlerFunc, passThru);
break;
}
if (ch == '\n' || ch == '\r') {
fEatComment = false;
if (!fSawBackslash && (fInDoubleQuote || fInSingleQuote)) {
printf("File %s ; Line %" B_PRId32 " # unterminated quote\n",
name, fLineNo);
result = B_ERROR;
break;
}
fLineNo++;
if (fSawBackslash) {
fSawBackslash = false;
continue;
}
result = SendArgv(argvHandlerFunc, passThru);
continue;
}
if (fEatComment)
continue;
if (!fSawBackslash) {
if (!fInDoubleQuote && !fInSingleQuote) {
if (ch == ';') {
result = SendArgv(argvHandlerFunc, passThru);
if (result != B_OK)
break;
continue;
} else if (ch == '#') {
fEatComment = true;
continue;
} else if (ch == ' ' || ch == '\t') {
NextArgvIfNotEmpty();
continue;
} else if (!fSawBackslash && ch == '\\') {
fSawBackslash = true;
continue;
}
}
if (!fInSingleQuote && ch == '"') {
fInDoubleQuote = !fInDoubleQuote;
continue;
}
if (!fInDoubleQuote && ch == '\'') {
fInSingleQuote = !fInSingleQuote;
continue;
}
} else {
fCurrentArgs[++fCurrentArgsPos] = '\\';
fSawBackslash = false;
}
fCurrentArgs[++fCurrentArgsPos] = ch;
}
return result;
}
SettingsArgvDispatcher::SettingsArgvDispatcher(const char* name)
:
fName(name)
{
}
void
SettingsArgvDispatcher::SaveSettings(Settings* settings,
bool onlyIfNonDefault)
{
if (!onlyIfNonDefault || NeedsSaving()) {
settings->Write("%s ", Name());
SaveSettingValue(settings);
settings->Write("\n");
}
}
bool
SettingsArgvDispatcher::HandleRectValue(BRect &result,
const char* const* argv, bool printError)
{
if (!*argv) {
if (printError)
printf("rect left expected");
return false;
}
result.left = atoi(*argv);
if (!*++argv) {
if (printError)
printf("rect top expected");
return false;
}
result.top = atoi(*argv);
if (!*++argv) {
if (printError)
printf("rect right expected");
return false;
}
result.right = atoi(*argv);
if (!*++argv) {
if (printError)
printf("rect bottom expected");
return false;
}
result.bottom = atoi(*argv);
return true;
}
void
SettingsArgvDispatcher::WriteRectValue(Settings* setting, BRect rect)
{
setting->Write("%d %d %d %d", (int32)rect.left, (int32)rect.top,
(int32)rect.right, (int32)rect.bottom);
}
Settings::Settings(const char* filename, const char* settingsDirName)
:
fFileName(filename),
fSettingsDir(settingsDirName),
fList(0),
fCount(0),
fListSize(30),
fCurrentSettings(0)
{
fList = (SettingsArgvDispatcher**)calloc((size_t)fListSize,
sizeof(SettingsArgvDispatcher*));
}
Settings::~Settings()
{
for (int32 index = 0; index < fCount; index++)
delete fList[index];
free(fList);
}
const char*
Settings::ParseUserSettings(int, const char* const* argv, void* castToThis)
{
if (!*argv)
return 0;
SettingsArgvDispatcher* handler = ((Settings*)castToThis)->Find(*argv);
if (!handler)
return "unknown command";
return handler->Handle(argv);
}
bool
Settings::Add(SettingsArgvDispatcher* setting)
{
if (Find(setting->Name()))
return false;
if (fCount >= fListSize) {
fListSize += 30;
fList = (SettingsArgvDispatcher**)realloc(fList,
fListSize * sizeof(SettingsArgvDispatcher*));
}
fList[fCount++] = setting;
return true;
}
SettingsArgvDispatcher*
Settings::Find(const char* name)
{
for (int32 index = 0; index < fCount; index++)
if (strcmp(name, fList[index]->Name()) == 0)
return fList[index];
return NULL;
}
void
Settings::TryReadingSettings()
{
BPath prefsPath;
if (find_directory(B_USER_SETTINGS_DIRECTORY, &prefsPath, true) == B_OK) {
prefsPath.Append(fSettingsDir);
BPath path(prefsPath);
path.Append(fFileName);
ArgvParser::EachArgv(path.Path(), Settings::ParseUserSettings, this);
}
}
void
Settings::SaveSettings(bool onlyIfNonDefault)
{
SaveCurrentSettings(onlyIfNonDefault);
}
void
Settings::MakeSettingsDirectory(BDirectory* resultingSettingsDir)
{
BPath path;
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK)
return;
path.Append(fSettingsDir);
char* ptr = (char *)alloca(strlen(path.Path()) + 1);
strcpy(ptr, path.Path());
char* end = ptr+strlen(ptr);
char* mid = ptr+1;
while (mid < end) {
mid = strchr(mid, '/');
if (!mid) break;
*mid = 0;
mkdir(ptr, 0777);
*mid = '/';
mid++;
}
mkdir(ptr, 0777);
resultingSettingsDir->SetTo(path.Path());
}
void
Settings::SaveCurrentSettings(bool onlyIfNonDefault)
{
BDirectory settingsDir;
MakeSettingsDirectory(&settingsDir);
if (settingsDir.InitCheck() != B_OK)
return;
BEntry entry(&settingsDir, fFileName);
entry.Remove();
BFile prefs(&entry, O_RDWR | O_CREAT);
if (prefs.InitCheck() != B_OK)
return;
fCurrentSettings = &prefs;
for (int32 index = 0; index < fCount; index++)
fList[index]->SaveSettings(this, onlyIfNonDefault);
fCurrentSettings = NULL;
}
void
Settings::Write(const char* format, ...)
{
va_list args;
va_start(args, format);
VSWrite(format, args);
va_end(args);
}
void
Settings::VSWrite(const char* format, va_list arg)
{
char buffer[2048];
vsprintf(buffer, format, arg);
ASSERT(fCurrentSettings && fCurrentSettings->InitCheck() == B_OK);
fCurrentSettings->Write(buffer, strlen(buffer));
}