1/* 2Open Tracker License 3 4Terms and Conditions 5 6Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8Permission is hereby granted, free of charge, to any person obtaining a copy of 9this software and associated documentation files (the "Software"), to deal in 10the Software without restriction, including without limitation the rights to 11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12of the Software, and to permit persons to whom the Software is furnished to do 13so, subject to the following conditions: 14 15The above copyright notice and this permission notice applies to all licensees 16and shall be included in all copies or substantial portions of the Software. 17 18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25Except as contained in this notice, the name of Be Incorporated shall not be 26used in advertising or otherwise to promote the sale, use or other dealings in 27this Software without prior written authorization from Be Incorporated. 28 29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30of Be Incorporated in the United States and other countries. Other brand product 31names are registered trademarks or trademarks of their respective holders. 32All rights reserved. 33*/ 34 35#include <Debug.h> 36#include <Directory.h> 37#include <Entry.h> 38#include <FindDirectory.h> 39#include <File.h> 40#include <Path.h> 41#include <StopWatch.h> 42 43#include <alloca.h> 44#include <stdlib.h> 45#include <stdio.h> 46#include <string.h> 47#include <stdarg.h> 48 49 50#include "SettingsHandler.h" 51 52ArgvParser::ArgvParser(const char* name) 53 : fFile(0), 54 fBuffer(NULL), 55 fPos(-1), 56 fArgc(0), 57 fCurrentArgv(0), 58 fCurrentArgsPos(-1), 59 fSawBackslash(false), 60 fEatComment(false), 61 fInDoubleQuote(false), 62 fInSingleQuote(false), 63 fLineNo(0), 64 fFileName(name) 65{ 66 fFile = fopen(fFileName, "r"); 67 if (!fFile) { 68 PRINT(("Error opening %s\n", fFileName)); 69 return; 70 } 71 fBuffer = new char [kBufferSize]; 72 fCurrentArgv = new char * [1024]; 73} 74 75 76ArgvParser::~ArgvParser() 77{ 78 delete [] fBuffer; 79 80 MakeArgvEmpty(); 81 delete [] fCurrentArgv; 82 83 if (fFile) 84 fclose(fFile); 85} 86 87 88void 89ArgvParser::MakeArgvEmpty() 90{ 91 // done with current argv, free it up 92 for (int32 index = 0; index < fArgc; index++) 93 delete[] fCurrentArgv[index]; 94 95 fArgc = 0; 96} 97 98 99status_t 100ArgvParser::SendArgv(ArgvHandler argvHandlerFunc, void* passThru) 101{ 102 if (fArgc) { 103 NextArgv(); 104 fCurrentArgv[fArgc] = 0; 105 const char* result = (argvHandlerFunc)(fArgc, fCurrentArgv, passThru); 106 if (result) { 107 printf("File %s; Line %" B_PRId32 " # %s", fFileName, fLineNo, 108 result); 109 } 110 MakeArgvEmpty(); 111 if (result) 112 return B_ERROR; 113 } 114 115 return B_OK; 116} 117 118 119void 120ArgvParser::NextArgv() 121{ 122 if (fSawBackslash) { 123 fCurrentArgs[++fCurrentArgsPos] = '\\'; 124 fSawBackslash = false; 125 } 126 fCurrentArgs[++fCurrentArgsPos] = '\0'; 127 // terminate current arg pos 128 129 // copy it as a string to the current argv slot 130 fCurrentArgv[fArgc] = new char [strlen(fCurrentArgs) + 1]; 131 strcpy(fCurrentArgv[fArgc], fCurrentArgs); 132 fCurrentArgsPos = -1; 133 fArgc++; 134} 135 136 137void 138ArgvParser::NextArgvIfNotEmpty() 139{ 140 if (!fSawBackslash && fCurrentArgsPos < 0) 141 return; 142 143 NextArgv(); 144} 145 146 147char 148ArgvParser::GetCh() 149{ 150 if (fPos < 0 || fBuffer[fPos] == 0) { 151 if (fFile == 0) 152 return EOF; 153 if (fgets(fBuffer, kBufferSize, fFile) == 0) 154 return EOF; 155 fPos = 0; 156 } 157 return fBuffer[fPos++]; 158} 159 160 161status_t 162ArgvParser::EachArgv(const char* name, ArgvHandler argvHandlerFunc, 163 void* passThru) 164{ 165 ArgvParser parser(name); 166 return parser.EachArgvPrivate(name, argvHandlerFunc, passThru); 167} 168 169 170status_t 171ArgvParser::EachArgvPrivate(const char* name, ArgvHandler argvHandlerFunc, 172 void* passThru) 173{ 174 status_t result; 175 176 for (;;) { 177 char ch = GetCh(); 178 if (ch == EOF) { 179 // done with fFile 180 if (fInDoubleQuote || fInSingleQuote) { 181 printf("File %s # unterminated quote at end of file\n", name); 182 result = B_ERROR; 183 break; 184 } 185 result = SendArgv(argvHandlerFunc, passThru); 186 break; 187 } 188 189 if (ch == '\n' || ch == '\r') { 190 // handle new line 191 fEatComment = false; 192 if (!fSawBackslash && (fInDoubleQuote || fInSingleQuote)) { 193 printf("File %s ; Line %" B_PRId32 " # unterminated quote\n", 194 name, fLineNo); 195 result = B_ERROR; 196 break; 197 } 198 199 fLineNo++; 200 if (fSawBackslash) { 201 fSawBackslash = false; 202 continue; 203 } 204 205 // end of line, flush all argv 206 result = SendArgv(argvHandlerFunc, passThru); 207 208 continue; 209 } 210 211 if (fEatComment) 212 continue; 213 214 if (!fSawBackslash) { 215 if (!fInDoubleQuote && !fInSingleQuote) { 216 if (ch == ';') { 217 // semicolon is a command separator, pass on 218 // the whole argv 219 result = SendArgv(argvHandlerFunc, passThru); 220 if (result != B_OK) 221 break; 222 continue; 223 } else if (ch == '#') { 224 // ignore everything on this line after this character 225 fEatComment = true; 226 continue; 227 } else if (ch == ' ' || ch == '\t') { 228 // space or tab separates the individual arg strings 229 NextArgvIfNotEmpty(); 230 continue; 231 } else if (!fSawBackslash && ch == '\\') { 232 // the next character is escaped 233 fSawBackslash = true; 234 continue; 235 } 236 } 237 if (!fInSingleQuote && ch == '"') { 238 // enter/exit double quote handling 239 fInDoubleQuote = !fInDoubleQuote; 240 continue; 241 } 242 if (!fInDoubleQuote && ch == '\'') { 243 // enter/exit single quote handling 244 fInSingleQuote = !fInSingleQuote; 245 continue; 246 } 247 } else { 248 // we just pass through the escape sequence as is 249 fCurrentArgs[++fCurrentArgsPos] = '\\'; 250 fSawBackslash = false; 251 } 252 fCurrentArgs[++fCurrentArgsPos] = ch; 253 } 254 255 return result; 256} 257 258 259SettingsArgvDispatcher::SettingsArgvDispatcher(const char* name) 260 : name(name) 261{ 262} 263 264 265void 266SettingsArgvDispatcher::SaveSettings(Settings* settings, 267 bool onlyIfNonDefault) 268{ 269 if (!onlyIfNonDefault || NeedsSaving()) { 270 settings->Write("%s ", Name()); 271 SaveSettingValue(settings); 272 settings->Write("\n"); 273 } 274} 275 276 277bool 278SettingsArgvDispatcher::HandleRectValue(BRect &result, 279 const char* const* argv, bool printError) 280{ 281 if (!*argv) { 282 if (printError) 283 printf("rect left expected"); 284 return false; 285 } 286 result.left = atoi(*argv); 287 288 if (!*++argv) { 289 if (printError) 290 printf("rect top expected"); 291 return false; 292 } 293 result.top = atoi(*argv); 294 295 if (!*++argv) { 296 if (printError) 297 printf("rect right expected"); 298 return false; 299 } 300 result.right = atoi(*argv); 301 302 if (!*++argv) { 303 if (printError) 304 printf("rect bottom expected"); 305 return false; 306 } 307 result.bottom = atoi(*argv); 308 309 return true; 310} 311 312 313void 314SettingsArgvDispatcher::WriteRectValue(Settings* setting, BRect rect) 315{ 316 setting->Write("%d %d %d %d", (int32)rect.left, (int32)rect.top, 317 (int32)rect.right, (int32)rect.bottom); 318} 319 320 321Settings::Settings(const char* filename, const char* settingsDirName) 322 : fFileName(filename), 323 fSettingsDir(settingsDirName), 324 fList(0), 325 fCount(0), 326 fListSize(30), 327 fCurrentSettings(0) 328{ 329 fList = (SettingsArgvDispatcher**)calloc((size_t)fListSize, 330 sizeof(SettingsArgvDispatcher*)); 331} 332 333 334Settings::~Settings() 335{ 336 for (int32 index = 0; index < fCount; index++) 337 delete fList[index]; 338 339 free(fList); 340} 341 342 343const char* 344Settings::ParseUserSettings(int, const char* const* argv, void* castToThis) 345{ 346 if (!*argv) 347 return 0; 348 349 SettingsArgvDispatcher* handler = ((Settings*)castToThis)->Find(*argv); 350 if (!handler) 351 return "unknown command"; 352 353 return handler->Handle(argv); 354} 355 356 357bool 358Settings::Add(SettingsArgvDispatcher* setting) 359{ 360 // check for uniqueness 361 if (Find(setting->Name())) 362 return false; 363 364 if (fCount >= fListSize) { 365 fListSize += 30; 366 fList = (SettingsArgvDispatcher**)realloc(fList, 367 fListSize * sizeof(SettingsArgvDispatcher*)); 368 } 369 fList[fCount++] = setting; 370 return true; 371} 372 373 374SettingsArgvDispatcher* 375Settings::Find(const char* name) 376{ 377 for (int32 index = 0; index < fCount; index++) 378 if (strcmp(name, fList[index]->Name()) == 0) 379 return fList[index]; 380 381 return NULL; 382} 383 384 385void 386Settings::TryReadingSettings() 387{ 388 BPath prefsPath; 389 if (find_directory(B_USER_SETTINGS_DIRECTORY, &prefsPath, true) == B_OK) { 390 prefsPath.Append(fSettingsDir); 391 392 BPath path(prefsPath); 393 path.Append(fFileName); 394 ArgvParser::EachArgv(path.Path(), Settings::ParseUserSettings, this); 395 } 396} 397 398 399void 400Settings::SaveSettings(bool onlyIfNonDefault) 401{ 402 SaveCurrentSettings(onlyIfNonDefault); 403} 404 405 406void 407Settings::MakeSettingsDirectory(BDirectory* resultingSettingsDir) 408{ 409 BPath path; 410 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK) 411 return; 412 413 // make sure there is a directory 414 // mkdir() will only make one leaf at a time, unfortunately 415 path.Append(fSettingsDir); 416 char* ptr = (char *)alloca(strlen(path.Path()) + 1); 417 strcpy(ptr, path.Path()); 418 char* end = ptr+strlen(ptr); 419 char* mid = ptr+1; 420 while (mid < end) { 421 mid = strchr(mid, '/'); 422 if (!mid) break; 423 *mid = 0; 424 mkdir(ptr, 0777); 425 *mid = '/'; 426 mid++; 427 } 428 mkdir(ptr, 0777); 429 resultingSettingsDir->SetTo(path.Path()); 430} 431 432 433void 434Settings::SaveCurrentSettings(bool onlyIfNonDefault) 435{ 436 BDirectory settingsDir; 437 MakeSettingsDirectory(&settingsDir); 438 439 if (settingsDir.InitCheck() != B_OK) 440 return; 441 442 // nuke old settings 443 BEntry entry(&settingsDir, fFileName); 444 entry.Remove(); 445 446 BFile prefs(&entry, O_RDWR | O_CREAT); 447 if (prefs.InitCheck() != B_OK) 448 return; 449 450 fCurrentSettings = &prefs; 451 for (int32 index = 0; index < fCount; index++) 452 fList[index]->SaveSettings(this, onlyIfNonDefault); 453 454 fCurrentSettings = NULL; 455} 456 457 458void 459Settings::Write(const char* format, ...) 460{ 461 va_list args; 462 463 va_start(args, format); 464 VSWrite(format, args); 465 va_end(args); 466} 467 468 469void 470Settings::VSWrite(const char* format, va_list arg) 471{ 472 char fBuffer[2048]; 473 vsprintf(fBuffer, format, arg); 474 ASSERT(fCurrentSettings && fCurrentSettings->InitCheck() == B_OK); 475 fCurrentSettings->Write(fBuffer, strlen(fBuffer)); 476} 477