1 /*
   2  * Copyright (c) 2014, Oracle and/or its affiliates.
   3  * All rights reserved. Use is subject to license terms.
   4  *
   5  * This file is available and licensed under the following license:
   6  *
   7  * Redistribution and use in source and binary forms, with or without
   8  * modification, are permitted provided that the following conditions
   9  * are met:
  10  *
  11  *  - Redistributions of source code must retain the above copyright
  12  *    notice, this list of conditions and the following disclaimer.
  13  *  - Redistributions in binary form must reproduce the above copyright
  14  *    notice, this list of conditions and the following disclaimer in
  15  *    the documentation and/or other materials provided with the distribution.
  16  *  - Neither the name of Oracle Corporation nor the names of its
  17  *    contributors may be used to endorse or promote products derived
  18  *    from this software without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31  */
  32 
  33 
  34 #include "FilePath.h"
  35 
  36 #include <algorithm>
  37 #include <list>
  38 
  39 #ifdef WINDOWS
  40 #include <ShellAPI.h>
  41 #endif //WINDOWS
  42 
  43 #ifdef POSIX
  44 #include <sys/stat.h>
  45 #endif //POSIX
  46 
  47 
  48 bool FilePath::FileExists(const TString FileName) {
  49     bool result = false;
  50 #ifdef WINDOWS
  51     WIN32_FIND_DATA FindFileData;
  52     HANDLE handle = FindFirstFile(FileName.data(), &FindFileData);
  53 
  54     if (handle != INVALID_HANDLE_VALUE) {
  55         if (FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes) {
  56             result = true;
  57         }
  58         else {
  59             result = true;
  60         }
  61 
  62         FindClose(handle);
  63     }
  64 #endif //WINDOWS
  65 #ifdef POSIX
  66     struct stat buf;
  67 
  68     if ((stat(StringToFileSystemString(FileName), &buf) == 0) && (S_ISREG(buf.st_mode) != 0)) {
  69         result = true;
  70     }
  71 #endif //POSIX
  72     return result;
  73 }
  74 
  75 bool FilePath::DirectoryExists(const TString DirectoryName) {
  76     bool result = false;
  77 #ifdef WINDOWS
  78     WIN32_FIND_DATA FindFileData;
  79     HANDLE handle = FindFirstFile(DirectoryName.data(), &FindFileData);
  80 
  81     if (handle != INVALID_HANDLE_VALUE) {
  82         if (FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes) {
  83             result = true;
  84         }
  85 
  86         FindClose(handle);
  87     }
  88 #endif //WINDOWS
  89 #ifdef POSIX
  90     struct stat buf;
  91 
  92     if ((stat(StringToFileSystemString(DirectoryName), &buf) == 0) && (S_ISDIR(buf.st_mode) != 0)) {
  93         result = true;
  94     }
  95 #endif //POSIX
  96     return result;
  97 }
  98 
  99 #ifdef WINDOWS
 100 std::string GetLastErrorAsString() {
 101     //Get the error message, if any.
 102     DWORD errorMessageID = ::GetLastError();
 103 
 104     if (errorMessageID == 0) {
 105         return "No error message has been recorded";
 106     }
 107 
 108     LPSTR messageBuffer = NULL;
 109     size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
 110                                  NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
 111 
 112     std::string message(messageBuffer, size);
 113 
 114     // Free the buffer.
 115     LocalFree(messageBuffer);
 116 
 117     return message;
 118 }
 119 #endif //WINDOWS
 120 
 121 bool FilePath::DeleteFile(const TString FileName) {
 122     bool result = false;
 123 
 124     if (FileExists(FileName) == true) {
 125 #ifdef WINDOWS
 126         TString lFileName = FileName;
 127         FileAttributes attributes(lFileName);
 128 
 129         if (attributes.Contains(faReadOnly) == true) {
 130             attributes.Remove(faReadOnly);
 131         }
 132 
 133         result = ::DeleteFile(lFileName.data()) == TRUE;
 134 #endif //WINDOWS
 135 #ifdef POSIX
 136         if (unlink(StringToFileSystemString(FileName)) == 0) {
 137             result = true;
 138         }
 139 #endif //POSIX
 140     }
 141 
 142     return result;
 143 }
 144 
 145 bool FilePath::DeleteDirectory(const TString DirectoryName) {
 146     bool result = false;
 147 
 148     if (DirectoryExists(DirectoryName) == true) {
 149 #ifdef WINDOWS
 150         SHFILEOPSTRUCTW fos = {0};
 151         DynamicBuffer<TCHAR> lDirectoryName(DirectoryName.size() + 2);
 152         memcpy(lDirectoryName.GetData(), DirectoryName.data(), (DirectoryName.size() + 2) * sizeof(TCHAR));
 153         lDirectoryName[DirectoryName.size() + 1] = NULL; // Double null terminate for SHFileOperation.
 154 
 155         // Delete the folder and everything inside.
 156         fos.wFunc = FO_DELETE;
 157         fos.pFrom = lDirectoryName.GetData();
 158         fos.fFlags = FOF_NO_UI;
 159         result = SHFileOperation(&fos) == 0;
 160 #endif //WINDOWS
 161 #ifdef POSIX
 162         if (unlink(StringToFileSystemString(DirectoryName)) == 0) {
 163             result = true;
 164         }
 165 #endif //POSIX
 166     }
 167 
 168     return result;
 169 }
 170 
 171 TString FilePath::IncludeTrailingSeparater(const TString value) {
 172     TString result = value;
 173 
 174     if (value.size() > 0) {
 175         TString::iterator i = result.end();
 176         i--;
 177 
 178         if (*i != TRAILING_PATHSEPARATOR) {
 179             result += TRAILING_PATHSEPARATOR;
 180         }
 181     }
 182 
 183     return result;
 184 }
 185 
 186 TString FilePath::IncludeTrailingSeparater(const char* value) {
 187     TString lvalue = PlatformString(value).toString();
 188     return IncludeTrailingSeparater(lvalue);
 189 }
 190 
 191 TString FilePath::IncludeTrailingSeparater(const wchar_t* value) {
 192     TString lvalue = PlatformString(value).toString();
 193     return IncludeTrailingSeparater(lvalue);
 194 }
 195 
 196 TString FilePath::ExtractFilePath(TString Path) {
 197 #ifdef WINDOWS
 198     TString result;
 199     size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR);
 200     if (slash != TString::npos)
 201         result = Path.substr(0, slash);
 202     return result;
 203 #endif //WINDOWS
 204 #ifdef POSIX
 205     return dirname(StringToFileSystemString(Path));
 206 #endif //POSIX
 207 }
 208 
 209 TString FilePath::ExtractFileExt(TString Path) {
 210     TString result;
 211     size_t dot = Path.find_last_of('.');
 212 
 213     if (dot != TString::npos) {
 214         result  = Path.substr(dot, Path.size() - dot);
 215     }
 216 
 217     return result;
 218 }
 219 
 220 TString FilePath::ExtractFileName(TString Path) {
 221 #ifdef WINDOWS
 222     TString result;
 223 
 224     size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR);
 225     if (slash != TString::npos)
 226         result = Path.substr(slash + 1, Path.size() - slash - 1);
 227 
 228     return result;
 229 #endif // WINDOWS
 230 #ifdef POSIX
 231     return basename(StringToFileSystemString(Path));
 232 #endif //POSIX
 233 }
 234 
 235 TString FilePath::ChangeFileExt(TString Path, TString Extension) {
 236     TString result;
 237     size_t dot = Path.find_last_of('.');
 238 
 239     if (dot != TString::npos) {
 240         result = Path.substr(0, dot) + Extension;
 241     }
 242 
 243     if (result.empty() == true) {
 244         result = Path;
 245     }
 246 
 247     return result;
 248 }
 249 
 250 TString FilePath::FixPathForPlatform(TString Path) {
 251     TString result = Path;
 252     std::replace(result.begin(), result.end(), BAD_TRAILING_PATHSEPARATOR, TRAILING_PATHSEPARATOR);
 253     return result;
 254 }
 255 
 256 TString FilePath::FixPathSeparatorForPlatform(TString Path) {
 257     TString result = Path;
 258     std::replace(result.begin(), result.end(), BAD_PATH_SEPARATOR, PATH_SEPARATOR);
 259     return result;
 260 }
 261 
 262 TString FilePath::PathSeparator() {
 263     TString result;
 264     result = PATH_SEPARATOR;
 265     return result;
 266 }
 267 
 268 bool FilePath::CreateDirectory(TString Path, bool ownerOnly) {
 269     bool result = false;
 270 
 271     std::list<TString> paths;
 272     TString lpath = Path;
 273 
 274     while (lpath.empty() == false && DirectoryExists(lpath) == false) {
 275         paths.push_front(lpath);
 276         lpath = ExtractFilePath(lpath);
 277     }
 278 
 279     for (std::list<TString>::iterator iterator = paths.begin(); iterator != paths.end(); iterator++) {
 280         lpath = *iterator;
 281 
 282 #ifdef WINDOWS
 283         if (_wmkdir(lpath.data()) == 0) {
 284 #endif // WINDOWS
 285 #ifdef POSIX
 286         mode_t mode = S_IRWXU;
 287         if (!ownerOnly) {
 288             mode |= S_IRWXG | S_IROTH | S_IXOTH;
 289         }
 290         if (mkdir(StringToFileSystemString(lpath), mode) == 0) {
 291 #endif //POSIX
 292             result = true;
 293         }
 294         else {
 295             result = false;
 296             break;
 297         }
 298     }
 299 
 300     return result;
 301 }
 302 
 303 void FilePath::ChangePermissions(TString FileName, bool ownerOnly) {
 304 #ifdef POSIX
 305     mode_t mode = S_IRWXU;
 306     if (!ownerOnly) {
 307         mode |= S_IRWXG | S_IROTH | S_IXOTH;
 308     }
 309     chmod(FileName.data(), mode);
 310 #endif // POSIX
 311 }
 312 
 313 //--------------------------------------------------------------------------------------------------
 314 
 315 #include <algorithm>
 316 
 317 FileAttributes::FileAttributes(const TString FileName, bool FollowLink) {
 318     FFileName = FileName;
 319     FFollowLink = FollowLink;
 320     ReadAttributes();
 321 }
 322 
 323 bool FileAttributes::WriteAttributes() {
 324     bool result = false;
 325 
 326 #ifdef WINDOWS
 327     DWORD attributes = 0;
 328 
 329     for (std::vector<FileAttribute>::const_iterator iterator = FAttributes.begin();
 330          iterator != FAttributes.end(); iterator++) {
 331         switch (*iterator) {
 332             case faArchive: {
 333                 attributes = attributes & FILE_ATTRIBUTE_ARCHIVE;
 334                 break;
 335             }
 336             case faCompressed: {
 337                 attributes = attributes & FILE_ATTRIBUTE_COMPRESSED;
 338                 break;
 339             }
 340             case faDevice: {
 341                 attributes = attributes & FILE_ATTRIBUTE_DEVICE;
 342                 break;
 343             }
 344             case faDirectory: {
 345                 attributes = attributes & FILE_ATTRIBUTE_DIRECTORY;
 346                 break;
 347             }
 348             case faEncrypted: {
 349                 attributes = attributes & FILE_ATTRIBUTE_ENCRYPTED;
 350                 break;
 351             }
 352             case faHidden: {
 353                 attributes = attributes & FILE_ATTRIBUTE_HIDDEN;
 354                 break;
 355             }
 356 //            case faIntegrityStream: {
 357 //                attributes = attributes & FILE_ATTRIBUTE_INTEGRITY_STREAM;
 358 //                break;
 359 //            }
 360             case faNormal: {
 361                 attributes = attributes & FILE_ATTRIBUTE_NORMAL;
 362                 break;
 363             }
 364             case faNotContentIndexed: {
 365                 attributes = attributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
 366                 break;
 367             }
 368 //            case faNoScrubData: {
 369 //                attributes = attributes & FILE_ATTRIBUTE_NO_SCRUB_DATA;
 370 //                break;
 371 //            }
 372             case faOffline: {
 373                 attributes = attributes & FILE_ATTRIBUTE_OFFLINE;
 374                 break;
 375             }
 376             case faSystem: {
 377                 attributes = attributes & FILE_ATTRIBUTE_SYSTEM;
 378                 break;
 379             }
 380             case faSymbolicLink: {
 381                 attributes = attributes & FILE_ATTRIBUTE_REPARSE_POINT;
 382                 break;
 383             }
 384             case faSparceFile: {
 385                 attributes = attributes & FILE_ATTRIBUTE_SPARSE_FILE;
 386                 break;
 387             }
 388             case faReadOnly: {
 389                 attributes = attributes & FILE_ATTRIBUTE_READONLY;
 390                 break;
 391             }
 392             case faTemporary: {
 393                 attributes = attributes & FILE_ATTRIBUTE_TEMPORARY;
 394                 break;
 395             }
 396             case faVirtual: {
 397                 attributes = attributes & FILE_ATTRIBUTE_VIRTUAL;
 398                 break;
 399             }
 400         }
 401     }
 402 
 403     if (::SetFileAttributes(FFileName.data(), attributes) != 0) {
 404         result = true;
 405     }
 406 #endif // WINDOWS
 407 #ifdef POSIX
 408     mode_t attributes = 0;
 409 
 410     for (std::vector<FileAttribute>::const_iterator iterator = FAttributes.begin();
 411          iterator != FAttributes.end(); iterator++) {
 412         switch (*iterator) {
 413             case faBlockSpecial: {
 414                 attributes |= S_IFBLK;
 415                 break;
 416             }
 417             case faCharacterSpecial: {
 418                 attributes |= S_IFCHR;
 419                 break;
 420             }
 421             case faFIFOSpecial: {
 422                 attributes |= S_IFIFO;
 423                 break;
 424             }
 425             case faNormal: {
 426                 attributes |= S_IFREG;
 427                 break;
 428             }
 429             case faDirectory: {
 430                 attributes |= S_IFDIR;
 431                 break;
 432             }
 433             case faSymbolicLink: {
 434                 attributes |= S_IFLNK;
 435                 break;
 436             }
 437             case faSocket: {
 438                 attributes |= S_IFSOCK;
 439                 break;
 440             }
 441 
 442             // Owner
 443             case faReadOnly: {
 444                 attributes |= S_IRUSR;
 445                 break;
 446             }
 447             case  faWriteOnly: {
 448                 attributes |= S_IWUSR;
 449                 break;
 450             }
 451             case faReadWrite: {
 452                 attributes |= S_IRUSR;
 453                 attributes |= S_IWUSR;
 454                 break;
 455             }
 456             case faExecute: {
 457                 attributes |= S_IXUSR;
 458                 break;
 459             }
 460 
 461             // Group
 462             case faGroupReadOnly: {
 463                 attributes |= S_IRGRP;
 464                 break;
 465             }
 466             case  faGroupWriteOnly: {
 467                 attributes |= S_IWGRP;
 468                 break;
 469             }
 470             case faGroupReadWrite: {
 471                 attributes |= S_IRGRP;
 472                 attributes |= S_IWGRP;
 473                 break;
 474             }
 475             case faGroupExecute: {
 476                 attributes |= S_IXGRP;
 477                 break;
 478             }
 479 
 480             // Others
 481             case faOthersReadOnly: {
 482                 attributes |= S_IROTH;
 483                 break;
 484             }
 485             case  faOthersWriteOnly: {
 486                 attributes |= S_IWOTH;
 487                 break;
 488             }
 489             case faOthersReadWrite: {
 490                 attributes |= S_IROTH;
 491                 attributes |= S_IWOTH;
 492                 break;
 493             }
 494             case faOthersExecute: {
 495                 attributes |= S_IXOTH;
 496                 break;
 497             }
 498         }
 499     }
 500 
 501     if (chmod(FFileName.data(), attributes) == 0) {
 502         result = true;
 503     }
 504 #endif //POSIX
 505 
 506     return result;
 507 }
 508 
 509 #define S_ISRUSR(m)    (((m) & S_IRWXU) == S_IRUSR)
 510 #define S_ISWUSR(m)    (((m) & S_IRWXU) == S_IWUSR)
 511 #define S_ISXUSR(m)    (((m) & S_IRWXU) == S_IXUSR)
 512 
 513 #define S_ISRGRP(m)    (((m) & S_IRWXG) == S_IRGRP)
 514 #define S_ISWGRP(m)    (((m) & S_IRWXG) == S_IWGRP)
 515 #define S_ISXGRP(m)    (((m) & S_IRWXG) == S_IXGRP)
 516 
 517 #define S_ISROTH(m)    (((m) & S_IRWXO) == S_IROTH)
 518 #define S_ISWOTH(m)    (((m) & S_IRWXO) == S_IWOTH)
 519 #define S_ISXOTH(m)    (((m) & S_IRWXO) == S_IXOTH)
 520 
 521 bool FileAttributes::ReadAttributes() {
 522     bool result = false;
 523 
 524 #ifdef WINDOWS
 525     DWORD attributes = ::GetFileAttributes(FFileName.data());
 526 
 527     if (attributes != INVALID_FILE_ATTRIBUTES) {
 528         result = true;
 529 
 530         if (attributes | FILE_ATTRIBUTE_ARCHIVE) { FAttributes.push_back(faArchive); }
 531         if (attributes | FILE_ATTRIBUTE_COMPRESSED) { FAttributes.push_back(faCompressed); }
 532         if (attributes | FILE_ATTRIBUTE_DEVICE) { FAttributes.push_back(faDevice); }
 533         if (attributes | FILE_ATTRIBUTE_DIRECTORY) { FAttributes.push_back(faDirectory); }
 534         if (attributes | FILE_ATTRIBUTE_ENCRYPTED) { FAttributes.push_back(faEncrypted); }
 535         if (attributes | FILE_ATTRIBUTE_HIDDEN) { FAttributes.push_back(faHidden); }
 536         //if (attributes | FILE_ATTRIBUTE_INTEGRITY_STREAM) { FAttributes.push_back(faIntegrityStream); }
 537         if (attributes | FILE_ATTRIBUTE_NORMAL) { FAttributes.push_back(faNormal); }
 538         if (attributes | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) { FAttributes.push_back(faNotContentIndexed); }
 539         //if (attributes | FILE_ATTRIBUTE_NO_SCRUB_DATA) { FAttributes.push_back(faNoScrubData); }
 540         if (attributes | FILE_ATTRIBUTE_SYSTEM) { FAttributes.push_back(faSystem); }
 541         if (attributes | FILE_ATTRIBUTE_OFFLINE) { FAttributes.push_back(faOffline); }
 542         if (attributes | FILE_ATTRIBUTE_REPARSE_POINT) { FAttributes.push_back(faSymbolicLink); }
 543         if (attributes | FILE_ATTRIBUTE_SPARSE_FILE) { FAttributes.push_back(faSparceFile); }
 544         if (attributes | FILE_ATTRIBUTE_READONLY ) { FAttributes.push_back(faReadOnly); }
 545         if (attributes | FILE_ATTRIBUTE_TEMPORARY) { FAttributes.push_back(faTemporary); }
 546         if (attributes | FILE_ATTRIBUTE_VIRTUAL) { FAttributes.push_back(faVirtual); }
 547     }
 548 #endif // WINDOWS
 549 #ifdef POSIX
 550     struct stat status;
 551 
 552     if (stat(StringToFileSystemString(FFileName), &status) == 0) {
 553         result = true;
 554 
 555         if (S_ISBLK(status.st_mode) != 0) { FAttributes.push_back(faBlockSpecial); }
 556         if (S_ISCHR(status.st_mode) != 0) { FAttributes.push_back(faCharacterSpecial); }
 557         if (S_ISFIFO(status.st_mode) != 0) { FAttributes.push_back(faFIFOSpecial); }
 558         if (S_ISREG(status.st_mode) != 0) { FAttributes.push_back(faNormal); }
 559         if (S_ISDIR(status.st_mode) != 0) { FAttributes.push_back(faDirectory); }
 560         if (S_ISLNK(status.st_mode) != 0) { FAttributes.push_back(faSymbolicLink); }
 561         if (S_ISSOCK(status.st_mode) != 0) { FAttributes.push_back(faSocket); }
 562 
 563         // Owner
 564         if (S_ISRUSR(status.st_mode) != 0) {
 565             if (S_ISWUSR(status.st_mode) != 0) { FAttributes.push_back(faReadWrite); }
 566             else { FAttributes.push_back(faReadOnly); }
 567         }
 568         else if (S_ISWUSR(status.st_mode) != 0) { FAttributes.push_back(faWriteOnly); }
 569 
 570         if (S_ISXUSR(status.st_mode) != 0) { FAttributes.push_back(faExecute); }
 571 
 572         // Group
 573         if (S_ISRGRP(status.st_mode) != 0) {
 574             if (S_ISWGRP(status.st_mode) != 0) { FAttributes.push_back(faGroupReadWrite); }
 575             else { FAttributes.push_back(faGroupReadOnly); }
 576         }
 577         else if (S_ISWGRP(status.st_mode) != 0) { FAttributes.push_back(faGroupWriteOnly); }
 578 
 579         if (S_ISXGRP(status.st_mode) != 0) { FAttributes.push_back(faGroupExecute); }
 580 
 581 
 582         // Others
 583         if (S_ISROTH(status.st_mode) != 0) {
 584             if (S_ISWOTH(status.st_mode) != 0) { FAttributes.push_back(faOthersReadWrite); }
 585             else { FAttributes.push_back(faOthersReadOnly); }
 586         }
 587         else if (S_ISWOTH(status.st_mode) != 0) { FAttributes.push_back(faOthersWriteOnly); }
 588 
 589         if (S_ISXOTH(status.st_mode) != 0) { FAttributes.push_back(faOthersExecute); }
 590 
 591         if (FFileName.size() > 0 && FFileName[0] == '.') {
 592             FAttributes.push_back(faHidden);
 593         }
 594     }
 595 #endif //POSIX
 596 
 597     return result;
 598 }
 599 
 600 bool FileAttributes::Valid(const FileAttribute Value) {
 601     bool result = false;
 602 
 603     switch (Value) {
 604 #ifdef WINDOWS
 605         case faHidden:
 606 #endif // WINDOWS
 607 #ifdef POSIX
 608         case faReadWrite:
 609         case faWriteOnly:
 610         case faExecute:
 611 
 612         case faGroupReadWrite:
 613         case faGroupWriteOnly:
 614         case faGroupReadOnly:
 615         case faGroupExecute:
 616 
 617         case faOthersReadWrite:
 618         case faOthersWriteOnly:
 619         case faOthersReadOnly:
 620         case faOthersExecute:
 621 #endif //POSIX
 622 
 623         case faReadOnly: {
 624             result = true;
 625             break;
 626         }
 627     }
 628 
 629     return result;
 630 }
 631 
 632 void FileAttributes::Append(FileAttribute Value) {
 633     if (Valid(Value) == true) {
 634 #ifdef POSIX
 635         if ((Value == faReadOnly && Contains(faWriteOnly) == true) ||
 636             (Value == faWriteOnly && Contains(faReadOnly) == true)) {
 637             Value = faReadWrite;
 638         }
 639 #endif //POSIX
 640 
 641         FAttributes.push_back(Value);
 642         WriteAttributes();
 643     }
 644 }
 645 
 646 bool FileAttributes::Contains(FileAttribute Value) {
 647     bool result = false;
 648 
 649     std::vector<FileAttribute>::const_iterator iterator = std::find(FAttributes.begin(), FAttributes.end(), Value);
 650 
 651     if (iterator != FAttributes.end()) {
 652         result = true;
 653     }
 654 
 655     return result;
 656 }
 657 
 658 void FileAttributes::Remove(FileAttribute Value) {
 659     if (Valid(Value) == true) {
 660 #ifdef POSIX
 661         if (Value == faReadOnly && Contains(faReadWrite) == true) {
 662             Append(faWriteOnly);
 663             Remove(faReadWrite);
 664         }
 665         else if (Value == faWriteOnly && Contains(faReadWrite) == true) {
 666             Append(faReadOnly);
 667             Remove(faReadWrite);
 668         }
 669 #endif //POSIX
 670 
 671         std::vector<FileAttribute>::iterator iterator = std::find(FAttributes.begin(), FAttributes.end(), Value);
 672 
 673         if (iterator != FAttributes.end()) {
 674             FAttributes.erase(iterator);
 675             WriteAttributes();
 676         }
 677     }
 678 }