1 /* 2 * Copyright 2015, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "Conditions.h" 8 9 #include <stdio.h> 10 11 #include <Entry.h> 12 #include <ObjectList.h> 13 #include <Message.h> 14 #include <StringList.h> 15 16 #include "Utility.h" 17 18 19 class ConditionContainer : public Condition { 20 protected: 21 ConditionContainer(const BMessage& args); 22 ConditionContainer(); 23 24 public: 25 void AddCondition(Condition* condition); 26 27 virtual bool IsConstant(ConditionContext& context) const; 28 29 protected: 30 void AddConditionsToString(BString& string) const; 31 32 protected: 33 BObjectList<Condition> fConditions; 34 }; 35 36 37 class AndCondition : public ConditionContainer { 38 public: 39 AndCondition(const BMessage& args); 40 AndCondition(); 41 42 virtual bool Test(ConditionContext& context) const; 43 virtual BString ToString() const; 44 }; 45 46 47 class OrCondition : public ConditionContainer { 48 public: 49 OrCondition(const BMessage& args); 50 51 virtual bool Test(ConditionContext& context) const; 52 virtual bool IsConstant(ConditionContext& context) const; 53 54 virtual BString ToString() const; 55 }; 56 57 58 class NotCondition : public ConditionContainer { 59 public: 60 NotCondition(const BMessage& args); 61 NotCondition(); 62 63 virtual bool Test(ConditionContext& context) const; 64 virtual BString ToString() const; 65 }; 66 67 68 class SafeModeCondition : public Condition { 69 public: 70 virtual bool Test(ConditionContext& context) const; 71 virtual bool IsConstant(ConditionContext& context) const; 72 73 virtual BString ToString() const; 74 }; 75 76 77 class ReadOnlyCondition : public Condition { 78 public: 79 ReadOnlyCondition(const BMessage& args); 80 81 virtual bool Test(ConditionContext& context) const; 82 virtual bool IsConstant(ConditionContext& context) const; 83 84 virtual BString ToString() const; 85 86 private: 87 BString fPath; 88 mutable bool fIsReadOnly; 89 mutable bool fTestPerformed; 90 }; 91 92 93 class FileExistsCondition : public Condition { 94 public: 95 FileExistsCondition(const BMessage& args); 96 97 virtual bool Test(ConditionContext& context) const; 98 virtual BString ToString() const; 99 100 private: 101 BStringList fPaths; 102 }; 103 104 105 static Condition* 106 create_condition(const char* name, const BMessage& args) 107 { 108 if (strcmp(name, "and") == 0 && !args.IsEmpty()) 109 return new AndCondition(args); 110 if (strcmp(name, "or") == 0 && !args.IsEmpty()) 111 return new OrCondition(args); 112 if (strcmp(name, "not") == 0 && !args.IsEmpty()) 113 return new NotCondition(args); 114 115 if (strcmp(name, "safemode") == 0) 116 return new SafeModeCondition(); 117 if (strcmp(name, "read_only") == 0) 118 return new ReadOnlyCondition(args); 119 if (strcmp(name, "file_exists") == 0) 120 return new FileExistsCondition(args); 121 122 return NULL; 123 } 124 125 126 // #pragma mark - 127 128 129 Condition::Condition() 130 { 131 } 132 133 134 Condition::~Condition() 135 { 136 } 137 138 139 bool 140 Condition::IsConstant(ConditionContext& context) const 141 { 142 return false; 143 } 144 145 146 // #pragma mark - 147 148 149 ConditionContainer::ConditionContainer(const BMessage& args) 150 : 151 fConditions(10, true) 152 { 153 char* name; 154 type_code type; 155 int32 count; 156 for (int32 index = 0; args.GetInfo(B_MESSAGE_TYPE, index, &name, &type, 157 &count) == B_OK; index++) { 158 BMessage message; 159 for (int32 messageIndex = 0; args.FindMessage(name, messageIndex, 160 &message) == B_OK; messageIndex++) { 161 AddCondition(create_condition(name, message)); 162 } 163 } 164 } 165 166 167 ConditionContainer::ConditionContainer() 168 : 169 fConditions(10, true) 170 { 171 } 172 173 174 void 175 ConditionContainer::AddCondition(Condition* condition) 176 { 177 if (condition != NULL) 178 fConditions.AddItem(condition); 179 } 180 181 182 /*! A single constant failing condition makes this constant, too, otherwise, 183 a single non-constant condition makes this non-constant as well. 184 */ 185 bool 186 ConditionContainer::IsConstant(ConditionContext& context) const 187 { 188 bool fixed = true; 189 for (int32 index = 0; index < fConditions.CountItems(); index++) { 190 const Condition* condition = fConditions.ItemAt(index); 191 if (condition->IsConstant(context)) { 192 if (!condition->Test(context)) 193 return true; 194 } else 195 fixed = false; 196 } 197 return fixed; 198 } 199 200 201 void 202 ConditionContainer::AddConditionsToString(BString& string) const 203 { 204 string += "["; 205 206 for (int32 index = 0; index < fConditions.CountItems(); index++) { 207 if (index != 0) 208 string += ", "; 209 string += fConditions.ItemAt(index)->ToString(); 210 } 211 string += "]"; 212 } 213 214 215 // #pragma mark - and 216 217 218 AndCondition::AndCondition(const BMessage& args) 219 : 220 ConditionContainer(args) 221 { 222 } 223 224 225 AndCondition::AndCondition() 226 { 227 } 228 229 230 bool 231 AndCondition::Test(ConditionContext& context) const 232 { 233 for (int32 index = 0; index < fConditions.CountItems(); index++) { 234 Condition* condition = fConditions.ItemAt(index); 235 if (!condition->Test(context)) 236 return false; 237 } 238 return true; 239 } 240 241 242 BString 243 AndCondition::ToString() const 244 { 245 BString string = "and "; 246 ConditionContainer::AddConditionsToString(string); 247 return string; 248 } 249 250 251 // #pragma mark - or 252 253 254 OrCondition::OrCondition(const BMessage& args) 255 : 256 ConditionContainer(args) 257 { 258 } 259 260 261 bool 262 OrCondition::Test(ConditionContext& context) const 263 { 264 if (fConditions.IsEmpty()) 265 return true; 266 267 for (int32 index = 0; index < fConditions.CountItems(); index++) { 268 Condition* condition = fConditions.ItemAt(index); 269 if (condition->Test(context)) 270 return true; 271 } 272 return false; 273 } 274 275 276 /*! If there is a single succeeding constant condition, this is constant, too. 277 Otherwise, it is non-constant if there is a single non-constant condition. 278 */ 279 bool 280 OrCondition::IsConstant(ConditionContext& context) const 281 { 282 bool fixed = true; 283 for (int32 index = 0; index < fConditions.CountItems(); index++) { 284 const Condition* condition = fConditions.ItemAt(index); 285 if (condition->IsConstant(context)) { 286 if (condition->Test(context)) 287 return true; 288 } else 289 fixed = false; 290 } 291 return fixed; 292 } 293 294 295 BString 296 OrCondition::ToString() const 297 { 298 BString string = "or "; 299 ConditionContainer::AddConditionsToString(string); 300 return string; 301 } 302 303 304 // #pragma mark - or 305 306 307 NotCondition::NotCondition(const BMessage& args) 308 : 309 ConditionContainer(args) 310 { 311 } 312 313 314 NotCondition::NotCondition() 315 { 316 } 317 318 319 bool 320 NotCondition::Test(ConditionContext& context) const 321 { 322 for (int32 index = 0; index < fConditions.CountItems(); index++) { 323 Condition* condition = fConditions.ItemAt(index); 324 if (condition->Test(context)) 325 return false; 326 } 327 return true; 328 } 329 330 331 BString 332 NotCondition::ToString() const 333 { 334 BString string = "not "; 335 ConditionContainer::AddConditionsToString(string); 336 return string; 337 } 338 339 340 // #pragma mark - safemode 341 342 343 bool 344 SafeModeCondition::Test(ConditionContext& context) const 345 { 346 return context.IsSafeMode(); 347 } 348 349 350 bool 351 SafeModeCondition::IsConstant(ConditionContext& context) const 352 { 353 return true; 354 } 355 356 357 BString 358 SafeModeCondition::ToString() const 359 { 360 return "safemode"; 361 } 362 363 364 // #pragma mark - read_only 365 366 367 ReadOnlyCondition::ReadOnlyCondition(const BMessage& args) 368 : 369 fPath(args.GetString("args")), 370 fIsReadOnly(false), 371 fTestPerformed(false) 372 { 373 } 374 375 376 bool 377 ReadOnlyCondition::Test(ConditionContext& context) const 378 { 379 if (fTestPerformed) 380 return fIsReadOnly; 381 382 if (fPath.IsEmpty() || fPath == "/boot") 383 fIsReadOnly = context.BootVolumeIsReadOnly(); 384 else 385 fIsReadOnly = Utility::IsReadOnlyVolume(fPath); 386 387 fTestPerformed = true; 388 389 return fIsReadOnly; 390 } 391 392 393 bool 394 ReadOnlyCondition::IsConstant(ConditionContext& context) const 395 { 396 return true; 397 } 398 399 400 BString 401 ReadOnlyCondition::ToString() const 402 { 403 BString string = "readonly "; 404 string << fPath; 405 return string; 406 } 407 408 409 // #pragma mark - file_exists 410 411 412 FileExistsCondition::FileExistsCondition(const BMessage& args) 413 { 414 for (int32 index = 0; 415 const char* path = args.GetString("args", index, NULL); index++) { 416 fPaths.Add(path); 417 } 418 } 419 420 421 bool 422 FileExistsCondition::Test(ConditionContext& context) const 423 { 424 for (int32 index = 0; index < fPaths.CountStrings(); index++) { 425 BEntry entry; 426 if (entry.SetTo(fPaths.StringAt(index)) != B_OK 427 || !entry.Exists()) 428 return false; 429 } 430 return true; 431 } 432 433 434 BString 435 FileExistsCondition::ToString() const 436 { 437 BString string = "file_exists ["; 438 for (int32 index = 0; index < fPaths.CountStrings(); index++) { 439 if (index != 0) 440 string << ", "; 441 string << fPaths.StringAt(index); 442 } 443 string += "]"; 444 return string; 445 } 446 447 448 // #pragma mark - 449 450 451 /*static*/ Condition* 452 Conditions::FromMessage(const BMessage& message) 453 { 454 return create_condition("and", message); 455 } 456 457 458 /*static*/ Condition* 459 Conditions::AddNotSafeMode(Condition* condition) 460 { 461 AndCondition* andCondition = dynamic_cast<AndCondition*>(condition); 462 if (andCondition == NULL) 463 andCondition = new AndCondition(); 464 if (andCondition != condition && condition != NULL) 465 andCondition->AddCondition(condition); 466 467 NotCondition* notCondition = new NotCondition(); 468 notCondition->AddCondition(new SafeModeCondition()); 469 470 andCondition->AddCondition(notCondition); 471 return andCondition; 472 } 473