/* Open Tracker License Terms and Conditions Copyright (c) 1991-2000, Be Incorporated. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice applies to all licensees and shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Be Incorporated shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Be Incorporated. Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks of Be Incorporated in the United States and other countries. Other brand product names are registered trademarks or trademarks of their respective holders. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "SettingsHandler.h" // #pragma mark - ArgvParser /*! \class ArgvParser ArgvParser class opens a text file and passes the context in argv format to a specified handler */ 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() { // done with current argv, free it up 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'; // terminate current arg pos // copy it as a string to the current argv slot 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) { // done with fFile 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') { // handle new line 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; } // end of line, flush all argv result = SendArgv(argvHandlerFunc, passThru); continue; } if (fEatComment) continue; if (!fSawBackslash) { if (!fInDoubleQuote && !fInSingleQuote) { if (ch == ';') { // semicolon is a command separator, pass on // the whole argv result = SendArgv(argvHandlerFunc, passThru); if (result != B_OK) break; continue; } else if (ch == '#') { // ignore everything on this line after this character fEatComment = true; continue; } else if (ch == ' ' || ch == '\t') { // space or tab separates the individual arg strings NextArgvIfNotEmpty(); continue; } else if (!fSawBackslash && ch == '\\') { // the next character is escaped fSawBackslash = true; continue; } } if (!fInSingleQuote && ch == '"') { // enter/exit double quote handling fInDoubleQuote = !fInDoubleQuote; continue; } if (!fInDoubleQuote && ch == '\'') { // enter/exit single quote handling fInSingleQuote = !fInSingleQuote; continue; } } else { // we just pass through the escape sequence as is fCurrentArgs[++fCurrentArgsPos] = '\\'; fSawBackslash = false; } fCurrentArgs[++fCurrentArgsPos] = ch; } return result; } // #pragma mark - SettingsArgvDispatcher 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); } /*! \class Settings this class represents a list of all the settings handlers, reads and saves the settings file */ 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); } /*! Returns false if argv dispatcher with the same name already registered */ bool Settings::Add(SettingsArgvDispatcher* setting) { // check for uniqueness 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; // make sure there is a directory // mkdir() will only make one leaf at a time, unfortunately 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; // nuke old settings 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)); }