Example #1
0
    bool isTgaType(BasicIo& iIo, bool /*advance*/)
    {
        // not all TARGA files have a signature string, so first just try to match the file name extension
        std::string path = iIo.path();
        if(path.rfind(".tga") != std::string::npos || path.rfind(".TGA") != std::string::npos)
        {
            return true;
        }

        byte buf[26];
        long curPos = iIo.tell();
        iIo.seek(-26, BasicIo::end);
        if (iIo.error() || iIo.eof())
        {
            return false;
        }
        iIo.read(buf, sizeof(buf));
        if (iIo.error())
        {
            return false;
        }
        // some TARGA files, but not all, have a signature string at the end
        bool matched = (memcmp(buf + 8, "TRUEVISION-XFILE", 16) == 0);
        iIo.seek(curPos, BasicIo::beg);
        return matched;
    }
Example #2
0
 void MemIo::transfer(BasicIo& src)
 {
     MemIo *memIo = dynamic_cast<MemIo*>(&src);
     if (memIo) {
         // Optimization if src is another instance of MemIo
         if (true == isMalloced_) {
             std::free(data_);
         }
         idx_ = 0;
         data_ = memIo->data_;
         size_ = memIo->size_;
         isMalloced_ = memIo->isMalloced_;
         memIo->idx_ = 0;
         memIo->data_ = 0;
         memIo->size_ = 0;
         memIo->isMalloced_ = false;
     }
     else {
         // Generic reopen to reset position to start
         if (src.open() != 0) {
             throw Error(9, src.path(), strError());
         }
         idx_ = 0;
         write(src);
         src.close();
     }
     if (error() || src.error()) throw Error(19, strError());
 }
Example #3
0
    void FileIo::transfer(BasicIo& src)
    {
        const bool wasOpen = (fp_ != 0);
        const std::string lastMode(openMode_);

        FileIo *fileIo = dynamic_cast<FileIo*>(&src);
        if (fileIo) {
            // Optimization if src is another instance of FileIo
            fileIo->close();
            // Check if the file can be written to, if it already exists
            if (open("w+b") != 0) {
                // Remove the (temporary) file
                std::remove(fileIo->path_.c_str());
                throw Error(10, path_, "w+b", strError());
            }
            close();
            struct stat buf;
            if (::stat(path_.c_str(), &buf) == -1) {
                throw Error(2, path_, strError(), "stat");
            }
            // MSVCRT rename that does not overwrite existing files
            if (fileExists(path_) && std::remove(path_.c_str()) != 0) {
                throw Error(2, path_, strError(), "std::remove");
            }
            if (std::rename(fileIo->path_.c_str(), path_.c_str()) == -1) {
                throw Error(17, fileIo->path_, path_, strError());
            }
            std::remove(fileIo->path_.c_str());
            // Set original file permissions
            if (::chmod(path_.c_str(), buf.st_mode) == -1) {
                throw Error(2, fileIo->path_, strError(), "chmod");
            }
        }
        else {
            // Generic handling, reopen both to reset to start
            if (open("w+b") != 0) {
                throw Error(10, path_, "w+b", strError());
            }
            if (src.open() != 0) {
                throw Error(9, src.path(), strError());
            }
            write(src);
            src.close();
        }

        if (wasOpen) {
            if (open(lastMode) != 0) {
                throw Error(10, path_, lastMode, strError());
            }
        }
        else close();

        if (error() || src.error()) throw Error(18, path_, strError());
    }
Example #4
0
    void PgfImage::doWriteMetadata(BasicIo& outIo)
    {
        if (!io_->isopen()) throw Error(20);
        if (!outIo.isopen()) throw Error(21);

#ifdef DEBUG
        std::cout << "Exiv2::PgfImage::doWriteMetadata: Writing PGF file " << io_->path() << "\n";
        std::cout << "Exiv2::PgfImage::doWriteMetadata: tmp file created " << outIo.path() << "\n";
#endif

        // Ensure that this is the correct image type
        if (!isPgfType(*io_, true))
        {
            if (io_->error() || io_->eof()) throw Error(20);
            throw Error(22);
        }

        // Ensure PGF version.
        byte mnb            = readPgfMagicNumber(*io_);

        readPgfHeaderSize(*io_);

        int w, h;
        DataBuf header      = readPgfHeaderStructure(*io_, w, h);

        Image::AutoPtr img  = ImageFactory::create(ImageType::png);

        img->setExifData(exifData_);
        img->setIptcData(iptcData_);
        img->setXmpData(xmpData_);
        img->writeMetadata();
        int     imgSize  = img->io().size();
        DataBuf imgBuf   = img->io().read(imgSize);

#ifdef DEBUG
        std::cout << "Exiv2::PgfImage::doWriteMetadata: Creating image to host metadata (" << imgSize << " bytes)\n";
#endif

        //---------------------------------------------------------------

        // Write PGF Signature.
        if (outIo.write(pgfSignature, 3) != 3) throw Error(21);

        // Write Magic number.
        if (outIo.putb(mnb) == EOF) throw Error(21);

        // Write new Header size.
        uint32_t newHeaderSize = header.size_ + imgSize;
        DataBuf buffer(4);
        memcpy (buffer.pData_, &newHeaderSize, 4);
        byteSwap_(buffer,0,bSwap_);
        if (outIo.write(buffer.pData_, 4) != 4) throw Error(21);

#ifdef DEBUG
        std::cout << "Exiv2::PgfImage: new PGF header size : " << newHeaderSize << " bytes\n";

        printf("%x\n", buffer.pData_[0]);
        printf("%x\n", buffer.pData_[1]);
        printf("%x\n", buffer.pData_[2]);
        printf("%x\n", buffer.pData_[3]);
#endif

        // Write Header data.
        if (outIo.write(header.pData_, header.size_) != header.size_) throw Error(21);

        // Write new metadata byte array.
        if (outIo.write(imgBuf.pData_, imgBuf.size_) != imgBuf.size_) throw Error(21);

        // Copy the rest of PGF image data.

        DataBuf buf(4096);
        long readSize = 0;
        while ((readSize=io_->read(buf.pData_, buf.size_)))
        {
            if (outIo.write(buf.pData_, readSize) != readSize) throw Error(21);
        }
        if (outIo.error()) throw Error(21);

    } // PgfImage::doWriteMetadata
