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

Generated by: LCOV version 1.10