LCOV - code coverage report
Current view: top level - package/source/zipapi - ZipOutputStream.cxx (source / functions) Hit Total Coverage
Test: commit e02a6cb2c3e2b23b203b422e4e0680877f232636 Lines: 0 233 0.0 %
Date: 2014-04-14 Functions: 0 17 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : #include <com/sun/star/packages/zip/ZipConstants.hpp>
      21             : #include <com/sun/star/io/XOutputStream.hpp>
      22             : #include <comphelper/storagehelper.hxx>
      23             : 
      24             : #include <osl/time.h>
      25             : 
      26             : #include <EncryptionData.hxx>
      27             : #include <PackageConstants.hxx>
      28             : #include <ZipEntry.hxx>
      29             : #include <ZipFile.hxx>
      30             : #include <ZipPackageStream.hxx>
      31             : #include <ZipOutputStream.hxx>
      32             : 
      33             : using namespace com::sun::star;
      34             : using namespace com::sun::star::io;
      35             : using namespace com::sun::star::uno;
      36             : using namespace com::sun::star::packages;
      37             : using namespace com::sun::star::packages::zip;
      38             : using namespace com::sun::star::packages::zip::ZipConstants;
      39             : 
      40             : /** This class is used to write Zip files
      41             :  */
      42           0 : ZipOutputStream::ZipOutputStream( const uno::Reference< uno::XComponentContext >& rxContext,
      43             :                                   const uno::Reference < XOutputStream > &xOStream )
      44             : : m_xContext( rxContext )
      45             : , xStream(xOStream)
      46             : , m_aDeflateBuffer(n_ConstBufferSize)
      47             : , aDeflater(DEFAULT_COMPRESSION, true)
      48             : , aChucker(xOStream)
      49             : , pCurrentEntry(NULL)
      50             : , nMethod(DEFLATED)
      51             : , nLevel(0)
      52             : , mnDigested(0)
      53             : , bFinished(sal_False)
      54             : , bEncryptCurrentEntry(sal_False)
      55           0 : , m_pCurrentStream(NULL)
      56             : {
      57           0 : }
      58             : 
      59           0 : ZipOutputStream::~ZipOutputStream( void )
      60             : {
      61           0 :     for (sal_Int32 i = 0, nEnd = aZipList.size(); i < nEnd; i++)
      62           0 :         delete aZipList[i];
      63           0 : }
      64             : 
      65           0 : void SAL_CALL ZipOutputStream::setMethod( sal_Int32 nNewMethod )
      66             :     throw(RuntimeException)
      67             : {
      68           0 :     nMethod = static_cast < sal_Int16 > (nNewMethod);
      69           0 : }
      70           0 : void SAL_CALL ZipOutputStream::setLevel( sal_Int32 nNewLevel )
      71             :     throw(RuntimeException)
      72             : {
      73           0 :     aDeflater.setLevel( nNewLevel);
      74           0 : }
      75             : 
      76           0 : void SAL_CALL ZipOutputStream::putNextEntry( ZipEntry& rEntry,
      77             :                         ZipPackageStream* pStream,
      78             :                         sal_Bool bEncrypt)
      79             :     throw(IOException, RuntimeException)
      80             : {
      81           0 :     if (pCurrentEntry != NULL)
      82           0 :         closeEntry();
      83           0 :     if (rEntry.nTime == -1)
      84           0 :         rEntry.nTime = getCurrentDosTime();
      85           0 :     if (rEntry.nMethod == -1)
      86           0 :         rEntry.nMethod = nMethod;
      87           0 :     rEntry.nVersion = 20;
      88           0 :     rEntry.nFlag = 1 << 11;
      89           0 :     if (rEntry.nSize == -1 || rEntry.nCompressedSize == -1 ||
      90           0 :         rEntry.nCrc == -1)
      91             :     {
      92           0 :         rEntry.nSize = rEntry.nCompressedSize = 0;
      93           0 :         rEntry.nFlag |= 8;
      94             :     }
      95             : 
      96           0 :     if (bEncrypt)
      97             :     {
      98           0 :         bEncryptCurrentEntry = sal_True;
      99             : 
     100           0 :         m_xCipherContext = ZipFile::StaticGetCipher( m_xContext, pStream->GetEncryptionData(), true );
     101           0 :         m_xDigestContext = ZipFile::StaticGetDigestContextForChecksum( m_xContext, pStream->GetEncryptionData() );
     102           0 :         mnDigested = 0;
     103           0 :         rEntry.nFlag |= 1 << 4;
     104           0 :         m_pCurrentStream = pStream;
     105             :     }
     106           0 :     sal_Int32 nLOCLength = writeLOC(rEntry);
     107           0 :     rEntry.nOffset = aChucker.GetPosition() - nLOCLength;
     108           0 :     aZipList.push_back( &rEntry );
     109           0 :     pCurrentEntry = &rEntry;
     110           0 : }
     111             : 
     112           0 : void SAL_CALL ZipOutputStream::closeEntry(  )
     113             :     throw(IOException, RuntimeException)
     114             : {
     115           0 :     ZipEntry *pEntry = pCurrentEntry;
     116           0 :     if (pEntry)
     117             :     {
     118           0 :         switch (pEntry->nMethod)
     119             :         {
     120             :             case DEFLATED:
     121           0 :                 aDeflater.finish();
     122           0 :                 while (!aDeflater.finished())
     123           0 :                     doDeflate();
     124           0 :                 if ((pEntry->nFlag & 8) == 0)
     125             :                 {
     126           0 :                     if (pEntry->nSize != aDeflater.getTotalIn())
     127             :                     {
     128             :                         OSL_FAIL("Invalid entry size");
     129             :                     }
     130           0 :                     if (pEntry->nCompressedSize != aDeflater.getTotalOut())
     131             :                     {
     132             :                         // Different compression strategies make the merit of this
     133             :                         // test somewhat dubious
     134           0 :                         pEntry->nCompressedSize = aDeflater.getTotalOut();
     135             :                     }
     136           0 :                     if (pEntry->nCrc != aCRC.getValue())
     137             :                     {
     138             :                         OSL_FAIL("Invalid entry CRC-32");
     139             :                     }
     140             :                 }
     141             :                 else
     142             :                 {
     143           0 :                     if ( !bEncryptCurrentEntry )
     144             :                     {
     145           0 :                         pEntry->nSize = aDeflater.getTotalIn();
     146           0 :                         pEntry->nCompressedSize = aDeflater.getTotalOut();
     147             :                     }
     148           0 :                     pEntry->nCrc = aCRC.getValue();
     149           0 :                     writeEXT(*pEntry);
     150             :                 }
     151           0 :                 aDeflater.reset();
     152           0 :                 aCRC.reset();
     153           0 :                 break;
     154             :             case STORED:
     155           0 :                 if (!((pEntry->nFlag & 8) == 0))
     156             :                     OSL_FAIL( "Serious error, one of compressed size, size or CRC was -1 in a STORED stream");
     157           0 :                 break;
     158             :             default:
     159             :                 OSL_FAIL("Invalid compression method");
     160           0 :                 break;
     161             :         }
     162             : 
     163           0 :         if (bEncryptCurrentEntry)
     164             :         {
     165           0 :             bEncryptCurrentEntry = sal_False;
     166             : 
     167           0 :             m_xCipherContext.clear();
     168             : 
     169           0 :             uno::Sequence< sal_Int8 > aDigestSeq;
     170           0 :             if ( m_xDigestContext.is() )
     171             :             {
     172           0 :                 aDigestSeq = m_xDigestContext->finalizeDigestAndDispose();
     173           0 :                 m_xDigestContext.clear();
     174             :             }
     175             : 
     176           0 :             if ( m_pCurrentStream )
     177           0 :                 m_pCurrentStream->setDigest( aDigestSeq );
     178             :         }
     179           0 :         pCurrentEntry = NULL;
     180           0 :         m_pCurrentStream = NULL;
     181             :     }
     182           0 : }
     183             : 
     184           0 : void SAL_CALL ZipOutputStream::write( const Sequence< sal_Int8 >& rBuffer, sal_Int32 nNewOffset, sal_Int32 nNewLength )
     185             :     throw(IOException, RuntimeException)
     186             : {
     187           0 :     switch (pCurrentEntry->nMethod)
     188             :     {
     189             :         case DEFLATED:
     190           0 :             if (!aDeflater.finished())
     191             :             {
     192           0 :                 aDeflater.setInputSegment(rBuffer, nNewOffset, nNewLength);
     193           0 :                  while (!aDeflater.needsInput())
     194           0 :                     doDeflate();
     195           0 :                 if (!bEncryptCurrentEntry)
     196           0 :                     aCRC.updateSegment(rBuffer, nNewOffset, nNewLength);
     197             :             }
     198           0 :             break;
     199             :         case STORED:
     200             :             {
     201           0 :                 Sequence < sal_Int8 > aTmpBuffer ( rBuffer.getConstArray(), nNewLength );
     202           0 :                 aChucker.WriteBytes( aTmpBuffer );
     203             :             }
     204           0 :             break;
     205             :     }
     206           0 : }
     207             : 
     208           0 : void SAL_CALL ZipOutputStream::rawWrite( Sequence< sal_Int8 >& rBuffer, sal_Int32 /*nNewOffset*/, sal_Int32 nNewLength )
     209             :     throw(IOException, RuntimeException)
     210             : {
     211           0 :     Sequence < sal_Int8 > aTmpBuffer ( rBuffer.getConstArray(), nNewLength );
     212           0 :     aChucker.WriteBytes( aTmpBuffer );
     213           0 : }
     214             : 
     215           0 : void SAL_CALL ZipOutputStream::rawCloseEntry(  )
     216             :     throw(IOException, RuntimeException)
     217             : {
     218           0 :     if ( pCurrentEntry->nMethod == DEFLATED && ( pCurrentEntry->nFlag & 8 ) )
     219           0 :         writeEXT(*pCurrentEntry);
     220           0 :     pCurrentEntry = NULL;
     221           0 : }
     222             : 
     223           0 : void SAL_CALL ZipOutputStream::finish(  )
     224             :     throw(IOException, RuntimeException)
     225             : {
     226           0 :     if (bFinished)
     227           0 :         return;
     228             : 
     229           0 :     if (pCurrentEntry != NULL)
     230           0 :         closeEntry();
     231             : 
     232           0 :     if (aZipList.size() < 1)
     233             :         OSL_FAIL("Zip file must have at least one entry!\n");
     234             : 
     235           0 :     sal_Int32 nOffset= static_cast < sal_Int32 > (aChucker.GetPosition());
     236           0 :     for (sal_Int32 i =0, nEnd = aZipList.size(); i < nEnd; i++)
     237           0 :         writeCEN( *aZipList[i] );
     238           0 :     writeEND( nOffset, static_cast < sal_Int32 > (aChucker.GetPosition()) - nOffset);
     239           0 :     bFinished = sal_True;
     240           0 :     xStream->flush();
     241             : }
     242             : 
     243           0 : void ZipOutputStream::doDeflate()
     244             : {
     245           0 :     sal_Int32 nLength = aDeflater.doDeflateSegment(m_aDeflateBuffer, 0, m_aDeflateBuffer.getLength());
     246             : 
     247           0 :     if ( nLength > 0 )
     248             :     {
     249           0 :         uno::Sequence< sal_Int8 > aTmpBuffer( m_aDeflateBuffer.getConstArray(), nLength );
     250           0 :         if ( bEncryptCurrentEntry && m_xDigestContext.is() && m_xCipherContext.is() )
     251             :         {
     252             :             // Need to update our digest before encryption...
     253           0 :             sal_Int32 nDiff = n_ConstDigestLength - mnDigested;
     254           0 :             if ( nDiff )
     255             :             {
     256           0 :                 sal_Int32 nEat = ::std::min( nLength, nDiff );
     257           0 :                 uno::Sequence< sal_Int8 > aTmpSeq( aTmpBuffer.getConstArray(), nEat );
     258           0 :                 m_xDigestContext->updateDigest( aTmpSeq );
     259           0 :                 mnDigested = mnDigested + static_cast< sal_Int16 >( nEat );
     260             :             }
     261             : 
     262             :             // FIXME64: uno::Sequence not 64bit safe.
     263           0 :             uno::Sequence< sal_Int8 > aEncryptionBuffer = m_xCipherContext->convertWithCipherContext( aTmpBuffer );
     264             : 
     265           0 :             aChucker.WriteBytes( aEncryptionBuffer );
     266             : 
     267             :             // the sizes as well as checksum for encrypted streams is calculated here
     268           0 :             pCurrentEntry->nCompressedSize += aEncryptionBuffer.getLength();
     269           0 :             pCurrentEntry->nSize = pCurrentEntry->nCompressedSize;
     270           0 :             aCRC.update( aEncryptionBuffer );
     271             :         }
     272             :         else
     273             :         {
     274           0 :             aChucker.WriteBytes ( aTmpBuffer );
     275           0 :         }
     276             :     }
     277             : 
     278           0 :     if ( aDeflater.finished() && bEncryptCurrentEntry && m_xDigestContext.is() && m_xCipherContext.is() )
     279             :     {
     280             :         // FIXME64: sequence not 64bit safe.
     281           0 :         uno::Sequence< sal_Int8 > aEncryptionBuffer = m_xCipherContext->finalizeCipherContextAndDispose();
     282           0 :         if ( aEncryptionBuffer.getLength() )
     283             :         {
     284           0 :             aChucker.WriteBytes( aEncryptionBuffer );
     285             : 
     286             :             // the sizes as well as checksum for encrypted streams is calculated hier
     287           0 :             pCurrentEntry->nCompressedSize += aEncryptionBuffer.getLength();
     288           0 :             pCurrentEntry->nSize = pCurrentEntry->nCompressedSize;
     289           0 :             aCRC.update( aEncryptionBuffer );
     290           0 :         }
     291             :     }
     292           0 : }
     293             : 
     294           0 : void ZipOutputStream::writeEND(sal_uInt32 nOffset, sal_uInt32 nLength)
     295             :     throw(IOException, RuntimeException)
     296             : {
     297           0 :     aChucker << ENDSIG;
     298           0 :     aChucker << static_cast < sal_Int16 > ( 0 );
     299           0 :     aChucker << static_cast < sal_Int16 > ( 0 );
     300           0 :     aChucker << static_cast < sal_Int16 > ( aZipList.size() );
     301           0 :     aChucker << static_cast < sal_Int16 > ( aZipList.size() );
     302           0 :     aChucker << nLength;
     303           0 :     aChucker << nOffset;
     304           0 :     aChucker << static_cast < sal_Int16 > ( 0 );
     305           0 : }
     306             : 
     307           0 : static sal_uInt32 getTruncated( sal_Int64 nNum, bool *pIsTruncated )
     308             : {
     309           0 :     if( nNum >= 0xffffffff )
     310             :     {
     311           0 :         *pIsTruncated = true;
     312           0 :         return 0xffffffff;
     313             :     }
     314             :     else
     315           0 :         return static_cast< sal_uInt32 >( nNum );
     316             : }
     317             : 
     318           0 : void ZipOutputStream::writeCEN( const ZipEntry &rEntry )
     319             :     throw(IOException, RuntimeException)
     320             : {
     321           0 :     if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( rEntry.sPath, true ) )
     322           0 :         throw IOException("Unexpected character is used in file name.", uno::Reference< XInterface >() );
     323             : 
     324           0 :     OString sUTF8Name = OUStringToOString( rEntry.sPath, RTL_TEXTENCODING_UTF8 );
     325           0 :     sal_Int16 nNameLength       = static_cast < sal_Int16 > ( sUTF8Name.getLength() );
     326             : 
     327           0 :     aChucker << CENSIG;
     328           0 :     aChucker << rEntry.nVersion;
     329           0 :     aChucker << rEntry.nVersion;
     330           0 :     if (rEntry.nFlag & (1 << 4) )
     331             :     {
     332             :         // If it's an encrypted entry, we pretend its stored plain text
     333           0 :         ZipEntry *pEntry = const_cast < ZipEntry * > ( &rEntry );
     334           0 :         pEntry->nFlag &= ~(1 <<4 );
     335           0 :         aChucker << rEntry.nFlag;
     336           0 :         aChucker << static_cast < sal_Int16 > ( STORED );
     337             :     }
     338             :     else
     339             :     {
     340           0 :         aChucker << rEntry.nFlag;
     341           0 :         aChucker << rEntry.nMethod;
     342             :     }
     343           0 :     bool bWrite64Header = false;
     344             : 
     345           0 :     aChucker << static_cast < sal_uInt32> ( rEntry.nTime );
     346           0 :     aChucker << static_cast < sal_uInt32> ( rEntry.nCrc );
     347           0 :     aChucker << getTruncated( rEntry.nCompressedSize, &bWrite64Header );
     348           0 :     aChucker << getTruncated( rEntry.nSize, &bWrite64Header );
     349           0 :     aChucker << nNameLength;
     350           0 :     aChucker << static_cast < sal_Int16> (0);
     351           0 :     aChucker << static_cast < sal_Int16> (0);
     352           0 :     aChucker << static_cast < sal_Int16> (0);
     353           0 :     aChucker << static_cast < sal_Int16> (0);
     354           0 :     aChucker << static_cast < sal_Int32> (0);
     355           0 :     aChucker << getTruncated( rEntry.nOffset, &bWrite64Header );
     356             : 
     357           0 :     if( bWrite64Header )
     358             :     {
     359             :         // FIXME64: need to append a ZIP64 header instead of throwing
     360             :         // We're about to silently loose people's data - which they are
     361             :         // unlikely to appreciate so fail instead:
     362             :         throw IOException( "File contains streams that are too large.",
     363           0 :                            uno::Reference< XInterface >() );
     364             :     }
     365             : 
     366           0 :     Sequence < sal_Int8 > aSequence( (sal_Int8*)sUTF8Name.getStr(), sUTF8Name.getLength() );
     367           0 :     aChucker.WriteBytes( aSequence );
     368           0 : }
     369           0 : void ZipOutputStream::writeEXT( const ZipEntry &rEntry )
     370             :     throw(IOException, RuntimeException)
     371             : {
     372           0 :     bool bWrite64Header = false;
     373             : 
     374           0 :     aChucker << EXTSIG;
     375           0 :     aChucker << static_cast < sal_uInt32> ( rEntry.nCrc );
     376           0 :     aChucker << getTruncated( rEntry.nCompressedSize, &bWrite64Header );
     377           0 :     aChucker << getTruncated( rEntry.nSize, &bWrite64Header );
     378             : 
     379           0 :     if( bWrite64Header )
     380             :     {
     381             :         // FIXME64: need to append a ZIP64 header instead of throwing
     382             :         // We're about to silently loose people's data - which they are
     383             :         // unlikely to appreciate so fail instead:
     384             :         throw IOException( "File contains streams that are too large.",
     385           0 :                            uno::Reference< XInterface >() );
     386             :     }
     387           0 : }
     388             : 
     389           0 : sal_Int32 ZipOutputStream::writeLOC( const ZipEntry &rEntry )
     390             :     throw(IOException, RuntimeException)
     391             : {
     392           0 :     if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( rEntry.sPath, true ) )
     393           0 :         throw IOException("Unexpected character is used in file name.", uno::Reference< XInterface >() );
     394             : 
     395           0 :     OString sUTF8Name = OUStringToOString( rEntry.sPath, RTL_TEXTENCODING_UTF8 );
     396           0 :     sal_Int16 nNameLength       = static_cast < sal_Int16 > ( sUTF8Name.getLength() );
     397             : 
     398           0 :     aChucker << LOCSIG;
     399           0 :     aChucker << rEntry.nVersion;
     400             : 
     401           0 :     if (rEntry.nFlag & (1 << 4) )
     402             :     {
     403             :         // If it's an encrypted entry, we pretend its stored plain text
     404           0 :         sal_Int16 nTmpFlag = rEntry.nFlag;
     405           0 :         nTmpFlag &= ~(1 <<4 );
     406           0 :         aChucker << nTmpFlag;
     407           0 :         aChucker << static_cast < sal_Int16 > ( STORED );
     408             :     }
     409             :     else
     410             :     {
     411           0 :         aChucker << rEntry.nFlag;
     412           0 :         aChucker << rEntry.nMethod;
     413             :     }
     414             : 
     415           0 :     bool bWrite64Header = false;
     416             : 
     417           0 :     aChucker << static_cast < sal_uInt32 > (rEntry.nTime);
     418           0 :     if ((rEntry.nFlag & 8) == 8 )
     419             :     {
     420           0 :         aChucker << static_cast < sal_Int32 > (0);
     421           0 :         aChucker << static_cast < sal_Int32 > (0);
     422           0 :         aChucker << static_cast < sal_Int32 > (0);
     423             :     }
     424             :     else
     425             :     {
     426           0 :         aChucker << static_cast < sal_uInt32 > (rEntry.nCrc);
     427           0 :         aChucker << getTruncated( rEntry.nCompressedSize, &bWrite64Header );
     428           0 :         aChucker << getTruncated( rEntry.nSize, &bWrite64Header );
     429             :     }
     430           0 :     aChucker << nNameLength;
     431           0 :     aChucker << static_cast < sal_Int16 > (0);
     432             : 
     433           0 :     if( bWrite64Header )
     434             :     {
     435             :         // FIXME64: need to append a ZIP64 header instead of throwing
     436             :         // We're about to silently loose people's data - which they are
     437             :         // unlikely to appreciate so fail instead:
     438             :         throw IOException( "File contains streams that are too large.",
     439           0 :                            uno::Reference< XInterface >() );
     440             :     }
     441             : 
     442           0 :     Sequence < sal_Int8 > aSequence( (sal_Int8*)sUTF8Name.getStr(), sUTF8Name.getLength() );
     443           0 :     aChucker.WriteBytes( aSequence );
     444             : 
     445           0 :     return LOCHDR + nNameLength;
     446             : }
     447           0 : sal_uInt32 ZipOutputStream::getCurrentDosTime( )
     448             : {
     449             :     oslDateTime aDateTime;
     450             :     TimeValue aTimeValue;
     451           0 :     osl_getSystemTime ( &aTimeValue );
     452           0 :     osl_getDateTimeFromTimeValue( &aTimeValue, &aDateTime);
     453             : 
     454             :     // at year 2108, there is an overflow
     455             :     // -> some decision needs to be made
     456             :     // how to handle the ZIP file format (just overflow?)
     457             : 
     458             :     // if the current system time is before 1980,
     459             :     // then the time traveller will have to make a decision
     460             :     // how to handle the ZIP file format before it is invented
     461             :     // (just underflow?)
     462             : 
     463             :     assert(aDateTime.Year > 1980 && aDateTime.Year < 2108);
     464             : 
     465           0 :     sal_uInt32 nYear = static_cast <sal_uInt32> (aDateTime.Year);
     466             : 
     467           0 :     if (nYear>=1980)
     468           0 :         nYear-=1980;
     469           0 :     else if (nYear>=80)
     470             :     {
     471           0 :         nYear-=80;
     472             :     }
     473             :     sal_uInt32 nResult = static_cast < sal_uInt32>( ( ( ( aDateTime.Day) +
     474           0 :                                           ( 32 * (aDateTime.Month)) +
     475           0 :                                           ( 512 * nYear ) ) << 16) |
     476             :                                         ( ( aDateTime.Seconds/2) +
     477           0 :                                             ( 32 * aDateTime.Minutes) +
     478           0 :                                           ( 2048 * static_cast <sal_uInt32 > (aDateTime.Hours) ) ) );
     479           0 :     return nResult;
     480             : }
     481             : /*
     482             : 
     483             :    This is actually never used, so I removed it, but thought that the
     484             :    implementation details may be useful in the future...mtg 20010307
     485             : 
     486             :    I stopped using the time library and used the OSL version instead, but
     487             :    it might still be useful to have this code here..
     488             : 
     489             : void ZipOutputStream::dosDateToTMDate ( tm &rTime, sal_uInt32 nDosDate)
     490             : {
     491             :     sal_uInt32 nDate = static_cast < sal_uInt32 > (nDosDate >> 16);
     492             :     rTime.tm_mday = static_cast < sal_uInt32 > ( nDate & 0x1F);
     493             :     rTime.tm_mon  = static_cast < sal_uInt32 > ( ( ( (nDate) & 0x1E0)/0x20)-1);
     494             :     rTime.tm_year = static_cast < sal_uInt32 > ( ( (nDate & 0x0FE00)/0x0200)+1980);
     495             : 
     496             :     rTime.tm_hour = static_cast < sal_uInt32 > ( (nDosDate & 0xF800)/0x800);
     497             :     rTime.tm_min  = static_cast < sal_uInt32 > ( (nDosDate & 0x7E0)/0x20);
     498             :     rTime.tm_sec  = static_cast < sal_uInt32 > ( 2 * (nDosDate & 0x1F) );
     499             : }
     500             : */
     501             : 
     502             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10