Example #5
0
    void Jp2Image::doWriteMetadata(BasicIo& outIo)
    {
        if (!io_->isopen()) throw Error(20);
        if (!outIo.isopen()) throw Error(21);

#ifdef DEBUG
        std::cout << "Exiv2::Jp2Image::doWriteMetadata: Writing JPEG-2000 file " << io_->path() << "\n";
        std::cout << "Exiv2::Jp2Image::doWriteMetadata: tmp file created " << outIo.path() << "\n";
#endif

        // Ensure that this is the correct image type
        if (!isJp2Type(*io_, true))
        {
            if (io_->error() || io_->eof()) throw Error(20);
            throw Error(22);
        }

        // Write JPEG2000 Signature.
        if (outIo.write(Jp2Signature, 12) != 12) throw Error(21);

        Jp2BoxHeader box = {0,0};

        byte    boxDataSize[4];
        byte    boxUUIDtype[4];
        DataBuf bheaderBuf(8);     // Box header : 4 bytes (data size) + 4 bytes (box type).

        // FIXME: Andreas, why the loop do not stop when EOF is taken from _io. The loop go out by an exception
        // generated by a zero size data read.

        while(io_->tell() < io_->size())
        {
#ifdef DEBUG
            std::cout << "Exiv2::Jp2Image::doWriteMetadata: Position: " << io_->tell() << " / " << io_->size() << "\n";
#endif

            // Read chunk header.

            std::memset(bheaderBuf.pData_, 0x00, bheaderBuf.size_);
            long bufRead = io_->read(bheaderBuf.pData_, bheaderBuf.size_);
            if (io_->error()) throw Error(14);
            if (bufRead != bheaderBuf.size_) throw Error(20);

            // Decode box header.

            box.boxLength = getLong(bheaderBuf.pData_,     bigEndian);
            box.boxType   = getLong(bheaderBuf.pData_ + 4, bigEndian);

#ifdef DEBUG
            std::cout << "Exiv2::Jp2Image::doWriteMetadata: Find box type: " << bheaderBuf.pData_ + 4
                      << " lenght: " << box.boxLength << "\n";
#endif

            if (box.boxLength == 0)
            {
#ifdef DEBUG
                std::cout << "Exiv2::Jp2Image::doWriteMetadata: Null Box size has been found. "
                             "This is the last box of file.\n";
#endif
                box.boxLength = io_->size() - io_->tell() + 8;
            }
            if (box.boxLength == 1)
            {
                // FIXME. Special case. the real box size is given in another place.
            }

            // Read whole box : Box header + Box data (not fixed size - can be null).

            DataBuf boxBuf(box.boxLength);                             // Box header (8 bytes) + box data.
            memcpy(boxBuf.pData_, bheaderBuf.pData_, 8);               // Copy header.
            bufRead = io_->read(boxBuf.pData_ + 8, box.boxLength - 8); // Extract box data.
            if (io_->error())
            {
#ifdef DEBUG
                std::cout << "Exiv2::Jp2Image::doWriteMetadata: Error reading source file\n";
#endif

                throw Error(14);
            }

            if (bufRead != (long)(box.boxLength - 8))
            {
#ifdef DEBUG
                std::cout << "Exiv2::Jp2Image::doWriteMetadata: Cannot read source file data\n";
#endif
                throw Error(20);
            }

            switch(box.boxType)
            {
                case kJp2BoxTypeJp2Header:
                {

#ifdef DEBUG
                    std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write JP2Header box (lenght: " << box.boxLength << ")\n";
#endif
                    if (outIo.write(boxBuf.pData_, boxBuf.size_) != boxBuf.size_) throw Error(21);

                    // Write all updated metadata here, just after JP2Header.

                    if (exifData_.count() > 0)
                    {
                        // Update Exif data to a new UUID box

                        Blob blob;
                        ExifParser::encode(blob, littleEndian, exifData_);
                        if (blob.size())
                        {
                            const unsigned char ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00};

                            DataBuf rawExif(static_cast<long>(sizeof(ExifHeader) + blob.size()));
                            memcpy(rawExif.pData_, ExifHeader, sizeof(ExifHeader));
                            memcpy(rawExif.pData_ + sizeof(ExifHeader), &blob[0], blob.size());

                            DataBuf boxData(8 + 16 + rawExif.size_);
                            ul2Data(boxDataSize, boxData.size_, Exiv2::bigEndian);
                            ul2Data(boxUUIDtype, kJp2BoxTypeUuid, Exiv2::bigEndian);
                            memcpy(boxData.pData_,          boxDataSize,    4);
                            memcpy(boxData.pData_ + 4,      boxUUIDtype,    4);
                            memcpy(boxData.pData_ + 8,      kJp2UuidExif,   16);
                            memcpy(boxData.pData_ + 8 + 16, rawExif.pData_, rawExif.size_);

#ifdef DEBUG
                            std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write box with Exif metadata (lenght: "
                                      << boxData.size_ << ")\n";
#endif
                            if (outIo.write(boxData.pData_, boxData.size_) != boxData.size_) throw Error(21);
                        }
                    }

                    if (iptcData_.count() > 0)
                    {
                        // Update Iptc data to a new UUID box

                        DataBuf rawIptc = IptcParser::encode(iptcData_);
                        if (rawIptc.size_ > 0)
                        {
                            DataBuf boxData(8 + 16 + rawIptc.size_);
                            ul2Data(boxDataSize, boxData.size_, Exiv2::bigEndian);
                            ul2Data(boxUUIDtype, kJp2BoxTypeUuid, Exiv2::bigEndian);
                            memcpy(boxData.pData_,          boxDataSize,    4);
                            memcpy(boxData.pData_ + 4,      boxUUIDtype,    4);
                            memcpy(boxData.pData_ + 8,      kJp2UuidIptc,   16);
                            memcpy(boxData.pData_ + 8 + 16, rawIptc.pData_, rawIptc.size_);

#ifdef DEBUG
                            std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write box with Iptc metadata (lenght: "
                                      << boxData.size_ << ")\n";
#endif
                            if (outIo.write(boxData.pData_, boxData.size_) != boxData.size_) throw Error(21);
                        }
                    }

                    if (writeXmpFromPacket() == false)
                    {
                        if (XmpParser::encode(xmpPacket_, xmpData_) > 1)
                        {
#ifndef SUPPRESS_WARNINGS
                            EXV_ERROR << "Failed to encode XMP metadata.\n";
#endif
                        }
                    }
                    if (xmpPacket_.size() > 0)
                    {
                        // Update Xmp data to a new UUID box

                        DataBuf xmp(reinterpret_cast<const byte*>(xmpPacket_.data()), static_cast<long>(xmpPacket_.size()));
                        DataBuf boxData(8 + 16 + xmp.size_);
                        ul2Data(boxDataSize, boxData.size_, Exiv2::bigEndian);
                        ul2Data(boxUUIDtype, kJp2BoxTypeUuid, Exiv2::bigEndian);
                        memcpy(boxData.pData_,          boxDataSize,  4);
                        memcpy(boxData.pData_ + 4,      boxUUIDtype,  4);
                        memcpy(boxData.pData_ + 8,      kJp2UuidXmp,  16);
                        memcpy(boxData.pData_ + 8 + 16, xmp.pData_,   xmp.size_);

#ifdef DEBUG
                        std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write box with XMP metadata (lenght: "
                                  << boxData.size_ << ")\n";
#endif
                        if (outIo.write(boxData.pData_, boxData.size_) != boxData.size_) throw Error(21);
                    }

                    break;
                }

                case kJp2BoxTypeUuid:
                {
                    if(memcmp(boxBuf.pData_ + 8, kJp2UuidExif, sizeof(16)) == 0)
                    {
#ifdef DEBUG
                        std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Exif Uuid box\n";
#endif
                    }
                    else if(memcmp(boxBuf.pData_ + 8, kJp2UuidIptc, sizeof(16)) == 0)
                    {
#ifdef DEBUG
                        std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Iptc Uuid box\n";
#endif
                    }
                    else if(memcmp(boxBuf.pData_ + 8, kJp2UuidXmp,  sizeof(16)) == 0)
                    {
#ifdef DEBUG
                        std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Xmp Uuid box\n";
#endif
                    }
                    else
                    {
#ifdef DEBUG
                        std::cout << "Exiv2::Jp2Image::doWriteMetadata: write Uuid box (lenght: " << box.boxLength << ")\n";
#endif
                        if (outIo.write(boxBuf.pData_, boxBuf.size_) != boxBuf.size_) throw Error(21);
                    }
                    break;
                }

                default:
                {
#ifdef DEBUG
                    std::cout << "Exiv2::Jp2Image::doWriteMetadata: write box (lenght: " << box.boxLength << ")\n";
#endif
                    if (outIo.write(boxBuf.pData_, boxBuf.size_) != boxBuf.size_) throw Error(21);

                    break;
                }
            }
        }

#ifdef DEBUG
        std::cout << "Exiv2::Jp2Image::doWriteMetadata: EOF\n";
#endif

    } // Jp2Image::doWriteMetadata
Example #6
0
    void PngImage::doWriteMetadata(BasicIo& outIo)
    {
        if (!io_->isopen()) throw Error(20);
        if (!outIo.isopen()) throw Error(21);

#ifdef DEBUG
        std::cout << "Exiv2::PngImage::doWriteMetadata: Writing PNG file " << io_->path() << "\n";
        std::cout << "Exiv2::PngImage::doWriteMetadata: tmp file created " << outIo.path() << "\n";
#endif

        // Ensure that this is the correct image type
        if (!isPngType(*io_, true))
        {
            if (io_->error() || io_->eof()) throw Error(20);
            throw Error(22);
        }

        // Write PNG Signature.
        if (outIo.write(pngSignature, 8) != 8) throw Error(21);

        DataBuf cheaderBuf(8);       // Chunk header : 4 bytes (data size) + 4 bytes (chunk type).

        while(!io_->eof())
        {
            // Read chunk header.

            std::memset(cheaderBuf.pData_, 0x00, cheaderBuf.size_);
            long bufRead = io_->read(cheaderBuf.pData_, cheaderBuf.size_);
            if (io_->error()) throw Error(14);
            if (bufRead != cheaderBuf.size_) throw Error(20);

            // Decode chunk data length.

            uint32_t dataOffset = getULong(cheaderBuf.pData_, bigEndian);
            if (dataOffset > 0x7FFFFFFF) throw Exiv2::Error(14);

            // Read whole chunk : Chunk header + Chunk data (not fixed size - can be null) + CRC (4 bytes).

            DataBuf chunkBuf(8 + dataOffset + 4);                     // Chunk header (8 bytes) + Chunk data + CRC (4 bytes).
            memcpy(chunkBuf.pData_, cheaderBuf.pData_, 8);            // Copy header.
            bufRead = io_->read(chunkBuf.pData_ + 8, dataOffset + 4); // Extract chunk data + CRC
            if (io_->error()) throw Error(14);
            if (bufRead != (long)(dataOffset + 4)) throw Error(20);

            if (!memcmp(cheaderBuf.pData_ + 4, "IEND", 4))
            {
                // Last chunk found: we write it and done.
#ifdef DEBUG
                std::cout << "Exiv2::PngImage::doWriteMetadata: Write IEND chunk (lenght: " << dataOffset << ")\n";
#endif
                if (outIo.write(chunkBuf.pData_, chunkBuf.size_) != chunkBuf.size_) throw Error(21);
                return;
            }
            else if (!memcmp(cheaderBuf.pData_ + 4, "IHDR", 4))
            {
#ifdef DEBUG
                std::cout << "Exiv2::PngImage::doWriteMetadata: Write IHDR chunk (lenght: " << dataOffset << ")\n";
#endif
                if (outIo.write(chunkBuf.pData_, chunkBuf.size_) != chunkBuf.size_) throw Error(21);

                // Write all updated metadata here, just after IHDR.

                if (!comment_.empty())
                {
                    // Update Comment data to a new compressed iTXt PNG chunk

                    DataBuf com(reinterpret_cast<const byte*>(comment_.data()), static_cast<long>(comment_.size()));
                    DataBuf chunkData = PngChunk::makeMetadataChunk(com, PngChunk::comment_Data, true);

#ifdef DEBUG
                    std::cout << "Exiv2::PngImage::doWriteMetadata: Write chunk with Comment metadata (lenght: "
                              << chunkData.size_ << ")\n";
#endif
                    if (outIo.write(chunkData.pData_, chunkData.size_) != chunkData.size_) throw Error(21);
                }

                if (exifData_.count() > 0)
                {
                    // Update Exif data to a new zTXt PNG chunk

                    Blob blob;
                    ExifParser::encode(blob, littleEndian, exifData_);
                    if (blob.size())
                    {
                        const unsigned char ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00};

                        DataBuf rawExif(sizeof(ExifHeader) + blob.size());
                        memcpy(rawExif.pData_, ExifHeader, sizeof(ExifHeader));
                        memcpy(rawExif.pData_ + sizeof(ExifHeader), &blob[0], blob.size());
                        DataBuf chunkData = PngChunk::makeMetadataChunk(rawExif, PngChunk::exif_Data, true);

#ifdef DEBUG
                        std::cout << "Exiv2::PngImage::doWriteMetadata: Write chunk with Exif metadata (lenght: "
                                  << chunkData.size_ << ")\n";
#endif
                        if (outIo.write(chunkData.pData_, chunkData.size_) != chunkData.size_) throw Error(21);
                    }
                }

                if (iptcData_.count() > 0)
                {
                    // Update Iptc data to a new zTXt PNG chunk

                    DataBuf rawIptc = IptcParser::encode(iptcData_);
                    if (rawIptc.size_ > 0)
                    {
                        DataBuf chunkData = PngChunk::makeMetadataChunk(rawIptc, PngChunk::iptc_Data, true);

#ifdef DEBUG
                        std::cout << "Exiv2::PngImage::doWriteMetadata: Write chunk with Iptc metadata (lenght: "
                                  << chunkData.size_ << ")\n";
#endif
                        if (outIo.write(chunkData.pData_, chunkData.size_) != chunkData.size_) throw Error(21);
                    }
                }

                if (writeXmpFromPacket() == false)
                {
                    if (XmpParser::encode(xmpPacket_, xmpData_))
                    {
#ifndef SUPPRESS_WARNINGS
                        std::cerr << "Error: Failed to encode XMP metadata.\n";
#endif
                    }
                }
                if (xmpPacket_.size() > 0)
                {
                    // Update Xmp data to a new uncompressed iTXt PNG chunk
                    // Note than XMP spec. Ver September 2005, page 97 require an uncompressed chunk to host XMP data

                    DataBuf xmp(reinterpret_cast<const byte*>(xmpPacket_.data()), static_cast<long>(xmpPacket_.size()));
                    DataBuf chunkData = PngChunk::makeMetadataChunk(xmp, PngChunk::xmp_Data, false);

#ifdef DEBUG
                    std::cout << "Exiv2::PngImage::doWriteMetadata: Write chunk with XMP metadata (lenght: "
                              << chunkData.size_ << ")\n";
#endif
                    if (outIo.write(chunkData.pData_, chunkData.size_) != chunkData.size_) throw Error(21);
                }
            }
            else if (!memcmp(cheaderBuf.pData_ + 4, "tEXt", 4) ||
                     !memcmp(cheaderBuf.pData_ + 4, "zTXt", 4) ||
                     !memcmp(cheaderBuf.pData_ + 4, "iTXt", 4))
            {
                DataBuf key = PngChunk::keyTXTChunk(chunkBuf, true);
                if (memcmp("Raw profile type exif", key.pData_, 21) == 0 ||
                    memcmp("Raw profile type APP1", key.pData_, 21) == 0 ||
                    memcmp("Raw profile type iptc", key.pData_, 21) == 0 ||
                    memcmp("Raw profile type xmp",  key.pData_, 20) == 0 ||
                    memcmp("XML:com.adobe.xmp",     key.pData_, 17) == 0 ||
                    memcmp("Description",           key.pData_, 11) == 0)
                {
#ifdef DEBUG
                    std::cout << "Exiv2::PngImage::doWriteMetadata: strip " << cheaderBuf.pData_ + 4
                              << " chunk (key: " << key.pData_ << ")\n";
#endif
                }
                else
                {
#ifdef DEBUG
                    std::cout << "Exiv2::PngImage::doWriteMetadata: write " << cheaderBuf.pData_ + 4
                              << " chunk (lenght: " << dataOffset << ")\n";
#endif
                    if (outIo.write(chunkBuf.pData_, chunkBuf.size_) != chunkBuf.size_) throw Error(21);
                }
            }
            else
            {
                // Write all others chunk as well.
#ifdef DEBUG
                std::cout << "Exiv2::PngImage::doWriteMetadata: write " << cheaderBuf.pData_ + 4
                          << " chunk (lenght: " << dataOffset << ")\n";
#endif
                if (outIo.write(chunkBuf.pData_, chunkBuf.size_) != chunkBuf.size_) throw Error(21);

            }
        }

    } // PngImage::doWriteMetadata
Example #7
0
int WriteReadSeek(BasicIo &io)
{
    byte buf[4096];
    const char tester1[] = "this is a little test of MemIo";
    const char tester2[] = "Appending this on the end";
    const char expect[] = "this is a little teAppending this on the end";
    const long insert = 19;
    const long len1 = (long)std::strlen(tester1) + 1;
    const long len2 = (long)std::strlen(tester2) + 1;

    if (io.open() != 0) {
        throw Error(9, io.path(), strError());
    }
    IoCloser closer(io);
    if (io.write((byte*)tester1, len1) != len1) {
        std::cerr << ": WRS initial write failed\n";
        return 2;
    }

    if (io.size() != len1) {
        std::cerr << ": WRS size is not " << len1 << "\n";
        return 2;
    }

    io.seek(-len1, BasicIo::cur);

    int c = EOF;
    std::memset(buf, -1, sizeof(buf));
    for (int i = 0; (c=io.getb()) != EOF; ++i) {
        buf[i] = (byte)c;
    }

    // Make sure we got the null back
    if(buf[len1-1] != 0) {
        std::cerr << ": WRS missing null terminator 1\n";
        return 3;
    }

    if (strcmp(tester1, (char*)buf) != 0 ) {
        std::cerr << ": WRS strings don't match 1\n";
        return 4;
    }

    io.seek(-2, BasicIo::end);
    if (io.getb() != 'o') {
        std::cerr << ": WRS bad getb o\n";
        return 5;
    }

    io.seek(-2, BasicIo::cur);
    if (io.getb() != 'I') {
        std::cerr << ": WRS bad getb I\n";
        return 6;
    }

    if (io.putb('O') != 'O') {
        std::cerr << ": WRS bad putb\n";
        return 7;
    }

    io.seek(-1, BasicIo::cur);
    if (io.getb() != 'O') {
        std::cerr << ": WRS bad getb O\n";
        return 8;
    }

    io.seek(insert, BasicIo::beg);
    if(io.write((byte*)tester2, len2) != len2) {
        std::cerr << ": WRS bad write 1\n";
        return 9;
    }

    // open should seek to beginning
    if (io.open() != 0)  {
        throw Error(9, io.path(), strError());
    }
    std::memset(buf, -1, sizeof(buf));
    if (io.read(buf, sizeof(buf)) != insert + len2) {
        std::cerr << ": WRS something went wrong\n";
        return 10;
    }

    // Make sure we got the null back
    if(buf[insert + len2 - 1] != 0) {
        std::cerr << ": WRS missing null terminator 2\n";
        return 11;
    }

    if (std::strcmp(expect, (char*)buf) != 0 ) {
        std::cerr << ": WRS strings don't match 2\n";
        return 12;
    }

    return 0;
}
Example #8
0
    void PngImage::doWriteMetadata(BasicIo& outIo)
    {
        if (!io_->isopen()) throw Error(20);
        if (!outIo.isopen()) throw Error(21);

#ifdef DEBUG
        std::cout << "Exiv2::PngImage::doWriteMetadata: Writing PNG file " << io_->path() << "\n";
        std::cout << "Exiv2::PngImage::doWriteMetadata: tmp file created " << outIo.path() << "\n";
#endif

        // Ensure that this is the correct image type
        if (!isPngType(*io_, true))
        {
            if (io_->error() || io_->eof()) throw Error(20);
            throw Error(22);
        }

        // Write PNG Signature.
        if (outIo.write(pngSignature, 8) != 8) throw Error(21);

        DataBuf cheaderBuf(8);       // Chunk header : 4 bytes (data size) + 4 bytes (chunk type).

        while(!io_->eof())
        {
            // Read chunk header.

            std::memset(cheaderBuf.pData_, 0x00, cheaderBuf.size_);
            long bufRead = io_->read(cheaderBuf.pData_, cheaderBuf.size_);
            if (io_->error()) throw Error(14);
            if (bufRead != cheaderBuf.size_) throw Error(20);

            // Decode chunk data length.

            uint32_t dataOffset = getULong(cheaderBuf.pData_, bigEndian);
            if (dataOffset > 0x7FFFFFFF) throw Exiv2::Error(14);

            // Read whole chunk : Chunk header + Chunk data (not fixed size - can be null) + CRC (4 bytes).

            DataBuf chunkBuf(8 + dataOffset + 4);                     // Chunk header (8 bytes) + Chunk data + CRC (4 bytes).
            memcpy(chunkBuf.pData_, cheaderBuf.pData_, 8);            // Copy header.
            bufRead = io_->read(chunkBuf.pData_ + 8, dataOffset + 4); // Extract chunk data + CRC
            if (io_->error()) throw Error(14);
            if (bufRead != (long)(dataOffset + 4)) throw Error(20);

            if (!memcmp(cheaderBuf.pData_ + 4, "IEND", 4))
            {
                // Last chunk found: we write it and done.
#ifdef DEBUG
                std::cout << "Exiv2::PngImage::doWriteMetadata: Write IEND chunk (length: " << dataOffset << ")\n";
#endif
                if (outIo.write(chunkBuf.pData_, chunkBuf.size_) != chunkBuf.size_) throw Error(21);
                return;
            }
            else if (!memcmp(cheaderBuf.pData_ + 4, "IHDR", 4))
            {
#ifdef DEBUG
                std::cout << "Exiv2::PngImage::doWriteMetadata: Write IHDR chunk (length: " << dataOffset << ")\n";
#endif
                if (outIo.write(chunkBuf.pData_, chunkBuf.size_) != chunkBuf.size_) throw Error(21);

                // Write all updated metadata here, just after IHDR.
                if (!comment_.empty())
                {
                    // Update Comment data to a new PNG chunk
                    std::string chunk = PngChunk::makeMetadataChunk(comment_, mdComment);
                    if (outIo.write((const byte*)chunk.data(), static_cast<long>(chunk.size())) != (long)chunk.size())
                    {
                        throw Error(21);
                    }
                }

                if (exifData_.count() > 0)
                {
                    // Update Exif data to a new PNG chunk
                    Blob blob;
                    ExifParser::encode(blob, littleEndian, exifData_);
                    if (blob.size() > 0)
                    {
                        static const char exifHeader[] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 };
                        std::string rawExif =   std::string(exifHeader, 6)
                                              + std::string((const char*)&blob[0], blob.size());
                        std::string chunk = PngChunk::makeMetadataChunk(rawExif, mdExif);
                        if (outIo.write((const byte*)chunk.data(), static_cast<long>(chunk.size())) != (long)chunk.size())
                        {
                            throw Error(21);
                        }
                    }
                }

                if (iptcData_.count() > 0)
                {
                    // Update IPTC data to a new PNG chunk
                    DataBuf newPsData = Photoshop::setIptcIrb(0, 0, iptcData_);
                    if (newPsData.size_ > 0)
                    {
                        std::string rawIptc((const char*)newPsData.pData_, newPsData.size_);
                        std::string chunk = PngChunk::makeMetadataChunk(rawIptc, mdIptc);
                        if (outIo.write((const byte*)chunk.data(), static_cast<long>(chunk.size())) != (long)chunk.size())
                        {
                            throw Error(21);
                        }
                    }
                }

                if (writeXmpFromPacket() == false) {
                    if (XmpParser::encode(xmpPacket_, xmpData_) > 1) {
#ifndef SUPPRESS_WARNINGS
                        std::cerr << "Error: Failed to encode XMP metadata.\n";
#endif
                    }
                }
                if (xmpPacket_.size() > 0) {
                    // Update XMP data to a new PNG chunk
                    std::string chunk = PngChunk::makeMetadataChunk(xmpPacket_, mdXmp);
                    if (outIo.write((const byte*)chunk.data(), static_cast<long>(chunk.size())) != (long)chunk.size()) {
                        throw Error(21);
                    }
                }
            }
            else if (!memcmp(cheaderBuf.pData_ + 4, "tEXt", 4) ||
                     !memcmp(cheaderBuf.pData_ + 4, "zTXt", 4) ||
                     !memcmp(cheaderBuf.pData_ + 4, "iTXt", 4))
            {
                DataBuf key = PngChunk::keyTXTChunk(chunkBuf, true);
                if (memcmp("Raw profile type exif", key.pData_, 21) == 0 ||
                    memcmp("Raw profile type APP1", key.pData_, 21) == 0 ||
                    memcmp("Raw profile type iptc", key.pData_, 21) == 0 ||
                    memcmp("Raw profile type xmp",  key.pData_, 20) == 0 ||
                    memcmp("XML:com.adobe.xmp",     key.pData_, 17) == 0 ||
                    memcmp("Description",           key.pData_, 11) == 0)
                {
#ifdef DEBUG
                    std::cout << "Exiv2::PngImage::doWriteMetadata: strip " << cheaderBuf.pData_ + 4
                              << " chunk (key: " << key.pData_ << ")\n";
#endif
                }
                else
                {
#ifdef DEBUG
                    std::cout << "Exiv2::PngImage::doWriteMetadata: write " << cheaderBuf.pData_ + 4
                              << " chunk (length: " << dataOffset << ")\n";
#endif
                    if (outIo.write(chunkBuf.pData_, chunkBuf.size_) != chunkBuf.size_) throw Error(21);
                }
            }
            else
            {
                // Write all others chunk as well.
#ifdef DEBUG
                std::cout << "Exiv2::PngImage::doWriteMetadata: write " << cheaderBuf.pData_ + 4
                          << " chunk (length: " << dataOffset << ")\n";
#endif
                if (outIo.write(chunkBuf.pData_, chunkBuf.size_) != chunkBuf.size_) throw Error(21);

            }
        }

    } // PngImage::doWriteMetadata
