repository_infos: Enable automatic updates on the main Haiku repostiory.
[haiku.git] / src / apps / codycam / SettingsHandler.cpp
blobc04c2566e4ac9064530dee1c4d11cfdb47f2436f
1 #include "SettingsHandler.h"
3 #include <stdarg.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
8 #include <Catalog.h>
9 #include <Debug.h>
10 #include <Directory.h>
11 #include <Entry.h>
12 #include <File.h>
13 #include <FindDirectory.h>
14 #include <Locale.h>
15 #include <Path.h>
16 #include <StopWatch.h>
19 #undef B_TRANSLATION_CONTEXT
20 #define B_TRANSLATION_CONTEXT "SettingsHandler"
23 #if 0
24 static int
25 Compare(const SettingsArgvDispatcher* p1, const SettingsArgvDispatcher* p2)
27 return strcmp(p1->Name(), p2->Name());
29 #endif
32 #if 0
33 static int
34 CompareByNameOne(const SettingsArgvDispatcher* item1,
35 const SettingsArgvDispatcher* item2)
37 return strcmp(item1->Name(), item2->Name());
39 #endif
42 /*! \class ArgvParser
43 ArgvParser class opens a text file and passes the context in argv
44 format to a specified handler
48 ArgvParser::ArgvParser(const char* name)
50 fFile(0),
51 fBuffer(0),
52 fPos(-1),
53 fArgc(0),
54 fCurrentArgv(0),
55 fCurrentArgsPos(-1),
56 fSawBackslash(false),
57 fEatComment(false),
58 fInDoubleQuote(false),
59 fInSingleQuote(false),
60 fLineNo(0),
61 fFileName(name)
63 fFile = fopen(fFileName, "r");
64 if (!fFile) {
65 PRINT((B_TRANSLATE("Error opening %s\n"), fFileName));
66 return;
68 fBuffer = new char [kBufferSize];
69 fCurrentArgv = new char* [1024];
73 ArgvParser::~ArgvParser()
75 delete[] fBuffer;
77 MakeArgvEmpty();
78 delete [] fCurrentArgv;
80 if (fFile)
81 fclose(fFile);
85 void
86 ArgvParser::MakeArgvEmpty()
88 // done with current argv, free it up
89 for (int32 index = 0; index < fArgc; index++)
90 delete fCurrentArgv[index];
92 fArgc = 0;
96 status_t
97 ArgvParser::SendArgv(ArgvHandler argvHandlerFunc, void* passThru)
99 if (fArgc) {
100 NextArgv();
101 fCurrentArgv[fArgc] = 0;
102 const char *result = (argvHandlerFunc)(fArgc, fCurrentArgv, passThru);
103 if (result)
104 printf(B_TRANSLATE("File %s; Line %ld # %s"), fFileName, fLineNo, result);
105 MakeArgvEmpty();
106 if (result)
107 return B_ERROR;
110 return B_NO_ERROR;
114 void
115 ArgvParser::NextArgv()
117 if (fSawBackslash) {
118 fCurrentArgs[++fCurrentArgsPos] = '\\';
119 fSawBackslash = false;
121 fCurrentArgs[++fCurrentArgsPos] = '\0';
122 // terminate current arg pos
124 // copy it as a string to the current argv slot
125 fCurrentArgv[fArgc] = new char [strlen(fCurrentArgs) + 1];
126 strcpy(fCurrentArgv[fArgc], fCurrentArgs);
127 fCurrentArgsPos = -1;
128 fArgc++;
132 void
133 ArgvParser::NextArgvIfNotEmpty()
135 if (!fSawBackslash && fCurrentArgsPos < 0)
136 return;
138 NextArgv();
142 char
143 ArgvParser::GetCh()
145 if (fPos < 0 || fBuffer[fPos] == 0) {
146 if (fFile == 0)
147 return EOF;
148 if (fgets(fBuffer, kBufferSize, fFile) == 0)
149 return EOF;
150 fPos = 0;
152 return fBuffer[fPos++];
156 status_t
157 ArgvParser::EachArgv(const char* name, ArgvHandler argvHandlerFunc,
158 void* passThru)
160 ArgvParser parser(name);
161 return parser.EachArgvPrivate(name, argvHandlerFunc, passThru);
165 status_t
166 ArgvParser::EachArgvPrivate(const char* name, ArgvHandler argvHandlerFunc,
167 void* passThru)
169 status_t result;
171 for (;;) {
172 char ch = GetCh();
173 if (ch == EOF) {
174 // done with file
175 if (fInDoubleQuote || fInSingleQuote) {
176 printf(B_TRANSLATE("File %s # unterminated quote at end of "
177 "file\n"), name);
178 result = B_ERROR;
179 break;
181 result = SendArgv(argvHandlerFunc, passThru);
182 break;
185 if (ch == '\n' || ch == '\r') {
186 // handle new line
187 fEatComment = false;
188 if (!fSawBackslash && (fInDoubleQuote || fInSingleQuote)) {
189 printf(B_TRANSLATE("File %s ; Line %ld # unterminated "
190 "quote\n"), name, fLineNo);
191 result = B_ERROR;
192 break;
194 fLineNo++;
195 if (fSawBackslash) {
196 fSawBackslash = false;
197 continue;
199 // end of line, flush all argv
200 result = SendArgv(argvHandlerFunc, passThru);
201 if (result != B_NO_ERROR)
202 break;
203 continue;
206 if (fEatComment)
207 continue;
209 if (!fSawBackslash) {
210 if (!fInDoubleQuote && !fInSingleQuote) {
211 if (ch == ';') {
212 // semicolon is a command separator, pass on the whole argv
213 result = SendArgv(argvHandlerFunc, passThru);
214 if (result != B_NO_ERROR)
215 break;
216 continue;
217 } else if (ch == '#') {
218 // ignore everything on this line after this character
219 fEatComment = true;
220 continue;
221 } else if (ch == ' ' || ch == '\t') {
222 // space or tab separates the individual arg strings
223 NextArgvIfNotEmpty();
224 continue;
225 } else if (!fSawBackslash && ch == '\\') {
226 // the next character is escaped
227 fSawBackslash = true;
228 continue;
231 if (!fInSingleQuote && ch == '"') {
232 // enter/exit double quote handling
233 fInDoubleQuote = !fInDoubleQuote;
234 continue;
236 if (!fInDoubleQuote && ch == '\'') {
237 // enter/exit single quote handling
238 fInSingleQuote = !fInSingleQuote;
239 continue;
241 } else {
242 // we just pass through the escape sequence as is
243 fCurrentArgs[++fCurrentArgsPos] = '\\';
244 fSawBackslash = false;
246 fCurrentArgs[++fCurrentArgsPos] = ch;
249 return result;
253 // #pragma mark -
256 SettingsArgvDispatcher::SettingsArgvDispatcher(const char* name)
258 fName(name)
263 void
264 SettingsArgvDispatcher::SaveSettings(Settings* settings, bool onlyIfNonDefault)
266 if (!onlyIfNonDefault || NeedsSaving()) {
267 settings->Write("%s ", Name());
268 SaveSettingValue(settings);
269 settings->Write("\n");
274 bool
275 SettingsArgvDispatcher::HandleRectValue(BRect &result, const char* const *argv,
276 bool printError)
278 if (!*argv) {
279 if (printError)
280 printf("rect left expected");
281 return false;
283 result.left = atoi(*argv);
284 if (!*++argv) {
285 if (printError)
286 printf("rect top expected");
287 return false;
289 result.top = atoi(*argv);
290 if (!*++argv) {
291 if (printError)
292 printf("rect right expected");
293 return false;
295 result.right = atoi(*argv);
296 if (!*++argv) {
297 if (printError)
298 printf("rect bottom expected");
299 return false;
301 result.bottom = atoi(*argv);
302 return true;
306 void
307 SettingsArgvDispatcher::WriteRectValue(Settings* setting, BRect rect)
309 setting->Write("%d %d %d %d", (int32)rect.left, (int32)rect.top,
310 (int32)rect.right, (int32)rect.bottom);
314 // #pragma mark -
317 /*! \class Settings
318 this class represents a list of all the settings handlers, reads and
319 saves the settings file
323 Settings::Settings(const char* filename, const char* settingsDirName)
325 fFileName(filename),
326 fSettingsDir(settingsDirName),
327 fList(0),
328 fCount(0),
329 fListSize(30),
330 fCurrentSettings(0)
332 #ifdef SINGLE_SETTING_FILE
333 settingsHandler = this;
334 #endif
335 fList = (SettingsArgvDispatcher**)calloc(fListSize, sizeof(SettingsArgvDispatcher *));
339 Settings::~Settings()
341 for (int32 index = 0; index < fCount; index++)
342 delete fList[index];
344 free(fList);
348 const char*
349 Settings::_ParseUserSettings(int, const char* const *argv, void* castToThis)
351 if (!*argv)
352 return 0;
354 #ifdef SINGLE_SETTING_FILE
355 Settings* settings = settingsHandler;
356 #else
357 Settings* settings = (Settings*)castToThis;
358 #endif
360 SettingsArgvDispatcher* handler = settings->_Find(*argv);
361 if (!handler)
362 return B_TRANSLATE("unknown command");
363 return handler->Handle(argv);
368 Returns false if argv dispatcher with the same name already
369 registered
371 bool
372 Settings::Add(SettingsArgvDispatcher* setting)
374 // check for uniqueness
375 if (_Find(setting->Name()))
376 return false;
378 if (fCount >= fListSize) {
379 fListSize += 30;
380 fList = (SettingsArgvDispatcher **)realloc(fList,
381 fListSize * sizeof(SettingsArgvDispatcher *));
383 fList[fCount++] = setting;
384 return true;
388 SettingsArgvDispatcher*
389 Settings::_Find(const char* name)
391 for (int32 index = 0; index < fCount; index++)
392 if (strcmp(name, fList[index]->Name()) == 0)
393 return fList[index];
395 return 0;
399 void
400 Settings::TryReadingSettings()
402 BPath prefsPath;
403 if (find_directory(B_USER_SETTINGS_DIRECTORY, &prefsPath, true) == B_OK) {
404 prefsPath.Append(fSettingsDir);
406 BPath path(prefsPath);
407 path.Append(fFileName);
408 ArgvParser::EachArgv(path.Path(), Settings::_ParseUserSettings, this);
413 void
414 Settings::SaveSettings(bool onlyIfNonDefault)
416 ASSERT(SettingsHandler());
417 SettingsHandler()->_SaveCurrentSettings(onlyIfNonDefault);
421 void
422 Settings::_MakeSettingsDirectory(BDirectory *resultingSettingsDir)
424 BPath path;
425 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK)
426 return;
428 // make sure there is a directory
429 path.Append(fSettingsDir);
430 mkdir(path.Path(), 0777);
431 resultingSettingsDir->SetTo(path.Path());
435 void
436 Settings::_SaveCurrentSettings(bool onlyIfNonDefault)
438 BDirectory fSettingsDir;
439 _MakeSettingsDirectory(&fSettingsDir);
441 if (fSettingsDir.InitCheck() != B_OK)
442 return;
444 printf("+++++++++++ Settings::_SaveCurrentSettings %s\n", fFileName);
445 // nuke old settings
446 BEntry entry(&fSettingsDir, fFileName);
447 entry.Remove();
449 BFile prefs(&entry, O_RDWR | O_CREAT);
450 if (prefs.InitCheck() != B_OK)
451 return;
453 fCurrentSettings = &prefs;
454 for (int32 index = 0; index < fCount; index++) {
455 fList[index]->SaveSettings(this, onlyIfNonDefault);
458 fCurrentSettings = 0;
462 void
463 Settings::Write(const char* format, ...)
465 va_list args;
467 va_start(args, format);
468 VSWrite(format, args);
469 va_end(args);
473 void
474 Settings::VSWrite(const char* format, va_list arg)
476 char buffer[2048];
477 vsprintf(buffer, format, arg);
478 ASSERT(fCurrentSettings && fCurrentSettings->InitCheck() == B_OK);
479 fCurrentSettings->Write(buffer, strlen(buffer));
483 #ifdef SINGLE_SETTING_FILE
484 Settings* Settings::settingsHandler = 0;
485 #endif