/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * $Id: PosixFileMgr.cpp 1805935 2017-08-23 16:09:19Z scantor $
 */

#include <config.h>
#include <stdio.h>

#if HAVE_UNISTD_H
#include <unistd.h>
#endif

#if HAVE_LIMITS_H
#include <limits.h>
#endif

#include <xercesc/util/FileManagers/PosixFileMgr.hpp>

#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/Janitor.hpp>
#include <xercesc/util/RuntimeException.hpp>
#include <xercesc/util/PanicHandler.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/XMLUniDefs.hpp>


XERCES_CPP_NAMESPACE_BEGIN


PosixFileMgr::PosixFileMgr()
{
}


PosixFileMgr::~PosixFileMgr()
{
}


FileHandle
PosixFileMgr::fileOpen(const XMLCh* path, bool toWrite, MemoryManager* const manager)
{
    const char* tmpFileName = XMLString::transcode(path, manager);
    ArrayJanitor<char> janText((char*)tmpFileName, manager);
    return fileOpen(tmpFileName, toWrite, manager);
}


FileHandle
PosixFileMgr::fileOpen(const char* path, bool toWrite, MemoryManager* const /*manager*/)
{
    const char* perms = (toWrite) ? "wb" : "rb";
    FileHandle result = (FileHandle)fopen(path , perms);
    return result;
}


FileHandle
PosixFileMgr::openStdIn(MemoryManager* const /*manager*/)
{
    return (FileHandle)fdopen(dup(0), "rb");
}


void
PosixFileMgr::fileClose(FileHandle f, MemoryManager* const manager)
{
    if (!f)
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);

    if (fclose((FILE*)f))
        ThrowXMLwithMemMgr(XMLPlatformUtilsException,
                 XMLExcepts::File_CouldNotCloseFile, manager);
}


void
PosixFileMgr::fileReset(FileHandle f, MemoryManager* const manager)
{
    if (!f)
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);

    // Seek to the start of the file
    if (fseek((FILE*)f, 0, SEEK_SET))
        ThrowXMLwithMemMgr(XMLPlatformUtilsException,
                 XMLExcepts::File_CouldNotResetFile, manager);
}


XMLFilePos
PosixFileMgr::curPos(FileHandle f, MemoryManager* const manager)
{
    if (!f)
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);
		 
    long curPos = ftell((FILE*)f);
	
    if (curPos == -1)
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotGetSize, manager);

    return (XMLFilePos)curPos;
}


XMLFilePos
PosixFileMgr::fileSize(FileHandle f, MemoryManager* const manager)
{
    if (!f)
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);
		
    // Get the current position
    long curPos = ftell((FILE*)f);
    if (curPos == -1)
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotGetCurPos, manager);

    // Seek to the end and save that value for return
    if (fseek((FILE*)f, 0, SEEK_END))
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotSeekToEnd, manager);

    long retVal = ftell((FILE*)f);
    if (retVal == -1)
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotSeekToEnd, manager);

    // And put the pointer back
    if (fseek((FILE*)f, curPos, SEEK_SET))
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotSeekToPos, manager);

    return (XMLFilePos)retVal;
}


XMLSize_t
PosixFileMgr::fileRead(FileHandle f, XMLSize_t byteCount, XMLByte* buffer, MemoryManager* const manager)
{
    if (!f || !buffer)
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);
		
    XMLSize_t bytesRead = 0;
    if (byteCount > 0)
    {
        bytesRead = fread((void*)buffer, 1, byteCount, (FILE*)f);

        if (ferror((FILE*)f))
            ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotReadFromFile, manager);
    }
	
    return bytesRead;
}


void
PosixFileMgr::fileWrite(FileHandle f, XMLSize_t byteCount, const XMLByte* buffer, MemoryManager* const manager)
{
    if (!f || !buffer)
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);

    while (byteCount > 0)
    {
        XMLSize_t bytesWritten = fwrite(buffer, sizeof(XMLByte), byteCount, (FILE*)f);

        if (ferror((FILE*)f))
            ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotWriteToFile, manager);

        buffer		+= bytesWritten;
        byteCount	-= bytesWritten;
    }
}


XMLCh*
PosixFileMgr::getFullPath(const XMLCh* const srcPath, MemoryManager* const manager)
{
    //
    //  NOTE: The path provided has always already been opened successfully,
    //  so we know that its not some pathological freaky path. It comes in
    //  in native format, and goes out as Unicode always
    //
    char* newSrc = XMLString::transcode(srcPath, manager);
    ArrayJanitor<char> janText(newSrc, manager);

#if HAVE_PATH_MAX
    // Use a local buffer that is big enough for the largest legal path
    char absPath[PATH_MAX + 1];
    
    // get the absolute path
    if (!realpath(newSrc, absPath))
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotGetBasePathName, manager);

    XMLCh* ret = XMLString::transcode(absPath, manager);
#else
    // get the absolute path
    char *absPath = realpath(newSrc, NULL);
    if(!absPath)
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotGetBasePathName, manager);

    XMLCh* ret = XMLString::transcode(absPath, manager);
    free(absPath);
#endif
    return ret;
}


XMLCh*
PosixFileMgr::getCurrentDirectory(MemoryManager* const manager)
{
#if HAVE_PATH_MAX
    char dirBuf[PATH_MAX + 2];
    char *curDir = getcwd(&dirBuf[0], PATH_MAX + 1);
#else
    char *curDir = getcwd(NULL, 0);
#endif

    if (!curDir)
        ThrowXMLwithMemMgr(XMLPlatformUtilsException,
                 XMLExcepts::File_CouldNotGetBasePathName, manager);

    XMLCh* ret = XMLString::transcode(curDir, manager);
#if !HAVE_PATH_MAX
    free(curDir);
#endif
    return ret;
}


bool
PosixFileMgr::isRelative(const XMLCh* const toCheck, MemoryManager* const /*manager*/)
{
    // Check for pathological case of empty path
    if (!toCheck || !toCheck[0])
        return false;

    //
    //  If it starts with a slash, then it cannot be relative.
    //
    return toCheck[0] != chForwardSlash;
}


XERCES_CPP_NAMESPACE_END