Example #9
0
    void PsdImage::doWriteMetadata(BasicIo& outIo)
    {
        if (!io_->isopen()) throw Error(20);
        if (!outIo.isopen()) throw Error(21);

#ifdef DEBUG
        std::cout << "Exiv2::PsdImage::doWriteMetadata: Writing PSD file " << io_->path() << "\n";
        std::cout << "Exiv2::PsdImage::doWriteMetadata: tmp file created " << outIo.path() << "\n";
#endif

        // Ensure that this is the correct image type
        if (!isPsdType(*io_, true)) {
            if (io_->error() || io_->eof()) throw Error(20);
            throw Error(22);
        }

        io_->seek(0, BasicIo::beg);    // rewind

        DataBuf lbuf(4096);
        byte buf[8];

        // Get Photoshop header from original file
        byte psd_head[26];
        if (io_->read(psd_head, 26) != 26) throw Error(3, "Photoshop");

        // Write Photoshop header data out to new PSD file
        if (outIo.write(psd_head, 26) != 26) throw Error(21);

        // Read colorDataLength from original PSD 
        if (io_->read(buf, 4) != 4) throw Error(3, "Photoshop");

        uint32_t colorDataLength = getULong(buf, bigEndian);

        // Write colorDataLength
        ul2Data(buf, colorDataLength, bigEndian);
        if (outIo.write(buf, 4) != 4) throw Error(21);
#ifdef DEBUG
        std::cerr << std::dec << "colorDataLength: " << colorDataLength << "\n";
#endif
        // Copy colorData
        uint32_t readTotal = 0;
        long toRead = 0;
        while (readTotal < colorDataLength) {
            toRead =   static_cast<long>(colorDataLength - readTotal) < lbuf.size_
                     ? colorDataLength - readTotal : lbuf.size_;
            if (io_->read(lbuf.pData_, toRead) != toRead) throw Error(3, "Photoshop");
            readTotal += toRead;
            if (outIo.write(lbuf.pData_, toRead) != toRead) throw Error(21);
        }
        if (outIo.error()) throw Error(21);

        uint32_t resLenOffset = io_->tell();  // remember for later update

        // Read length of all resource blocks from original PSD
        if (io_->read(buf, 4) != 4) throw Error(3, "Photoshop");

        uint32_t oldResLength = getULong(buf, bigEndian);
        uint32_t newResLength = 0;

        // Write oldResLength (will be updated later)
        ul2Data(buf, oldResLength, bigEndian);
        if (outIo.write(buf, 4) != 4) throw Error(21);

#ifdef DEBUG
        std::cerr << std::dec << "oldResLength: " << oldResLength << "\n";
#endif

        // Iterate over original resource blocks.
        // Replace or insert IPTC, EXIF and XMP
        // Original resource blocks assumed to be sorted ASC

        bool iptcDone = false;
        bool exifDone = false;
        bool xmpDone = false;
        while (oldResLength > 0) {
            if (io_->read(buf, 8) != 8) throw Error(3, "Photoshop");

            // read resource type and ID
            uint32_t resourceType = getULong(buf, bigEndian);

            if (resourceType != kPhotoshopResourceType) {
                break; // bad resource type
            }
            uint16_t resourceId = getUShort(buf + 4, bigEndian);
            uint32_t resourceNameLength = buf[6];
            uint32_t adjResourceNameLen = resourceNameLength & ~1;
            unsigned char resourceNameFirstChar = buf[7];

            // read rest of resource name, plus any padding
            DataBuf resName(256);
            if (   io_->read(resName.pData_, adjResourceNameLen)
                != static_cast<long>(adjResourceNameLen)) throw Error(3, "Photoshop");

            // read resource size (actual length w/o padding!)
            if (io_->read(buf, 4) != 4) throw Error(3, "Photoshop"); 

            uint32_t resourceSize = getULong(buf, bigEndian);
            uint32_t curOffset = io_->tell();

            // Write IPTC_NAA resource block
            if (   resourceId == kPhotoshopResourceID_IPTC_NAA
                || (resourceId > kPhotoshopResourceID_IPTC_NAA && iptcDone == false)) {
                newResLength += writeIptcData(iptcData_, outIo);
                resourceSize = (resourceSize + 1) & ~1;    // adjust for padding
                iptcDone = true;
            }

            // Write ExifInfo resource block
            else if (   resourceId == kPhotoshopResourceID_ExifInfo
                     || (resourceId > kPhotoshopResourceID_ExifInfo && exifDone == false)) {
                newResLength += writeExifData(exifData_, outIo);
                resourceSize = (resourceSize + 1) & ~1;    // adjust for padding
                exifDone = true;
            }

            // Write XMPpacket resource block
            else if (   resourceId == kPhotoshopResourceID_XMPPacket
                     || (resourceId > kPhotoshopResourceID_XMPPacket && xmpDone == false)) {
                newResLength += writeXmpData(xmpData_, outIo);
                resourceSize = (resourceSize + 1) & ~1;    // adjust for padding
                xmpDone = true;
            }

            // Copy all other resource blocks
            if (   resourceId != kPhotoshopResourceID_IPTC_NAA
                && resourceId != kPhotoshopResourceID_ExifInfo
                && resourceId != kPhotoshopResourceID_XMPPacket) {
#ifdef DEBUG
                std::cerr << std::hex << "copy : resourceId: " << resourceId << "\n";
                std::cerr << std::dec;
#endif
                // Copy resource block to new PSD file
                ul2Data(buf, kPhotoshopResourceType, bigEndian);
                if (outIo.write(buf, 4) != 4) throw Error(21);
                us2Data(buf, resourceId, bigEndian);
                if (outIo.write(buf, 2) != 2) throw Error(21);
                // Write resource name as Pascal string
                buf[0] = resourceNameLength & 0x000f;
                if (outIo.write(buf, 1) != 1) throw Error(21);
                buf[0] = resourceNameFirstChar;
                if (outIo.write(buf, 1) != 1) throw Error(21);
                if (   outIo.write(resName.pData_, adjResourceNameLen)
                    != static_cast<long>(adjResourceNameLen)) throw Error(21);
                ul2Data(buf, resourceSize, bigEndian);
                if (outIo.write(buf, 4) != 4) throw Error(21);

                readTotal = 0;
                toRead = 0;
                resourceSize = (resourceSize + 1) & ~1;        // pad to even
                while (readTotal < resourceSize) {
                    toRead =   static_cast<long>(resourceSize - readTotal) < lbuf.size_
                             ? resourceSize - readTotal : lbuf.size_;
                    if (io_->read(lbuf.pData_, toRead) != toRead) {
                        throw Error(3, "Photoshop");
                    }
                    readTotal += toRead;
                    if (outIo.write(lbuf.pData_, toRead) != toRead) throw Error(21);
                }
                if (outIo.error()) throw Error(21);
                newResLength += resourceSize + adjResourceNameLen + 12;
            }

            io_->seek(curOffset + resourceSize, BasicIo::beg);
            oldResLength -= (12 + adjResourceNameLen + resourceSize);
        }

        // Append IPTC_NAA resource block, if not yet written
        if (iptcDone == false) {
            newResLength += writeIptcData(iptcData_, outIo);
            iptcDone = true;
        }

        // Append ExifInfo resource block, if not yet written
        if (exifDone == false) {
            newResLength += writeExifData(exifData_, outIo);
            exifDone = true;
        }

        // Append XmpPacket resource block, if not yet written
        if (xmpDone == false) {
            newResLength += writeXmpData(xmpData_, outIo);
            xmpDone = true;
        }

        // Copy remaining data
        long readSize = 0;
        while ((readSize=io_->read(lbuf.pData_, lbuf.size_))) {
            if (outIo.write(lbuf.pData_, readSize) != readSize) throw Error(21);
        }
        if (outIo.error()) throw Error(21);

        // Update length of resources
#ifdef DEBUG
        std::cerr << "newResLength: " << newResLength << "\n";
#endif
        outIo.seek(resLenOffset, BasicIo::beg);
        ul2Data(buf, newResLength, bigEndian);
        if (outIo.write(buf, 4) != 4) throw Error(21);

    } // PsdImage::doWriteMetadata
