Skip to content
Snippets Groups Projects
MSwindows.C 29.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*---------------------------------------------------------------------------*\
      =========                 |
      \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
       \\    /   O peration     |
    
    OpenFOAM bot's avatar
    OpenFOAM bot committed
        \\  /    A nd           | www.openfoam.com
    
         \\/     M anipulation  |
    -------------------------------------------------------------------------------
    
    OpenFOAM bot's avatar
    OpenFOAM bot committed
        Copyright (C) 2011-2017 OpenFOAM Foundation
        Copyright (C) 2011 Symscape
    
        Copyright (C) 2016-2021 OpenCFD Ltd.
    
    -------------------------------------------------------------------------------
    License
        This file is part of OpenFOAM.
    
        OpenFOAM is free software: you can redistribute it and/or modify it
        under the terms of the GNU General Public License as published by
        the Free Software Foundation, either version 3 of the License, or
        (at your option) any later version.
    
        OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
        ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
        FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
        for more details.
    
        You should have received a copy of the GNU General Public License
        along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
    
    
    Description
        MS-Windows versions of the functions declared in OSspecific.H
    
    
    \*---------------------------------------------------------------------------*/
    
    #include "OSspecific.H"
    #include "MSwindows.H"
    #include "fileName.H"
    #include "fileStat.H"
    #include "DynamicList.H"
    #include "CStringList.H"
    #include "IOstreams.H"
    #include "Pstream.H"
    #undef DebugInfo    // Windows name clash with OpenFOAM messageStream
    
    #include <cassert>
    #include <cstdlib>
    #include <fstream>
    #include <unordered_map>
    
    // Windows headers
    #define WIN32_LEAN_AND_MEAN
    #include <csignal>
    #include <io.h>     // For _close
    #include <windows.h>
    
    
    
    // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
    
    namespace Foam
    {
        defineTypeNameAndDebug(MSwindows, 0);
    }
    
    
    namespace Foam
    {
        // Don't abort under windows, causes abort dialog to popup.
        // Instead just exit with exitCode.
        static void sigAbortHandler(int exitCode)
        {
            ::exit(exitCode);
        }
    
        static bool installAbortHandler()
        {
            // If it didn't succeed there's not much we can do,
            // so don't check result.
            ::signal(SIGABRT, &sigAbortHandler);
            return true;
        }
    
        static bool const abortHandlerInstalled = installAbortHandler();
    
    
    
        // Move file, overwriting existing
        static bool renameFile(const fileName& src, const fileName& dst)
        {
            constexpr const int flags
            (
                MOVEFILE_COPY_ALLOWED
              | MOVEFILE_REPLACE_EXISTING
              | MOVEFILE_WRITE_THROUGH
            );
    
            // TODO: handle extra-long paths with ::MoveFileExW
    
            return ::MoveFileExA(src.c_str(), dst.c_str(), flags);
        }
    
    
    } // End namespace Foam
    
    
    // * * * * * * * * * * * * * * * * Local Classes * * * * * * * * * * * * * * //
    
    namespace Foam
    {
    namespace MSwindows
    {
    
    //- A simple directory contents iterator
    class directoryIterator
    {
        HANDLE handle_;
    
        bool exists_;
    
        bool hidden_;
    
        std::string item_;
    
        //- Accept file/dir name
        inline bool accept() const
        {
            return
            (
                item_.size() && item_ != "." && item_ != ".."
             && (hidden_ || item_[0] != '.')
            );
        }
    
    
    public:
    
        //- Construct for dirName, optionally allowing hidden files/dirs
        directoryIterator(const fileName& dirName, bool allowHidden = false)
        :
            handle_(INVALID_HANDLE_VALUE),
            exists_(false),
            hidden_(allowHidden),
            item_()
        {
            if (!dirName.empty())
            {
                WIN32_FIND_DATA findData;
    
                handle_ = ::FindFirstFile((dirName/"*").c_str(), &findData);
    
                if (good())
                {
                    exists_ = true;
                    item_ = findData.cFileName;
    
                    // If first element is not acceptable, get another one
                    if (!accept())
                    {
                        next();
                    }
                }
            }
        }
    
    
        //- Destructor
        ~directoryIterator()
        {
            close();
        }
    
    
        // Member Functions
    
            //- Directory existed for opening
            bool exists() const
            {
                return exists_;
            }
    
            //- Directory pointer is valid
            bool good() const
            {
                return (INVALID_HANDLE_VALUE != handle_);
            }
    
            //- Close directory
            void close()
            {
                if (good())
                {
                    ::FindClose(handle_);
                    handle_ = INVALID_HANDLE_VALUE;
                }
            }
    
            //- The current item
            const std::string& val() const
            {
                return item_;
            }
    
            //- Read next item, always ignoring "." and ".." entries.
            //  Normally also ignore hidden files/dirs (beginning with '.')
            //  Automatically close when it runs out of items
            bool next()
            {
                if (good())
                {
                    WIN32_FIND_DATA findData;
    
                    while (::FindNextFile(handle_, &findData))
                    {
                        item_ = findData.cFileName;
    
                        if (accept())
                        {
                            return true;
                        }
                    }
                    close();  // No more items
                }
    
                return false;
            }
    
    
        // Member Operators
    
            //- Same as good()
            operator bool() const
            {
                return good();
            }
    
            //- Same as val()
            const std::string& operator*() const
            {
                return val();
            }
    
            //- Same as next()
            directoryIterator& operator++()
            {
                next();
                return *this;
            }
    };
    
    } // End namespace MSwindows
    } // End namespace Foam
    
    
    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    
    std::string Foam::MSwindows::lastError()
    {
        // Retrieve the system error message for the last-error code
    
        // Based on an example at:
        // http://msdn2.microsoft.com/en-us/library/ms680582(VS.85).aspx
    
        LPVOID lpMsgBuf;
        DWORD dw = GetLastError();
    
        FormatMessage
        (
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM |
            FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL,  // source
            dw,    // message-id
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),  // language-id
            reinterpret_cast<LPTSTR>(&lpMsgBuf),
            0, NULL
        );
    
        const char* fmt = "Error %d: %s";
    
        // Use snprintf with zero to establish the size (without '\0') required
        std::string output;
    
        int n = ::snprintf(nullptr, 0, fmt, static_cast<LPCTSTR>(lpMsgBuf));
    
        if (n > 0)
        {
            output.resize(n+1);
    
            // Print directly into buffer
            n = ::snprintf(&(output[0]), n+1, fmt, static_cast<LPCTSTR>(lpMsgBuf));
            output.resize(n);
        }
    
        LocalFree(lpMsgBuf);
    
        return output;
    }
    
    
    std::string Foam::MSwindows::userName()
    {
        const DWORD bufLen = 256;
        TCHAR buf[bufLen];
        DWORD len = bufLen;
    
        if (::GetUserName(buf, &len))
        {
            return buf;
        }
    
        std::string str;
    
        if
        (
            ERROR_INSUFFICIENT_BUFFER == ::GetLastError()
         && len < 2048
        )
        {
            // The len is with trailing '\0'
            str.resize(len);
    
            // Retrieve directly into buffer
            ::GetUserName(&(str[0]), &len);
    
            // Without trailing '\0'
            str.resize(len-1);
        }
    
        return str;
    }
    
    
    pid_t Foam::pid()
    {
        const DWORD processId = ::GetCurrentProcessId();
        return processId;
    }
    
    
    pid_t Foam::ppid()
    {
        // No equivalent under windows.
    
        if (MSwindows::debug)
        {
            Info<< "ppid not supported under MSwindows" << endl;
        }
    
        return 0;
    }
    
    
    pid_t Foam::pgid()
    {
        // No equivalent under windows.
    
        if (MSwindows::debug)
        {
            Info<< "pgid not supported under MSwindows" << endl;
        }
    
        return 0;
    }
    
    
    
    bool Foam::hasEnv(const std::string& envName)
    
    {
        // An empty envName => always false
        return !envName.empty() &&
            ::GetEnvironmentVariable(envName.c_str(), nullptr, 0);
    }
    
    
    Foam::string Foam::getEnv(const std::string& envName)
    {
        std::string env;
    
    
        auto len = ::GetEnvironmentVariable(envName.c_str(), nullptr, 0);
    
        // len [return] = size with trailing nul char, or zero on failure
    
            env.resize(len);
    
            // len [in] = size with trailing nul char
            // len [return] = size without trailing nul char
            len = ::GetEnvironmentVariable(envName.c_str(), &(env[0]), len);
    
    
            env.resize(len);
            return fileName::validate(env);
        }
    
        return env;
    }
    
    
    bool Foam::setEnv
    (
        const word& envName,
        const std::string& value,
        const bool overwrite
    )
    {
        // Ignore an empty envName => always false
        return
        (
            !envName.empty()
         && ::SetEnvironmentVariable(envName.c_str(), value.c_str())
        );
    }
    
    
    Foam::string Foam::hostName(bool)
    {
        const DWORD bufLen = MAX_COMPUTERNAME_LENGTH + 1;
        TCHAR buf[bufLen];
        DWORD len = bufLen;
    
        return ::GetComputerName(buf, &len) ? buf : string();
    }
    
    
    Foam::string Foam::domainName()
    {
        // Could use ::gethostname and ::gethostbyname like POSIX.C, but would
        // then need to link against ws_32. Prefer to minimize dependencies.
    
        return string::null;
    }
    
    
    Foam::string Foam::userName()
    {
        string name = Foam::getEnv("USERNAME");
    
        if (name.empty())
        {
            name = MSwindows::userName();
        }
    
        return name;
    }
    
    
    bool Foam::isAdministrator()
    {
        // Assume worst case
        return true;
    }
    
    
    Foam::fileName Foam::home()
    {
        fileName env = Foam::getEnv("HOME");
    
        if (env.empty())
        {
            env  = Foam::getEnv("USERPROFILE");
        }
    
        return env;
    }
    
    
    Foam::fileName Foam::home(const std::string& userName)
    {
        return Foam::home();
    }
    
    
    Foam::fileName Foam::cwd()
    {
        string path;
    
        auto len = ::GetCurrentDirectory(0, nullptr);
    
        // len [return] = size with trailing nul char, or zero on failure
    
            // len [in] = size with trailing nul char
            // len [return] = size without trailing nul char
            len = ::GetCurrentDirectory(len, &(path[0]));
    
    481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
    
            path.resize(len);
            return fileName::validate(path);
        }
    
        FatalErrorInFunction
            << "Couldn't get the current working directory"
            << exit(FatalError);
    
        return fileName();
    }
    
    
    Foam::fileName Foam::cwd(bool logical)
    {
        return Foam::cwd();
    }
    
    
    bool Foam::chDir(const fileName& dir)
    {
        // Ignore an empty dir name => always false
        return !dir.empty() && ::SetCurrentDirectory(dir.c_str());;
    }
    
    
    bool Foam::mkDir(const fileName& pathName, const mode_t mode)
    {
        // empty names are meaningless
        if (pathName.empty())
        {
            return false;
        }
    
        bool ok = ::CreateDirectory(pathName.c_str(), NULL);
    
        if (ok)
        {
            Foam::chMod(pathName, mode);
            return true;
        }
    
        const DWORD error = ::GetLastError();
        switch (error)
        {
            case ERROR_ALREADY_EXISTS:
            {
                ok = true;
                break;
            }
    
            case ERROR_PATH_NOT_FOUND:
            {
                // Part of the path does not exist so try to create it
                const fileName& parentName = pathName.path();
    
                if (parentName.size() && mkDir(parentName, mode))
                {
                    ok = mkDir(pathName, mode);
                }
                break;
            }
        }
    
        if (!ok)
        {
            FatalErrorInFunction
                << "Couldn't create directory: " << pathName
                << " " << MSwindows::lastError()
                << exit(FatalError);
        }
    
        return ok;
    }
    
    
    bool Foam::chMod(const fileName& name, const mode_t m)
    {
        // Ignore an empty name => always false
        return !name.empty() && _chmod(name.c_str(), m) == 0;
    }
    
    
    mode_t Foam::mode(const fileName& name, const bool followLink)
    {
        // Ignore an empty name => always 0
        if (!name.empty())
        {
            fileStat fileStatus(name, followLink);
            if (fileStatus.valid())
            {
                return fileStatus.status().st_mode;
            }
        }
    
        return 0;
    }
    
    
    // Windows equivalent to S_ISDIR
    #define ms_isdir(a) \
        ((m != INVALID_FILE_ATTRIBUTES) && (m & FILE_ATTRIBUTE_DIRECTORY))
    
    // Windows equivalent to S_ISREG
    #define ms_isreg(s) \
        ((m != INVALID_FILE_ATTRIBUTES) && !(m & FILE_ATTRIBUTE_DIRECTORY))
    
    
    Foam::fileName::Type Foam::type
    (
        const fileName& name,
        const bool /* followLink */
    )
    {
        // Ignore an empty name => always UNDEFINED
        if (name.empty())
        {
            return fileName::UNDEFINED;
        }
    
        const DWORD m = ::GetFileAttributes(name.c_str());
    
        if (ms_isreg(m))
        {
            return fileName::FILE;
        }
        else if (ms_isdir(m))
        {
            return fileName::DIRECTORY;
        }
    
        return fileName::UNDEFINED;
    }
    
    
    // Local check for gz file
    static bool isGzFile(const std::string& name)
    {
        const DWORD m = ::GetFileAttributes((name + ".gz").c_str());
        return ms_isreg(m);
    }
    
    
    bool Foam::exists
    (
        const fileName& name,
        const bool checkGzip,
        const bool followLink
    )
    {
        // Ignore an empty name => always false
        if (name.empty())
        {
            return false;
        }
    
        const DWORD m = ::GetFileAttributes(name.c_str());
    
        return (ms_isdir(m) || ms_isreg(m) || (checkGzip && isGzFile(name)));
    }
    
    
    bool Foam::isDir(const fileName& name, const bool followLink)
    {
        // Ignore an empty name => always false
        if (name.empty())
        {
            return false;
        }
    
        const DWORD m = ::GetFileAttributes(name.c_str());
    
        return ms_isdir(m);
    }
    
    
    bool Foam::isFile
    (
        const fileName& name,
        const bool checkGzip,
        const bool followLink
    )
    {
        // Ignore an empty name => always false
        if (name.empty())
        {
            return false;
        }
    
        const DWORD m = ::GetFileAttributes(name.c_str());
    
        return (ms_isreg(m) || (!ms_isdir(m) && checkGzip && isGzFile(name)));
    }
    
    
    off_t Foam::fileSize(const fileName& name, const bool followLink)
    {
        // Ignore an empty name
        if (!name.empty())
        {
            fileStat fileStatus(name, followLink);
            if (fileStatus.valid())
            {
                return fileStatus.status().st_size;
            }
        }
    
        return -1;
    }
    
    
    time_t Foam::lastModified(const fileName& name, const bool followLink)
    {
        // Ignore an empty name
        return name.empty() ? 0 : fileStat(name, followLink).modTime();
    }
    
    
    double Foam::highResLastModified(const fileName& name, const bool followLink)
    {
        // Ignore an empty name
        return name.empty() ? 0 : fileStat(name, followLink).dmodTime();
    }
    
    
    Foam::fileNameList Foam::readDir
    (
        const fileName& directory,
        const fileName::Type type,
        const bool filtergz,
        const bool followLink
    )
    {
        // Initial filename list size
        // also used as increment if initial size found to be insufficient
        static constexpr int maxNnames = 100;
    
        // Basic sanity: cannot strip '.gz' from directory names
        const bool stripgz = filtergz && (type != fileName::DIRECTORY);
        const word extgz("gz");
    
        fileNameList dirEntries;
    
        // Iterate contents (ignores an empty directory name)
    
        MSwindows::directoryIterator dirIter(directory);
    
        if (!dirIter.exists())
        {
            if (MSwindows::debug)
            {
                InfoInFunction
                    << "cannot open directory " << directory << endl;
            }
    
            return dirEntries;
        }
    
        if (MSwindows::debug)
        {
            InfoInFunction
                << " : reading directory " << directory << endl;
        }
    
        label nFailed = 0;     // Entries with invalid characters
        label nEntries = 0;    // Number of selected entries
        dirEntries.resize(maxNnames);
    
        // Process all the directory entries
        for (/*nil*/; dirIter; ++dirIter)
        {
            const std::string& item = *dirIter;
    
            // Validate filename without quotes, etc in the name.
            // No duplicate slashes to strip - dirent will not have them anyhow.
    
            const fileName name(fileName::validate(item));
            if (name != item)
            {
                ++nFailed;
            }
            else if
            (
                (type == fileName::DIRECTORY)
             || (type == fileName::FILE && !fileName::isBackup(name))
            )
            {
                if ((directory/name).type() == type)
                {
                    if (nEntries >= dirEntries.size())
                    {
                        dirEntries.resize(dirEntries.size() + maxNnames);
                    }
    
                    if (stripgz && name.hasExt(extgz))
                    {
                        dirEntries[nEntries++] = name.lessExt();
                    }
                    else
                    {
                        dirEntries[nEntries++] = name;
                    }
                }
            }
        }
    
        // Finalize the length of the entries list
        dirEntries.resize(nEntries);
    
        if (nFailed && MSwindows::debug)
        {
            std::cerr
                << "Foam::readDir() : reading directory " << directory << nl
                << nFailed << " entries with invalid characters in their name"
                << std::endl;
        }
    
        return dirEntries;
    }
    
    
    bool Foam::cp(const fileName& src, const fileName& dest, const bool followLink)
    {
        // Make sure source exists - this also handles an empty source name
        if (!exists(src))
        {
            return false;
        }
    
        fileName destFile(dest);
    
        const fileName::Type srcType = src.type(followLink);
    
        // Check type of source file.
        if (srcType == fileName::FILE)
        {
            // If dest is a directory, create the destination file name.
            if (destFile.type() == fileName::DIRECTORY)
            {
                destFile = destFile/src.name();
            }
    
            // Make sure the destination directory exists.
            if (!isDir(destFile.path()) && !mkDir(destFile.path()))
            {
                return false;
            }
    
            // Open and check streams.
            // - use binary mode to avoid any issues
            std::ifstream srcStream(src, ios_base::in | ios_base::binary);
            if (!srcStream)
            {
                return false;
            }
    
            // - use binary mode to avoid any issues
            std::ofstream destStream(destFile, ios_base::out | ios_base::binary);
            if (!destStream)
            {
                return false;
            }
    
            // Copy character data.
            char ch;
            while (srcStream.get(ch))
            {
                destStream.put(ch);
            }
    
            // Final check.
            if (!srcStream.eof() || !destStream)
            {
                return false;
            }
        }
        else if (srcType == fileName::DIRECTORY)
        {
            if (destFile.type() == fileName::DIRECTORY)
            {
    
                // Both are directories. Could mean copy contents or copy
                // recursively.  Don't actually know what the user wants,
                // but assume that if names are identical == copy contents.
                //
                // So: "path1/foo" "path2/foo"  copy contents
                // So: "path1/foo" "path2/bar"  copy directory
    
                const word srcDirName = src.name();
                if (destFile.name() != srcDirName)
                {
                    destFile /= srcDirName;
                }
    
            }
    
            // Make sure the destination directory extists.
            if (!isDir(destFile) && !mkDir(destFile))
            {
                return false;
            }
    
            // Copy files
            fileNameList files = readDir(src, fileName::FILE, false, followLink);
            for (const fileName& item : files)
            {
                if (MSwindows::debug)
                {
                    Info<< "Copying : " << src/item
                        << " to " << destFile/item << endl;
                }
    
                // File to file.
                Foam::cp(src/item, destFile/item);
            }
    
            // Copy sub directories.
            fileNameList dirs = readDir
            (
                src,
                fileName::DIRECTORY,
                false,
                followLink
            );
    
            for (const fileName& item : dirs)
            {
                if (MSwindows::debug)
                {
                    Info<< "Copying : " << src/item
                        << " to " << destFile << endl;
                }
    
                // Dir to Dir.
                Foam::cp(src/item, destFile);
            }
        }
        else
        {
            return false;
        }
    
        return true;
    }
    
    
    bool Foam::ln(const fileName& src, const fileName& dst)
    {
    
        // links are poorly supported, or need administrator privileges.
    
        // Skip for now.
    
        if (MSwindows::debug)
        {
            Info<< "MSwindows does not support ln - softlinking" << endl;
        }
    
        return false;
    }
    
    
    bool Foam::mv(const fileName& src, const fileName& dst, const bool followLink)
    {
        if (MSwindows::debug)
        {
            Info<< "Move : " << src << " to " << dst << endl;
        }
    
        // Ignore an empty names => always false
        if (src.empty() || dst.empty())
        {
            return false;
        }
    
    
        if
        (
            dst.type() == fileName::DIRECTORY
         && src.type(followLink) != fileName::DIRECTORY
        )
        {
            const fileName dstName(dst/src.name());
    
    
            return renameFile(src, dstName);
    
    }
    
    
    bool Foam::mvBak(const fileName& src, const std::string& ext)
    {
        // Ignore an empty name or extension => always false
        if (src.empty() || ext.empty())
        {
            return false;
        }
    
        if (exists(src, false))
        {
    
            constexpr const int maxIndex = 99;
    
            for (int n = 0; n <= maxIndex; ++n)
    
            {
                fileName dstName(src + "." + ext);
                if (n)
                {
                    sprintf(index, "%02d", n);
                    dstName += index;
                }
    
                // avoid overwriting existing files, except for the last
                // possible index where we have no choice
                if (!exists(dstName, false) || n == maxIndex)
                {
    
                    return renameFile(src, dstName);
    
                }
            }
        }
    
        // fall-through: nothing to do
        return false;