1 /*
   2  * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include "FilePath.h"
  27 
  28 #include <algorithm>
  29 #include <list>
  30 
  31 #ifdef WINDOWS
  32 #include <ShellAPI.h>
  33 #endif // WINDOWS
  34 
  35 #ifdef POSIX
  36 #include <sys/stat.h>
  37 #endif // POSIX
  38 
  39 
  40 bool FilePath::FileExists(const TString FileName) {
  41     bool result = false;
  42 #ifdef WINDOWS
  43     WIN32_FIND_DATA FindFileData;
  44     TString fileName = FixPathForPlatform(FileName);
  45     HANDLE handle = FindFirstFile(fileName.data(), &FindFileData);
  46 
  47     if (handle != INVALID_HANDLE_VALUE) {
  48         if (FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes) {
  49             result = true;
  50         }
  51         else {
  52             result = true;
  53         }
  54 
  55         FindClose(handle);
  56     }
  57 #endif // WINDOWS
  58 #ifdef POSIX
  59     struct stat buf;
  60 
  61     if ((stat(StringToFileSystemString(FileName), &buf) == 0) &&
  62             (S_ISREG(buf.st_mode) != 0)) {
  63         result = true;
  64     }
  65 #endif // POSIX
  66     return result;
  67 }
  68 
  69 bool FilePath::DirectoryExists(const TString DirectoryName) {
  70     bool result = false;
  71 #ifdef WINDOWS
  72     WIN32_FIND_DATA FindFileData;
  73     TString directoryName = FixPathForPlatform(DirectoryName);
  74     HANDLE handle = FindFirstFile(directoryName.data(), &FindFileData);
  75 
  76     if (handle != INVALID_HANDLE_VALUE) {
  77         if (FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes) {
  78             result = true;
  79         }
  80 
  81         FindClose(handle);
  82     }
  83 #endif // WINDOWS
  84 #ifdef POSIX
  85     struct stat buf;
  86 
  87     if ((stat(StringToFileSystemString(DirectoryName), &buf) == 0) &&
  88             (S_ISDIR(buf.st_mode) != 0)) {
  89         result = true;
  90     }
  91 #endif // POSIX
  92     return result;
  93 }
  94 
  95 #ifdef WINDOWS
  96 std::string GetLastErrorAsString() {
  97     // Get the error message, if any.
  98     DWORD errorMessageID = ::GetLastError();
  99 
 100     if (errorMessageID == 0) {
 101         return "No error message has been recorded";
 102     }
 103 
 104     LPSTR messageBuffer = NULL;
 105     size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
 106             | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
 107             NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL,
 108             SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
 109 
 110     std::string message(messageBuffer, size);
 111 
 112     // Free the buffer.
 113     LocalFree(messageBuffer);
 114 
 115     return message;
 116 }
 117 #endif // WINDOWS
 118 
 119 bool FilePath::DeleteFile(const TString FileName) {
 120     bool result = false;
 121 
 122     if (FileExists(FileName) == true) {
 123 #ifdef WINDOWS
 124         TString lFileName = FixPathForPlatform(FileName);
 125         FileAttributes attributes(lFileName);
 126 
 127         if (attributes.Contains(faReadOnly) == true) {
 128             attributes.Remove(faReadOnly);
 129         }
 130 
 131         result = ::DeleteFile(lFileName.data()) == TRUE;
 132 #endif // WINDOWS
 133 #ifdef POSIX
 134         if (unlink(StringToFileSystemString(FileName)) == 0) {
 135             result = true;
 136         }
 137 #endif // POSIX
 138     }
 139 
 140     return result;
 141 }
 142 
 143 bool FilePath::DeleteDirectory(const TString DirectoryName) {
 144     bool result = false;
 145 
 146     if (DirectoryExists(DirectoryName) == true) {
 147 #ifdef WINDOWS
 148         SHFILEOPSTRUCTW fos = {0};
 149         TString directoryName = FixPathForPlatform(DirectoryName);
 150         DynamicBuffer<TCHAR> lDirectoryName(directoryName.size() + 2);
 151         if (lDirectoryName.GetData() == NULL) {
 152             return false;
 153         }
 154         memcpy(lDirectoryName.GetData(), directoryName.data(), (directoryName.size() + 2) * sizeof(TCHAR));
 155         lDirectoryName[directoryName.size() + 1] = NULL;
 156         // Double null terminate for SHFileOperation.
 157 
 158         // Delete the folder and everything inside.
 159         fos.wFunc = FO_DELETE;
 160         fos.pFrom = lDirectoryName.GetData();
 161         fos.fFlags = FOF_NO_UI;
 162         result = SHFileOperation(&fos) == 0;
 163 #endif // WINDOWS
 164 #ifdef POSIX
 165         if (unlink(StringToFileSystemString(DirectoryName)) == 0) {
 166             result = true;
 167         }
 168 #endif // POSIX
 169     }
 170 
 171     return result;
 172 }
 173 
 174 TString FilePath::IncludeTrailingSeparator(const TString value) {
 175     TString result = value;
 176 
 177     if (value.size() > 0) {
 178         TString::iterator i = result.end();
 179         i--;
 180 
 181         if (*i != TRAILING_PATHSEPARATOR) {
 182             result += TRAILING_PATHSEPARATOR;
 183         }
 184     }
 185 
 186     return result;
 187 }
 188 
 189 TString FilePath::IncludeTrailingSeparator(const char* value) {
 190     TString lvalue = PlatformString(value).toString();
 191     return IncludeTrailingSeparator(lvalue);
 192 }
 193 
 194 TString FilePath::IncludeTrailingSeparator(const wchar_t* value) {
 195     TString lvalue = PlatformString(value).toString();
 196     return IncludeTrailingSeparator(lvalue);
 197 }
 198 
 199 TString FilePath::ExtractFilePath(TString Path) {
 200 #ifdef WINDOWS
 201     TString result;
 202     size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR);
 203     if (slash != TString::npos)
 204         result = Path.substr(0, slash);
 205     return result;
 206 #endif // WINDOWS
 207 #ifdef POSIX
 208     return dirname(StringToFileSystemString(Path));
 209 #endif // POSIX
 210 }
 211 
 212 TString FilePath::ExtractFileExt(TString Path) {
 213     TString result;
 214     size_t dot = Path.find_last_of('.');
 215 
 216     if (dot != TString::npos) {
 217         result  = Path.substr(dot, Path.size() - dot);
 218     }
 219 
 220     return result;
 221 }
 222 
 223 TString FilePath::ExtractFileName(TString Path) {
 224 #ifdef WINDOWS
 225     TString result;
 226 
 227     size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR);
 228     if (slash != TString::npos)
 229         result = Path.substr(slash + 1, Path.size() - slash - 1);
 230 
 231     return result;
 232 #endif //  WINDOWS
 233 #ifdef POSIX
 234     return basename(StringToFileSystemString(Path));
 235 #endif // POSIX
 236 }
 237 
 238 TString FilePath::ChangeFileExt(TString Path, TString Extension) {
 239     TString result;
 240     size_t dot = Path.find_last_of('.');
 241 
 242     if (dot != TString::npos) {
 243         result = Path.substr(0, dot) + Extension;
 244     }
 245 
 246     if (result.empty() == true) {
 247         result = Path;
 248     }
 249 
 250     return result;
 251 }
 252 
 253 TString FilePath::FixPathForPlatform(TString Path) {
 254     TString result = Path;
 255     std::replace(result.begin(), result.end(),
 256             BAD_TRAILING_PATHSEPARATOR, TRAILING_PATHSEPARATOR);
 257 #ifdef WINDOWS
 258     // The maximum path that does not require long path prefix. On Windows the
 259     // maximum path is 260 minus 1 (NUL) but for directories it is 260 minus
 260     // 12 minus 1 (to allow for the creation of a 8.3 file in the directory).
 261     const int maxPath = 247;
 262     if (result.length() > maxPath &&
 263         result.find(_T("\\\\?\\")) == TString::npos &&
 264         result.find(_T("\\\\?\\UNC")) == TString::npos) {
 265         const TString prefix(_T("\\\\"));
 266         if (!result.compare(0, prefix.size(), prefix)) {
 267             // UNC path, converting to UNC path in long notation
 268             result = _T("\\\\?\\UNC") + result.substr(1, result.length());
 269         } else {
 270             // converting to non-UNC path in long notation
 271             result = _T("\\\\?\\") + result;
 272         }
 273     }
 274 #endif // WINDOWS
 275     return result;
 276 }
 277 
 278 TString FilePath::FixPathSeparatorForPlatform(TString Path) {
 279     TString result = Path;
 280     std::replace(result.begin(), result.end(),
 281             BAD_PATH_SEPARATOR, PATH_SEPARATOR);
 282     return result;
 283 }
 284 
 285 TString FilePath::PathSeparator() {
 286     TString result;
 287     result = PATH_SEPARATOR;
 288     return result;
 289 }
 290 
 291 bool FilePath::CreateDirectory(TString Path, bool ownerOnly) {
 292     bool result = false;
 293 
 294     std::list<TString> paths;
 295     TString lpath = Path;
 296 
 297     while (lpath.empty() == false && DirectoryExists(lpath) == false) {
 298         paths.push_front(lpath);
 299         lpath = ExtractFilePath(lpath);
 300     }
 301 
 302     for (std::list<TString>::iterator iterator = paths.begin();
 303             iterator != paths.end(); iterator++) {
 304         lpath = *iterator;
 305 
 306 #ifdef WINDOWS
 307         if (_wmkdir(lpath.data()) == 0) {
 308 #endif // WINDOWS
 309 #ifdef POSIX
 310         mode_t mode = S_IRWXU;
 311         if (!ownerOnly) {
 312             mode |= S_IRWXG | S_IROTH | S_IXOTH;
 313         }
 314         if (mkdir(StringToFileSystemString(lpath), mode) == 0) {
 315 #endif // POSIX
 316             result = true;
 317         }
 318         else {
 319             result = false;
 320             break;
 321         }
 322     }
 323 
 324     return result;
 325 }
 326 
 327 void FilePath::ChangePermissions(TString FileName, bool ownerOnly) {
 328 #ifdef POSIX
 329     mode_t mode = S_IRWXU;
 330     if (!ownerOnly) {
 331         mode |= S_IRWXG | S_IROTH | S_IXOTH;
 332     }
 333     chmod(FileName.data(), mode);
 334 #endif // POSIX
 335 }
 336 
 337 //----------------------------------------------------------------------------
 338 
 339 #include <algorithm>
 340 
 341 FileAttributes::FileAttributes(const TString FileName, bool FollowLink) {
 342     FFileName = FileName;
 343     FFollowLink = FollowLink;
 344     ReadAttributes();
 345 }
 346 
 347 bool FileAttributes::WriteAttributes() {
 348     bool result = false;
 349 
 350 #ifdef WINDOWS
 351     DWORD attributes = 0;
 352 
 353     for (std::vector<FileAttribute>::const_iterator iterator =
 354             FAttributes.begin();
 355         iterator != FAttributes.end(); iterator++) {
 356         switch (*iterator) {
 357             case faArchive: {
 358                 attributes = attributes & FILE_ATTRIBUTE_ARCHIVE;
 359                 break;
 360             }
 361             case faCompressed: {
 362                 attributes = attributes & FILE_ATTRIBUTE_COMPRESSED;
 363                 break;
 364             }
 365             case faDevice: {
 366                 attributes = attributes & FILE_ATTRIBUTE_DEVICE;
 367                 break;
 368             }
 369             case faDirectory: {
 370                 attributes = attributes & FILE_ATTRIBUTE_DIRECTORY;
 371                 break;
 372             }
 373             case faEncrypted: {
 374                 attributes = attributes & FILE_ATTRIBUTE_ENCRYPTED;
 375                 break;
 376             }
 377             case faHidden: {
 378                 attributes = attributes & FILE_ATTRIBUTE_HIDDEN;
 379                 break;
 380             }
 381             case faNormal: {
 382                 attributes = attributes & FILE_ATTRIBUTE_NORMAL;
 383                 break;
 384             }
 385             case faNotContentIndexed: {
 386                 attributes = attributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
 387                 break;
 388             }
 389             case faOffline: {
 390                 attributes = attributes & FILE_ATTRIBUTE_OFFLINE;
 391                 break;
 392             }
 393             case faSystem: {
 394                 attributes = attributes & FILE_ATTRIBUTE_SYSTEM;
 395                 break;
 396             }
 397             case faSymbolicLink: {
 398                 attributes = attributes & FILE_ATTRIBUTE_REPARSE_POINT;
 399                 break;
 400             }
 401             case faSparceFile: {
 402                 attributes = attributes & FILE_ATTRIBUTE_SPARSE_FILE;
 403                 break;
 404             }
 405             case faReadOnly: {
 406                 attributes = attributes & FILE_ATTRIBUTE_READONLY;
 407                 break;
 408             }
 409             case faTemporary: {
 410                 attributes = attributes & FILE_ATTRIBUTE_TEMPORARY;
 411                 break;
 412             }
 413             case faVirtual: {
 414                 attributes = attributes & FILE_ATTRIBUTE_VIRTUAL;
 415                 break;
 416             }
 417         }
 418     }
 419 
 420     if (::SetFileAttributes(FFileName.data(), attributes) != 0) {
 421         result = true;
 422     }
 423 #endif // WINDOWS
 424 #ifdef POSIX
 425     mode_t attributes = 0;
 426 
 427     for (std::vector<FileAttribute>::const_iterator iterator =
 428             FAttributes.begin();
 429         iterator != FAttributes.end(); iterator++) {
 430         switch (*iterator) {
 431             case faBlockSpecial: {
 432                 attributes |= S_IFBLK;
 433                 break;
 434             }
 435             case faCharacterSpecial: {
 436                 attributes |= S_IFCHR;
 437                 break;
 438             }
 439             case faFIFOSpecial: {
 440                 attributes |= S_IFIFO;
 441                 break;
 442             }
 443             case faNormal: {
 444                 attributes |= S_IFREG;
 445                 break;
 446             }
 447             case faDirectory: {
 448                 attributes |= S_IFDIR;
 449                 break;
 450             }
 451             case faSymbolicLink: {
 452                 attributes |= S_IFLNK;
 453                 break;
 454             }
 455             case faSocket: {
 456                 attributes |= S_IFSOCK;
 457                 break;
 458             }
 459 
 460             // Owner
 461             case faReadOnly: {
 462                 attributes |= S_IRUSR;
 463                 break;
 464             }
 465             case  faWriteOnly: {
 466                 attributes |= S_IWUSR;
 467                 break;
 468             }
 469             case faReadWrite: {
 470                 attributes |= S_IRUSR;
 471                 attributes |= S_IWUSR;
 472                 break;
 473             }
 474             case faExecute: {
 475                 attributes |= S_IXUSR;
 476                 break;
 477             }
 478 
 479             // Group
 480             case faGroupReadOnly: {
 481                 attributes |= S_IRGRP;
 482                 break;
 483             }
 484             case  faGroupWriteOnly: {
 485                 attributes |= S_IWGRP;
 486                 break;
 487             }
 488             case faGroupReadWrite: {
 489                 attributes |= S_IRGRP;
 490                 attributes |= S_IWGRP;
 491                 break;
 492             }
 493             case faGroupExecute: {
 494                 attributes |= S_IXGRP;
 495                 break;
 496             }
 497 
 498             // Others
 499             case faOthersReadOnly: {
 500                 attributes |= S_IROTH;
 501                 break;
 502             }
 503             case  faOthersWriteOnly: {
 504                 attributes |= S_IWOTH;
 505                 break;
 506             }
 507             case faOthersReadWrite: {
 508                 attributes |= S_IROTH;
 509                 attributes |= S_IWOTH;
 510                 break;
 511             }
 512             case faOthersExecute: {
 513                 attributes |= S_IXOTH;
 514                 break;
 515             }
 516             default:
 517                 break;
 518         }
 519     }
 520 
 521     if (chmod(FFileName.data(), attributes) == 0) {
 522         result = true;
 523     }
 524 #endif // POSIX
 525 
 526     return result;
 527 }
 528 
 529 #define S_ISRUSR(m)    (((m) & S_IRWXU) == S_IRUSR)
 530 #define S_ISWUSR(m)    (((m) & S_IRWXU) == S_IWUSR)
 531 #define S_ISXUSR(m)    (((m) & S_IRWXU) == S_IXUSR)
 532 
 533 #define S_ISRGRP(m)    (((m) & S_IRWXG) == S_IRGRP)
 534 #define S_ISWGRP(m)    (((m) & S_IRWXG) == S_IWGRP)
 535 #define S_ISXGRP(m)    (((m) & S_IRWXG) == S_IXGRP)
 536 
 537 #define S_ISROTH(m)    (((m) & S_IRWXO) == S_IROTH)
 538 #define S_ISWOTH(m)    (((m) & S_IRWXO) == S_IWOTH)
 539 #define S_ISXOTH(m)    (((m) & S_IRWXO) == S_IXOTH)
 540 
 541 bool FileAttributes::ReadAttributes() {
 542     bool result = false;
 543 
 544 #ifdef WINDOWS
 545     DWORD attributes = ::GetFileAttributes(FFileName.data());
 546 
 547     if (attributes != INVALID_FILE_ATTRIBUTES) {
 548         result = true;
 549 
 550         if (attributes | FILE_ATTRIBUTE_ARCHIVE) {
 551             FAttributes.push_back(faArchive);
 552         }
 553         if (attributes | FILE_ATTRIBUTE_COMPRESSED) {
 554             FAttributes.push_back(faCompressed);
 555         }
 556         if (attributes | FILE_ATTRIBUTE_DEVICE) {
 557             FAttributes.push_back(faDevice);
 558         }
 559         if (attributes | FILE_ATTRIBUTE_DIRECTORY) {
 560             FAttributes.push_back(faDirectory);
 561         }
 562         if (attributes | FILE_ATTRIBUTE_ENCRYPTED) {
 563             FAttributes.push_back(faEncrypted);
 564         }
 565         if (attributes | FILE_ATTRIBUTE_HIDDEN) {
 566             FAttributes.push_back(faHidden);
 567         }
 568         // if (attributes | FILE_ATTRIBUTE_INTEGRITY_STREAM) {
 569         //     FAttributes.push_back(faIntegrityStream);
 570         // }
 571         if (attributes | FILE_ATTRIBUTE_NORMAL) {
 572             FAttributes.push_back(faNormal);
 573         }
 574         if (attributes | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) {
 575             FAttributes.push_back(faNotContentIndexed);
 576         }
 577         // if (attributes | FILE_ATTRIBUTE_NO_SCRUB_DATA) {
 578         //     FAttributes.push_back(faNoScrubData);
 579         // }
 580         if (attributes | FILE_ATTRIBUTE_SYSTEM) {
 581             FAttributes.push_back(faSystem);
 582         }
 583         if (attributes | FILE_ATTRIBUTE_OFFLINE) {
 584             FAttributes.push_back(faOffline);
 585         }
 586         if (attributes | FILE_ATTRIBUTE_REPARSE_POINT) {
 587             FAttributes.push_back(faSymbolicLink);
 588         }
 589         if (attributes | FILE_ATTRIBUTE_SPARSE_FILE) {
 590             FAttributes.push_back(faSparceFile);
 591         }
 592         if (attributes | FILE_ATTRIBUTE_READONLY ) {
 593             FAttributes.push_back(faReadOnly);
 594         }
 595         if (attributes | FILE_ATTRIBUTE_TEMPORARY) {
 596             FAttributes.push_back(faTemporary);
 597         }
 598         if (attributes | FILE_ATTRIBUTE_VIRTUAL) {
 599             FAttributes.push_back(faVirtual);
 600         }
 601     }
 602 #endif // WINDOWS
 603 #ifdef POSIX
 604     struct stat status;
 605 
 606     if (stat(StringToFileSystemString(FFileName), &status) == 0) {
 607         result = true;
 608 
 609         if (S_ISBLK(status.st_mode) != 0) {
 610             FAttributes.push_back(faBlockSpecial);
 611         }
 612         if (S_ISCHR(status.st_mode) != 0) {
 613             FAttributes.push_back(faCharacterSpecial);
 614         }
 615         if (S_ISFIFO(status.st_mode) != 0) {
 616             FAttributes.push_back(faFIFOSpecial);
 617         }
 618         if (S_ISREG(status.st_mode) != 0) {
 619             FAttributes.push_back(faNormal);
 620         }
 621         if (S_ISDIR(status.st_mode) != 0) {
 622             FAttributes.push_back(faDirectory);
 623         }
 624         if (S_ISLNK(status.st_mode) != 0) {
 625             FAttributes.push_back(faSymbolicLink);
 626         }
 627         if (S_ISSOCK(status.st_mode) != 0) {
 628             FAttributes.push_back(faSocket);
 629         }
 630 
 631         // Owner
 632         if (S_ISRUSR(status.st_mode) != 0) {
 633             if (S_ISWUSR(status.st_mode) != 0) {
 634                 FAttributes.push_back(faReadWrite);
 635             } else {
 636                 FAttributes.push_back(faReadOnly);
 637             }
 638         } else if (S_ISWUSR(status.st_mode) != 0) {
 639             FAttributes.push_back(faWriteOnly);
 640         }
 641 
 642         if (S_ISXUSR(status.st_mode) != 0) {
 643             FAttributes.push_back(faExecute);
 644         }
 645 
 646         // Group
 647         if (S_ISRGRP(status.st_mode) != 0) {
 648             if (S_ISWGRP(status.st_mode) != 0) {
 649                 FAttributes.push_back(faGroupReadWrite);
 650             } else {
 651                 FAttributes.push_back(faGroupReadOnly);
 652             }
 653         } else if (S_ISWGRP(status.st_mode) != 0) {
 654             FAttributes.push_back(faGroupWriteOnly);
 655         }
 656 
 657         if (S_ISXGRP(status.st_mode) != 0) {
 658             FAttributes.push_back(faGroupExecute);
 659         }
 660 
 661 
 662         // Others
 663         if (S_ISROTH(status.st_mode) != 0) {
 664             if (S_ISWOTH(status.st_mode) != 0) {
 665                 FAttributes.push_back(faOthersReadWrite);
 666             } else {
 667                 FAttributes.push_back(faOthersReadOnly);
 668             }
 669         }
 670         else if (S_ISWOTH(status.st_mode) != 0) {
 671             FAttributes.push_back(faOthersWriteOnly);
 672         }
 673 
 674         if (S_ISXOTH(status.st_mode) != 0) {
 675             FAttributes.push_back(faOthersExecute);
 676         }
 677 
 678         if (FFileName.size() > 0 && FFileName[0] == '.') {
 679             FAttributes.push_back(faHidden);
 680         }
 681     }
 682 #endif // POSIX
 683 
 684     return result;
 685 }
 686 
 687 bool FileAttributes::Valid(const FileAttribute Value) {
 688     bool result = false;
 689 
 690     switch (Value) {
 691 #ifdef WINDOWS
 692         case faHidden:
 693 #endif // WINDOWS
 694 #ifdef POSIX
 695         case faReadWrite:
 696         case faWriteOnly:
 697         case faExecute:
 698 
 699         case faGroupReadWrite:
 700         case faGroupWriteOnly:
 701         case faGroupReadOnly:
 702         case faGroupExecute:
 703 
 704         case faOthersReadWrite:
 705         case faOthersWriteOnly:
 706         case faOthersReadOnly:
 707         case faOthersExecute:
 708 #endif // POSIX
 709 
 710         case faReadOnly: {
 711             result = true;
 712             break;
 713         }
 714         default:
 715             break;
 716     }
 717 
 718     return result;
 719 }
 720 
 721 void FileAttributes::Append(FileAttribute Value) {
 722     if (Valid(Value) == true) {
 723 #ifdef POSIX
 724         if ((Value == faReadOnly && Contains(faWriteOnly) == true) ||
 725             (Value == faWriteOnly && Contains(faReadOnly) == true)) {
 726             Value = faReadWrite;
 727         }
 728 #endif // POSIX
 729 
 730         FAttributes.push_back(Value);
 731         WriteAttributes();
 732     }
 733 }
 734 
 735 bool FileAttributes::Contains(FileAttribute Value) {
 736     bool result = false;
 737 
 738     std::vector<FileAttribute>::const_iterator iterator =
 739             std::find(FAttributes.begin(), FAttributes.end(), Value);
 740 
 741     if (iterator != FAttributes.end()) {
 742         result = true;
 743     }
 744 
 745     return result;
 746 }
 747 
 748 void FileAttributes::Remove(FileAttribute Value) {
 749     if (Valid(Value) == true) {
 750 #ifdef POSIX
 751         if (Value == faReadOnly && Contains(faReadWrite) == true) {
 752             Append(faWriteOnly);
 753             Remove(faReadWrite);
 754         }
 755         else if (Value == faWriteOnly && Contains(faReadWrite) == true) {
 756             Append(faReadOnly);
 757             Remove(faReadWrite);
 758         }
 759 #endif // POSIX
 760 
 761         std::vector<FileAttribute>::iterator iterator =
 762             std::find(FAttributes.begin(), FAttributes.end(), Value);
 763 
 764         if (iterator != FAttributes.end()) {
 765             FAttributes.erase(iterator);
 766             WriteAttributes();
 767         }
 768     }
 769 }