Example #10
0
    void FileIo::transfer(BasicIo& src)
    {
        const bool wasOpen = (p_->fp_ != 0);
        const std::string lastMode(p_->openMode_);

        FileIo *fileIo = dynamic_cast<FileIo*>(&src);
        if (fileIo) {
            // Optimization if src is another instance of FileIo
            fileIo->close();
            // Check if the file can be written to, if it already exists
            if (open("a+b") != 0) {
                // Remove the (temporary) file
#ifdef EXV_UNICODE_PATH
                if (fileIo->p_->wpMode_ == Impl::wpUnicode) {
                    ::_wremove(fileIo->wpath().c_str());
                }
                else
#endif
                {
                    ::remove(fileIo->path().c_str());
                }
#ifdef EXV_UNICODE_PATH
                if (p_->wpMode_ == Impl::wpUnicode) {
                    throw WError(10, wpath(), "a+b", strError().c_str());
                }
                else
#endif
                {
                    throw Error(10, path(), "a+b", strError());
                }
            }
            close();

            bool statOk = true;
            mode_t origStMode = 0;
            std::string spf;
            char* pf = 0;
#ifdef EXV_UNICODE_PATH
            std::wstring wspf;
            wchar_t* wpf = 0;
            if (p_->wpMode_ == Impl::wpUnicode) {
                wspf = wpath();
                wpf = const_cast<wchar_t*>(wspf.c_str());
            }
            else
#endif
            {
                spf = path();
                pf = const_cast<char*>(spf.c_str());
            }

            // Get the permissions of the file, or linked-to file, on platforms which have lstat
#ifdef EXV_HAVE_LSTAT

# ifdef EXV_UNICODE_PATH
#  error EXV_UNICODE_PATH and EXV_HAVE_LSTAT are not compatible. Stop.
# endif
            struct stat buf1;
            if (::lstat(pf, &buf1) == -1) {
                statOk = false;
#ifndef SUPPRESS_WARNINGS
                EXV_WARNING << Error(2, pf, strError(), "::lstat") << "\n";
#endif
            }
            origStMode = buf1.st_mode;
            DataBuf lbuf; // So that the allocated memory is freed. Must have same scope as pf
            // In case path() is a symlink, get the path of the linked-to file
            if (statOk && S_ISLNK(buf1.st_mode)) {
                lbuf.alloc(buf1.st_size + 1);
                memset(lbuf.pData_, 0x0, lbuf.size_);
                pf = reinterpret_cast<char*>(lbuf.pData_);
                if (::readlink(path().c_str(), pf, lbuf.size_ - 1) == -1) {
                    throw Error(2, path(), strError(), "readlink");
                }
                // We need the permissions of the file, not the symlink
                if (::stat(pf, &buf1) == -1) {
                    statOk = false;
#ifndef SUPPRESS_WARNINGS
                    EXV_WARNING << Error(2, pf, strError(), "::stat") << "\n";
#endif
                }
                origStMode = buf1.st_mode;
            }
#else // EXV_HAVE_LSTAT
            Impl::StructStat buf1;
            if (p_->stat(buf1) == -1) {
                statOk = false;
            }
            origStMode = buf1.st_mode;
#endif // !EXV_HAVE_LSTAT

            // MSVCRT rename that does not overwrite existing files
#ifdef EXV_UNICODE_PATH
            if (p_->wpMode_ == Impl::wpUnicode) {
                if (fileExists(wpf) && ::_wremove(wpf) != 0) {
                    throw WError(2, wpf, strError().c_str(), "::_wremove");
                }
                if (::_wrename(fileIo->wpath().c_str(), wpf) == -1) {
                    throw WError(17, fileIo->wpath(), wpf, strError().c_str());
                }
                ::_wremove(fileIo->wpath().c_str());
                // Check permissions of new file
                struct _stat buf2;
                if (statOk && ::_wstat(wpf, &buf2) == -1) {
                    statOk = false;
#ifndef SUPPRESS_WARNINGS
                    EXV_WARNING << Error(2, wpf, strError(), "::_wstat") << "\n";
#endif
                }
                if (statOk && origStMode != buf2.st_mode) {
                    // Set original file permissions
                    if (::_wchmod(wpf, origStMode) == -1) {
#ifndef SUPPRESS_WARNINGS
                        EXV_WARNING << Error(2, wpf, strError(), "::_wchmod") << "\n";
#endif
                    }
                }
            } // if (p_->wpMode_ == Impl::wpUnicode)
            else
#endif // EXV_UNICODE_PATH
            {
                if (fileExists(pf) && ::remove(pf) != 0) {
                    throw Error(2, pf, strError(), "::remove");
                }
                if (::rename(fileIo->path().c_str(), pf) == -1) {
                    throw Error(17, fileIo->path(), pf, strError());
                }
                ::remove(fileIo->path().c_str());
                // Check permissions of new file
                struct stat buf2;
                if (statOk && ::stat(pf, &buf2) == -1) {
                    statOk = false;
#ifndef SUPPRESS_WARNINGS
                    EXV_WARNING << Error(2, pf, strError(), "::stat") << "\n";
#endif
                }
                if (statOk && origStMode != buf2.st_mode) {
                    // Set original file permissions
                    if (::chmod(pf, origStMode) == -1) {
#ifndef SUPPRESS_WARNINGS
                        EXV_WARNING << Error(2, pf, strError(), "::chmod") << "\n";
#endif
                    }
                }
            }
        } // if (fileIo)
        else {
            // Generic handling, reopen both to reset to start
            if (open("w+b") != 0) {
#ifdef EXV_UNICODE_PATH
                if (p_->wpMode_ == Impl::wpUnicode) {
                    throw WError(10, wpath(), "w+b", strError().c_str());
                }
                else
#endif
                {
                    throw Error(10, path(), "w+b", strError());
                }
            }
            if (src.open() != 0) {
#ifdef EXV_UNICODE_PATH
                if (p_->wpMode_ == Impl::wpUnicode) {
                    throw WError(9, src.wpath(), strError().c_str());
                }
                else
#endif
                {
                    throw Error(9, src.path(), strError());
                }
            }
            write(src);
            src.close();
        }

        if (wasOpen) {
            if (open(lastMode) != 0) {
#ifdef EXV_UNICODE_PATH
                if (p_->wpMode_ == Impl::wpUnicode) {
                    throw WError(10, wpath(), lastMode.c_str(), strError().c_str());
                }
                else
#endif
                {
                    throw Error(10, path(), lastMode, strError());
                }
            }
        }
        else close();

        if (error() || src.error()) {
#ifdef EXV_UNICODE_PATH
            if (p_->wpMode_ == Impl::wpUnicode) {
                throw WError(18, wpath(), strError().c_str());
            }
            else
#endif
            {
                throw Error(18, path(), strError());
            }
        }
    } // FileIo::transfer
Example #11
0
    void FileIo::transfer(BasicIo& src)
    {
        const bool wasOpen = (fp_ != 0);
        const std::string lastMode(openMode_);

        FileIo *fileIo = dynamic_cast<FileIo*>(&src);
        if (fileIo) {
            // Optimization if src is another instance of FileIo
            fileIo->close();
            // Check if the file can be written to, if it already exists
            if (open("w+b") != 0) {
                // Remove the (temporary) file
                std::remove(fileIo->path_.c_str());
                throw Error(10, path_, "w+b", strError());
            }
            close();
            bool statOk = true;
            struct stat buf1;
            char* pf = const_cast<char*>(path_.c_str());
#ifdef EXV_HAVE_LSTAT
            if (::lstat(pf, &buf1) == -1) {
                statOk = false;
#ifndef SUPPRESS_WARNINGS
                std::cerr << "Warning: " << Error(2, pf, strError(), "lstat") << "\n";
#endif
            }
            DataBuf lbuf; // So that the allocated memory is freed. Must have same scope as pf
            // In case path_ is a symlink, get the path of the linked-to file
            if (statOk && S_ISLNK(buf1.st_mode)) {
                lbuf.alloc(buf1.st_size + 1);
                memset(lbuf.pData_, 0x0, lbuf.size_);
                pf = reinterpret_cast<char*>(lbuf.pData_);
                if (readlink(path_.c_str(), pf, lbuf.size_ - 1) == -1) {
                    throw Error(2, path_, strError(), "readlink");
                }
                // We need the permissions of the file, not the symlink
                if (::stat(pf, &buf1) == -1) {
                    statOk = false;
#ifndef SUPPRESS_WARNINGS
                    std::cerr << "Warning: " << Error(2, pf, strError(), "stat") << "\n";
#endif
                }
            }
#else
            if (::stat(pf, &buf1) == -1) {
                statOk = false;
#ifndef SUPPRESS_WARNINGS
                std::cerr << "Warning: " << Error(2, pf, strError(), "stat") << "\n";
#endif
            }
#endif // !EXV_HAVE_LSTAT
            // MSVCRT rename that does not overwrite existing files
            if (fileExists(pf) && std::remove(pf) != 0) {
                throw Error(2, pf, strError(), "std::remove");
            }
            if (std::rename(fileIo->path_.c_str(), pf) == -1) {
                throw Error(17, fileIo->path_, pf, strError());
            }
            std::remove(fileIo->path_.c_str());
            // Check permissions of new file
            struct stat buf2;
            if (statOk && ::stat(pf, &buf2) == -1) {
                statOk = false;
#ifndef SUPPRESS_WARNINGS
                std::cerr << "Warning: " << Error(2, pf, strError(), "stat") << "\n";
#endif
            }
            if (statOk && buf1.st_mode != buf2.st_mode) {
                // Set original file permissions
                if (::chmod(pf, buf1.st_mode) == -1) {
#ifndef SUPPRESS_WARNINGS
                    std::cerr << "Warning: " << Error(2, pf, strError(), "chmod") << "\n";
#endif
                }
            }
        }
        else {
            // Generic handling, reopen both to reset to start
            if (open("w+b") != 0) {
                throw Error(10, path_, "w+b", strError());
            }
            if (src.open() != 0) {
                throw Error(9, src.path(), strError());
            }
            write(src);
            src.close();
        }

        if (wasOpen) {
            if (open(lastMode) != 0) {
                throw Error(10, path_, lastMode, strError());
            }
        }
        else close();

        if (error() || src.error()) throw Error(18, path_, strError());
    }