1999-03-09 02:13:56 +00:00
|
|
|
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/*
|
|
|
|
* The contents of this file are subject to the Netscape Public License
|
|
|
|
* Version 1.0 (the "License"); you may not use this file except in
|
|
|
|
* compliance with the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/NPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is Mozilla Communicator client code,
|
|
|
|
* released March 31, 1998.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is Netscape Communications
|
|
|
|
* Corporation. Portions created by Netscape are
|
|
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
|
|
* Reserved.
|
|
|
|
*
|
|
|
|
* Contributors:
|
|
|
|
* Daniel Veditz <dveditz@netscape.com>
|
1999-06-23 06:16:28 +00:00
|
|
|
* Samir Gehani <sgehani@netscape.com>
|
1999-03-09 02:13:56 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This module implements a simple archive extractor for the PKZIP format.
|
1999-05-19 03:21:03 +00:00
|
|
|
*
|
|
|
|
* The underlying nsZipArchive is NOT thread-safe. Do not pass references
|
|
|
|
* or pointers to it across thread boundaries.
|
1999-03-09 02:13:56 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
#ifndef STANDALONE
|
1999-06-03 18:15:53 +00:00
|
|
|
#include "nscore.h"
|
|
|
|
#include "prmem.h"
|
|
|
|
#include "prio.h"
|
|
|
|
#include "plstr.h"
|
|
|
|
#include "xp_regexp.h"
|
|
|
|
#define ZFILE_CREATE PR_WRONLY | PR_CREATE_FILE
|
|
|
|
#define READTYPE PRInt32
|
1999-05-19 03:21:03 +00:00
|
|
|
#else
|
1999-08-06 07:58:15 +00:00
|
|
|
#include "windows.h"
|
1999-06-03 18:15:53 +00:00
|
|
|
#include "zipstub.h"
|
|
|
|
#undef NETSCAPE // undoes prtypes damage in zlib.h
|
|
|
|
#define ZFILE_CREATE "wb"
|
|
|
|
#define READTYPE PRUint32
|
1999-05-19 03:21:03 +00:00
|
|
|
#endif /* STANDALONE */
|
1999-04-28 00:34:30 +00:00
|
|
|
|
1999-03-09 02:13:56 +00:00
|
|
|
#include "zlib.h"
|
|
|
|
|
|
|
|
#include "zipfile.h"
|
|
|
|
#include "zipstruct.h"
|
|
|
|
#include "nsZipArchive.h"
|
|
|
|
|
|
|
|
|
|
|
|
static PRUint16 xtoint(unsigned char *ii);
|
|
|
|
static PRUint32 xtolong(unsigned char *ll);
|
|
|
|
|
|
|
|
|
|
|
|
/*---------------------------------------------
|
|
|
|
* C API wrapper for nsZipArchive
|
|
|
|
*--------------------------------------------*/
|
|
|
|
|
1999-06-23 06:16:28 +00:00
|
|
|
#ifdef STANDALONE
|
|
|
|
|
1999-03-09 02:13:56 +00:00
|
|
|
/**
|
|
|
|
* ZIP_OpenArchive
|
|
|
|
*
|
|
|
|
* opens the named zip/jar archive and returns a handle that
|
|
|
|
* represents the archive in other ZIP_ calls.
|
|
|
|
*
|
|
|
|
* @param zipname archive filename
|
|
|
|
* @param hZip receives handle if archive opened OK
|
|
|
|
* @return status code
|
|
|
|
*/
|
|
|
|
PR_PUBLIC_API(PRInt32) ZIP_OpenArchive( const char * zipname, void** hZip )
|
|
|
|
{
|
|
|
|
PRInt32 status;
|
|
|
|
|
|
|
|
/*--- error check args ---*/
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( hZip == 0 )
|
1999-03-09 02:13:56 +00:00
|
|
|
return ZIP_ERR_PARAM;
|
|
|
|
|
1999-04-28 00:34:30 +00:00
|
|
|
/*--- NULL output to prevent use by bozos who don't check errors ---*/
|
1999-05-19 03:21:03 +00:00
|
|
|
*hZip = 0;
|
1999-03-09 02:13:56 +00:00
|
|
|
|
|
|
|
/*--- create and open the archive ---*/
|
|
|
|
nsZipArchive* zip = new nsZipArchive();
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( zip == 0 )
|
1999-03-09 02:13:56 +00:00
|
|
|
return ZIP_ERR_MEMORY;
|
|
|
|
|
|
|
|
status = zip->OpenArchive(zipname);
|
|
|
|
|
|
|
|
if ( status == ZIP_OK )
|
|
|
|
*hZip = NS_STATIC_CAST(void*,zip);
|
1999-04-28 00:34:30 +00:00
|
|
|
else
|
|
|
|
delete zip;
|
1999-03-09 02:13:56 +00:00
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ZIP_CloseArchive
|
|
|
|
*
|
|
|
|
* closes zip archive and frees memory
|
|
|
|
* @param hZip handle obtained from ZIP_OpenArchive
|
|
|
|
* @return status code
|
|
|
|
*/
|
|
|
|
PR_PUBLIC_API(PRInt32) ZIP_CloseArchive( void** hZip )
|
|
|
|
{
|
|
|
|
/*--- error check args ---*/
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( hZip == 0 || *hZip == 0 )
|
1999-03-09 02:13:56 +00:00
|
|
|
return ZIP_ERR_PARAM;
|
|
|
|
|
|
|
|
nsZipArchive* zip = NS_STATIC_CAST(nsZipArchive*,*hZip);
|
|
|
|
if ( zip->kMagic != ZIP_MAGIC )
|
|
|
|
return ZIP_ERR_PARAM; /* whatever it is isn't one of ours! */
|
|
|
|
|
|
|
|
/*--- close the archive ---*/
|
1999-05-19 03:21:03 +00:00
|
|
|
*hZip = 0;
|
1999-03-09 02:13:56 +00:00
|
|
|
delete zip;
|
|
|
|
|
|
|
|
return ZIP_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ZIP_ExtractFile
|
|
|
|
*
|
|
|
|
* extracts named file from an opened archive
|
|
|
|
*
|
|
|
|
* @param hZip handle obtained from ZIP_OpenArchive
|
|
|
|
* @param filename name of file in archive
|
|
|
|
* @param outname filename to extract to
|
|
|
|
*/
|
|
|
|
PR_PUBLIC_API(PRInt32) ZIP_ExtractFile( void* hZip, const char * filename, const char * outname )
|
|
|
|
{
|
|
|
|
/*--- error check args ---*/
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( hZip == 0 )
|
1999-03-09 02:13:56 +00:00
|
|
|
return ZIP_ERR_PARAM;
|
|
|
|
|
|
|
|
nsZipArchive* zip = NS_STATIC_CAST(nsZipArchive*,hZip);
|
|
|
|
if ( zip->kMagic != ZIP_MAGIC )
|
|
|
|
return ZIP_ERR_PARAM; /* whatever it is isn't one of ours! */
|
|
|
|
|
|
|
|
/*--- extract the file ---*/
|
|
|
|
return zip->ExtractFile( filename, outname );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
1999-04-28 00:34:30 +00:00
|
|
|
/**
|
|
|
|
* ZIP_FindInit
|
|
|
|
*
|
|
|
|
* Initializes an enumeration of files in the archive
|
|
|
|
*
|
|
|
|
* @param hZip handle obtained from ZIP_OpenArchive
|
|
|
|
* @param pattern regexp to match files in archive, the usual shell expressions.
|
|
|
|
* NULL pattern also matches all files, faster than "*"
|
|
|
|
*/
|
1999-05-19 03:21:03 +00:00
|
|
|
PR_PUBLIC_API(void*) ZIP_FindInit( void* hZip, const char * pattern )
|
1999-04-28 00:34:30 +00:00
|
|
|
{
|
|
|
|
/*--- error check args ---*/
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( hZip == 0 )
|
|
|
|
return 0;
|
1999-04-28 00:34:30 +00:00
|
|
|
|
|
|
|
nsZipArchive* zip = NS_STATIC_CAST(nsZipArchive*,hZip);
|
|
|
|
if ( zip->kMagic != ZIP_MAGIC )
|
1999-05-19 03:21:03 +00:00
|
|
|
return 0; /* whatever it is isn't one of ours! */
|
1999-04-28 00:34:30 +00:00
|
|
|
|
|
|
|
/*--- initialize the pattern search ---*/
|
|
|
|
return zip->FindInit( pattern );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
1999-06-23 06:16:28 +00:00
|
|
|
* ZIP_FindNext
|
1999-04-28 00:34:30 +00:00
|
|
|
*
|
|
|
|
* Puts the next name in the passed buffer. Returns ZIP_ERR_SMALLBUF when
|
|
|
|
* the name is too large for the buffer, and ZIP_ERR_FNF when there are no
|
|
|
|
* more files that match the pattern
|
|
|
|
*
|
1999-05-19 03:21:03 +00:00
|
|
|
* @param hFind handle obtained from ZIP_FindInit
|
1999-04-28 00:34:30 +00:00
|
|
|
* @param outbuf buffer to receive next filename
|
|
|
|
* @param bufsize size of allocated buffer
|
|
|
|
*/
|
1999-05-19 03:21:03 +00:00
|
|
|
PR_PUBLIC_API(PRInt32) ZIP_FindNext( void* hFind, char * outbuf, PRUint16 bufsize )
|
1999-04-28 00:34:30 +00:00
|
|
|
{
|
1999-06-23 06:16:28 +00:00
|
|
|
PRInt32 status;
|
|
|
|
|
1999-04-28 00:34:30 +00:00
|
|
|
/*--- error check args ---*/
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( hFind == 0 )
|
1999-04-28 00:34:30 +00:00
|
|
|
return ZIP_ERR_PARAM;
|
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
nsZipFind* find = NS_STATIC_CAST(nsZipFind*,hFind);
|
|
|
|
if ( find->kMagic != ZIPFIND_MAGIC )
|
1999-04-28 00:34:30 +00:00
|
|
|
return ZIP_ERR_PARAM; /* whatever it is isn't one of ours! */
|
|
|
|
|
|
|
|
/*--- return next filename file ---*/
|
1999-06-23 06:16:28 +00:00
|
|
|
nsZipItem* item;
|
|
|
|
status = find->mArchive->FindNext( find, &item );
|
|
|
|
if ( status == ZIP_OK )
|
|
|
|
{
|
|
|
|
if ( bufsize > item->namelen )
|
|
|
|
{
|
|
|
|
PL_strcpy( outbuf, item->name );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
status = ZIP_ERR_SMALLBUF;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
1999-04-28 00:34:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
/**
|
|
|
|
* ZIP_FindFree
|
|
|
|
*
|
|
|
|
* Releases allocated memory associated with the find token
|
|
|
|
*
|
|
|
|
* @param hFind handle obtained from ZIP_FindInit
|
|
|
|
*/
|
|
|
|
PR_PUBLIC_API(PRInt32) ZIP_FindFree( void* hFind )
|
|
|
|
{
|
|
|
|
/*--- error check args ---*/
|
|
|
|
if ( hFind == 0 )
|
|
|
|
return ZIP_ERR_PARAM;
|
|
|
|
|
|
|
|
nsZipFind* find = NS_STATIC_CAST(nsZipFind*,hFind);
|
|
|
|
if ( find->kMagic != ZIPFIND_MAGIC )
|
|
|
|
return ZIP_ERR_PARAM; /* whatever it is isn't one of ours! */
|
|
|
|
|
|
|
|
/* free the find structure */
|
|
|
|
return find->mArchive->FindFree( find );
|
|
|
|
}
|
|
|
|
|
1999-06-23 06:16:28 +00:00
|
|
|
#endif /* STANDALONE */
|
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
|
1999-03-09 02:13:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//***********************************************************
|
|
|
|
// nsZipArchive -- public methods
|
|
|
|
//***********************************************************
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------
|
|
|
|
// nsZipArchive::OpenArchive
|
|
|
|
//---------------------------------------------
|
|
|
|
PRInt32 nsZipArchive::OpenArchive( const char * aArchiveName )
|
|
|
|
{
|
|
|
|
//-- validate arguments
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( aArchiveName == 0 || *aArchiveName == '\0')
|
1999-03-09 02:13:56 +00:00
|
|
|
return ZIP_ERR_PARAM;
|
|
|
|
|
|
|
|
//-- not allowed to do two opens on the same object!
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( mFd != 0 )
|
1999-03-09 02:13:56 +00:00
|
|
|
return ZIP_ERR_GENERAL;
|
|
|
|
|
|
|
|
//-- open the physical file
|
|
|
|
mFd = PR_Open( aArchiveName, PR_RDONLY, 0 );
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( mFd == 0 )
|
1999-03-09 02:13:56 +00:00
|
|
|
return ZIP_ERR_DISK;
|
|
|
|
|
|
|
|
//-- get table of contents for archive
|
|
|
|
return BuildFileList();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-04-28 00:34:30 +00:00
|
|
|
|
1999-03-09 02:13:56 +00:00
|
|
|
//---------------------------------------------
|
|
|
|
// nsZipArchive::ExtractFile
|
|
|
|
//---------------------------------------------
|
|
|
|
PRInt32 nsZipArchive::ExtractFile(const char* aFilename, const char* aOutname)
|
|
|
|
{
|
|
|
|
//-- sanity check arguments
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( aFilename == 0 || aOutname == 0 )
|
1999-03-09 02:13:56 +00:00
|
|
|
return ZIP_ERR_PARAM;
|
|
|
|
|
|
|
|
//-- find file information
|
|
|
|
const nsZipItem* item = GetFileItem( aFilename );
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( item == 0 )
|
1999-03-09 02:13:56 +00:00
|
|
|
return ZIP_ERR_FNF;
|
|
|
|
|
|
|
|
//-- extract the file using appropriate method
|
|
|
|
switch( item->compression )
|
|
|
|
{
|
|
|
|
case STORED:
|
|
|
|
return CopyItemToDisk( item, aOutname );
|
|
|
|
|
|
|
|
case DEFLATED:
|
|
|
|
return InflateItemToDisk( item, aOutname );
|
|
|
|
|
|
|
|
default:
|
|
|
|
//-- unsupported compression type
|
|
|
|
return ZIP_ERR_UNSUPPORTED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
1999-04-28 00:34:30 +00:00
|
|
|
//---------------------------------------------
|
|
|
|
// nsZipArchive::FindInit
|
|
|
|
//---------------------------------------------
|
1999-05-19 03:21:03 +00:00
|
|
|
nsZipFind* nsZipArchive::FindInit( const char * aPattern )
|
1999-04-28 00:34:30 +00:00
|
|
|
{
|
1999-05-19 03:21:03 +00:00
|
|
|
PRBool regExp;
|
|
|
|
char* pattern = 0;
|
|
|
|
|
1999-04-28 00:34:30 +00:00
|
|
|
// validate the pattern
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( aPattern )
|
|
|
|
{
|
1999-04-28 00:34:30 +00:00
|
|
|
switch (XP_RegExpValid( (char*)aPattern ))
|
|
|
|
{
|
|
|
|
case INVALID_SXP:
|
1999-05-19 03:21:03 +00:00
|
|
|
return 0;
|
1999-04-28 00:34:30 +00:00
|
|
|
|
|
|
|
case NON_SXP:
|
1999-05-19 03:21:03 +00:00
|
|
|
regExp = PR_FALSE;
|
1999-04-28 00:34:30 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case VALID_SXP:
|
1999-05-19 03:21:03 +00:00
|
|
|
regExp = PR_TRUE;
|
1999-04-28 00:34:30 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
1999-05-19 03:21:03 +00:00
|
|
|
// undocumented return value from RegExpValid!
|
1999-04-28 00:34:30 +00:00
|
|
|
PR_ASSERT( PR_FALSE );
|
1999-05-19 03:21:03 +00:00
|
|
|
return 0;
|
1999-04-28 00:34:30 +00:00
|
|
|
}
|
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
pattern = PL_strdup( aPattern );
|
|
|
|
if ( !pattern )
|
|
|
|
return 0;
|
1999-04-28 00:34:30 +00:00
|
|
|
}
|
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
return new nsZipFind( this, pattern, regExp );
|
1999-04-28 00:34:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------
|
|
|
|
// nsZipArchive::FindNext
|
|
|
|
//---------------------------------------------
|
1999-06-23 06:16:28 +00:00
|
|
|
PRInt32 nsZipArchive::FindNext( nsZipFind* aFind, nsZipItem** aResult)
|
1999-04-28 00:34:30 +00:00
|
|
|
{
|
|
|
|
PRInt32 status;
|
|
|
|
PRBool found = PR_FALSE;
|
1999-05-19 03:21:03 +00:00
|
|
|
PRUint16 slot = aFind->mSlot;
|
|
|
|
nsZipItem* item = aFind->mItem;
|
|
|
|
|
1999-06-23 06:16:28 +00:00
|
|
|
if ( aFind->mArchive != this )
|
1999-05-19 03:21:03 +00:00
|
|
|
return ZIP_ERR_PARAM;
|
1999-04-28 00:34:30 +00:00
|
|
|
|
|
|
|
// we start from last match, look for next
|
|
|
|
while ( slot < ZIP_TABSIZE && !found )
|
|
|
|
{
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( item != 0 )
|
1999-04-28 00:34:30 +00:00
|
|
|
item = item->next; // move to next in current chain
|
|
|
|
else
|
|
|
|
item = mFiles[slot]; // starting a new slot
|
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( item == 0 )
|
1999-04-28 00:34:30 +00:00
|
|
|
{ // no more in this chain, move to next slot
|
|
|
|
++slot;
|
|
|
|
continue;
|
|
|
|
}
|
1999-05-19 03:21:03 +00:00
|
|
|
else if ( aFind->mPattern == 0 )
|
1999-04-28 00:34:30 +00:00
|
|
|
found = PR_TRUE; // always match
|
1999-05-19 03:21:03 +00:00
|
|
|
else if ( aFind->mRegExp )
|
1999-06-10 01:58:06 +00:00
|
|
|
found = (XP_RegExpMatch( item->name, aFind->mPattern, PR_FALSE ) == MATCH);
|
1999-04-28 00:34:30 +00:00
|
|
|
else
|
1999-05-19 03:21:03 +00:00
|
|
|
found = ( PL_strcmp( item->name, aFind->mPattern ) == 0 );
|
1999-04-28 00:34:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( found )
|
|
|
|
{
|
1999-06-23 06:16:28 +00:00
|
|
|
*aResult = item;
|
|
|
|
aFind->mSlot = slot;
|
|
|
|
aFind->mItem = item;
|
1999-04-28 00:34:30 +00:00
|
|
|
status = ZIP_OK;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
status = ZIP_ERR_FNF;
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
//---------------------------------------------
|
|
|
|
// nsZipArchive::FindFree
|
|
|
|
//---------------------------------------------
|
|
|
|
PRInt32 nsZipArchive::FindFree( nsZipFind* aFind )
|
|
|
|
{
|
|
|
|
if ( aFind->mArchive != this )
|
|
|
|
return ZIP_ERR_PARAM;
|
|
|
|
|
|
|
|
delete aFind;
|
|
|
|
return ZIP_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
1999-04-28 00:34:30 +00:00
|
|
|
|
1999-03-09 02:13:56 +00:00
|
|
|
//***********************************************************
|
|
|
|
// nsZipArchive -- private implementation
|
|
|
|
//***********************************************************
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------
|
|
|
|
// nsZipArchive::BuildFileList
|
|
|
|
//---------------------------------------------
|
|
|
|
PRInt32 nsZipArchive::BuildFileList()
|
|
|
|
{
|
|
|
|
PRInt32 status = ZIP_OK;
|
|
|
|
PRUint32 sig = 0L;
|
|
|
|
PRUint32 namelen, extralen;
|
1999-05-19 03:21:03 +00:00
|
|
|
PRUint32 hash;
|
1999-03-09 02:13:56 +00:00
|
|
|
|
|
|
|
ZipLocal Local;
|
|
|
|
|
|
|
|
nsZipItem* item;
|
|
|
|
|
1999-04-28 00:34:30 +00:00
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
// read the local file headers.
|
|
|
|
//
|
|
|
|
// What we *should* be doing is reading the central directory at the end,
|
|
|
|
// all in one place. We'll have to change this eventually since the local
|
|
|
|
// headers don't have the mode information we need for Unix files.
|
|
|
|
//-----------------------------------------------------------------------
|
1999-03-09 02:13:56 +00:00
|
|
|
PRInt32 pos = 0L;
|
|
|
|
while ( status == ZIP_OK )
|
|
|
|
{
|
1999-06-16 00:24:37 +00:00
|
|
|
#ifndef STANDALONE
|
1999-03-09 02:13:56 +00:00
|
|
|
if ( PR_Seek( mFd, pos, PR_SEEK_SET ) != (PRInt32)pos )
|
1999-06-16 00:24:37 +00:00
|
|
|
#else
|
|
|
|
// For standalone, PR_Seek() is stubbed with fseek(), which returns 0
|
|
|
|
// if successfull, otherwise a non-zero.
|
|
|
|
if ( PR_Seek( mFd, pos, PR_SEEK_SET ) != 0 )
|
|
|
|
#endif
|
1999-03-09 02:13:56 +00:00
|
|
|
{
|
|
|
|
//-- couldn't seek to next position
|
|
|
|
status = ZIP_ERR_CORRUPT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( PR_Read( mFd, (char*)&Local, sizeof(ZipLocal) ) != sizeof(ZipLocal) )
|
|
|
|
{
|
|
|
|
//-- file ends prematurely
|
|
|
|
status = ZIP_ERR_CORRUPT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-- make sure we're processing a local header
|
|
|
|
sig = xtolong( Local.signature );
|
|
|
|
if ( sig == CENTRALSIG )
|
|
|
|
{
|
|
|
|
// we're onto the next section
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if ( sig != LOCALSIG )
|
|
|
|
{
|
|
|
|
//-- otherwise expected to find a local header
|
|
|
|
status = ZIP_ERR_CORRUPT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
namelen = xtoint( Local.filename_len );
|
|
|
|
extralen = xtoint( Local.extrafield_len );
|
|
|
|
|
|
|
|
item = new nsZipItem();
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( item != 0 )
|
1999-03-09 02:13:56 +00:00
|
|
|
{
|
|
|
|
item->name = new char[namelen+1];
|
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( item->name != 0 )
|
1999-03-09 02:13:56 +00:00
|
|
|
{
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( PR_Read( mFd, item->name, namelen ) == (READTYPE)namelen )
|
1999-03-09 02:13:56 +00:00
|
|
|
{
|
|
|
|
item->name[namelen] = 0;
|
1999-04-28 00:34:30 +00:00
|
|
|
item->namelen = namelen;
|
1999-03-09 02:13:56 +00:00
|
|
|
|
|
|
|
item->headerloc = pos;
|
|
|
|
item->offset = pos + sizeof(ZipLocal) + namelen + extralen;
|
|
|
|
item->compression = xtoint( Local.method );
|
|
|
|
item->size = xtolong( Local.size );
|
|
|
|
item->realsize = xtolong( Local.orglen );
|
|
|
|
item->crc32 = xtolong( Local.crc32 );
|
|
|
|
|
|
|
|
//-- add item to file table
|
|
|
|
hash = HashName( item->name );
|
|
|
|
item->next = mFiles[hash];
|
|
|
|
mFiles[hash] = item;
|
|
|
|
|
|
|
|
pos = item->offset + item->size;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//-- file is truncated
|
|
|
|
status = ZIP_ERR_CORRUPT;
|
|
|
|
delete item;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//-- couldn't allocate for the filename
|
|
|
|
status = ZIP_ERR_MEMORY;
|
|
|
|
delete item;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//-- couldn't create a nsZipItem
|
|
|
|
status = ZIP_ERR_MEMORY;
|
|
|
|
}
|
|
|
|
} /* while reading local headers */
|
|
|
|
|
|
|
|
//-------------------------------------------------------
|
|
|
|
// we don't care about the rest of the file (until we
|
1999-04-28 00:34:30 +00:00
|
|
|
// fix this to read the central directory instead)
|
1999-03-09 02:13:56 +00:00
|
|
|
//-------------------------------------------------------
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------
|
|
|
|
// nsZipArchive::CopyItemToDisk
|
|
|
|
//---------------------------------------------
|
|
|
|
PRInt32 nsZipArchive::CopyItemToDisk( const nsZipItem* aItem, const char* aOutname )
|
|
|
|
{
|
|
|
|
PRInt32 status = ZIP_OK;
|
|
|
|
PRUint32 chunk, pos, size;
|
1999-05-19 03:21:03 +00:00
|
|
|
PRFileDesc* fOut = 0;
|
1999-03-09 02:13:56 +00:00
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
PR_ASSERT( aItem != 0 && aOutname != 0 );
|
1999-03-09 02:13:56 +00:00
|
|
|
|
|
|
|
char* buf = (char*)PR_Malloc(ZIP_BUFLEN);
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( buf == 0 )
|
1999-03-09 02:13:56 +00:00
|
|
|
return ZIP_ERR_MEMORY;
|
|
|
|
|
|
|
|
//-- find start of file in archive
|
1999-08-05 22:08:29 +00:00
|
|
|
|
|
|
|
#ifndef STANDALONE
|
1999-03-09 02:13:56 +00:00
|
|
|
if ( PR_Seek( mFd, aItem->offset, PR_SEEK_SET ) != (PRInt32)aItem->offset )
|
1999-08-05 22:08:29 +00:00
|
|
|
#else
|
|
|
|
// For standalone, PR_Seek() is stubbed with fseek(), which returns 0
|
|
|
|
// if successfull, otherwise a non-zero.
|
|
|
|
if ( PR_Seek( mFd, aItem->offset, PR_SEEK_SET ) != 0)
|
|
|
|
#endif
|
1999-03-09 02:13:56 +00:00
|
|
|
{
|
|
|
|
status = ZIP_ERR_CORRUPT;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-- open output file
|
1999-05-19 03:21:03 +00:00
|
|
|
fOut = PR_Open( aOutname, ZFILE_CREATE , 0644);
|
|
|
|
if ( fOut == 0 )
|
1999-03-09 02:13:56 +00:00
|
|
|
{
|
|
|
|
status = ZIP_ERR_DISK;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-- copy chunks until file is done
|
|
|
|
size = aItem->size;
|
|
|
|
for ( pos=0; pos < size; pos += chunk )
|
|
|
|
{
|
|
|
|
chunk = (pos+ZIP_BUFLEN <= size) ? ZIP_BUFLEN : size - pos;
|
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( PR_Read( mFd, buf, chunk ) != (READTYPE)chunk )
|
1999-03-09 02:13:56 +00:00
|
|
|
{
|
|
|
|
//-- unexpected end of data in archive
|
|
|
|
status = ZIP_ERR_CORRUPT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( PR_Write( fOut, buf, chunk ) < (READTYPE)chunk )
|
1999-03-09 02:13:56 +00:00
|
|
|
{
|
|
|
|
//-- Couldn't write all the data (disk full?)
|
|
|
|
status = ZIP_ERR_DISK;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( fOut != 0 )
|
1999-03-09 02:13:56 +00:00
|
|
|
PR_Close( fOut );
|
|
|
|
|
|
|
|
PR_FREEIF( buf );
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------
|
|
|
|
// nsZipArchive::GetFileItem
|
|
|
|
//---------------------------------------------
|
|
|
|
const nsZipItem* nsZipArchive::GetFileItem( const char * aFilename )
|
|
|
|
{
|
1999-05-19 03:21:03 +00:00
|
|
|
PR_ASSERT( aFilename != 0 );
|
1999-03-09 02:13:56 +00:00
|
|
|
|
|
|
|
nsZipItem* item = mFiles[ HashName(aFilename) ];
|
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
for ( ; item != 0; item = item->next )
|
1999-03-09 02:13:56 +00:00
|
|
|
{
|
|
|
|
if ( 0 == PL_strcmp( aFilename, item->name ) )
|
|
|
|
break; //-- found it
|
|
|
|
}
|
|
|
|
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------
|
|
|
|
// nsZipArchive::HashName
|
|
|
|
//---------------------------------------------
|
1999-05-19 03:21:03 +00:00
|
|
|
PRUint32 nsZipArchive::HashName( const char* aName )
|
1999-03-09 02:13:56 +00:00
|
|
|
{
|
1999-05-19 03:21:03 +00:00
|
|
|
PRUint32 val = 0;
|
1999-03-09 02:13:56 +00:00
|
|
|
PRUint8* c;
|
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
PR_ASSERT( aName != 0 );
|
1999-03-09 02:13:56 +00:00
|
|
|
|
|
|
|
for ( c = (PRUint8*)aName; *c != 0; c++ ) {
|
|
|
|
val = val*37 + *c;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (val % ZIP_TABSIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------
|
|
|
|
// nsZipArchive::InflateItemToDisk
|
|
|
|
//---------------------------------------------
|
|
|
|
PRInt32 nsZipArchive::InflateItemToDisk( const nsZipItem* aItem, const char* aOutname )
|
|
|
|
{
|
|
|
|
PRInt32 status = ZIP_OK;
|
|
|
|
PRUint32 chunk, inpos, outpos, size;
|
1999-05-19 03:21:03 +00:00
|
|
|
PRFileDesc* fOut = 0;
|
1999-03-09 02:13:56 +00:00
|
|
|
z_stream zs;
|
|
|
|
int zerr;
|
|
|
|
PRBool bInflating = PR_FALSE;
|
1999-06-29 23:23:23 +00:00
|
|
|
PRBool bRead;
|
|
|
|
PRBool bWrote;
|
1999-03-09 02:13:56 +00:00
|
|
|
|
1999-08-06 07:58:15 +00:00
|
|
|
#ifdef STANDALONE
|
|
|
|
MSG msg;
|
|
|
|
#endif /* STANDALONE */
|
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
PR_ASSERT( aItem != 0 && aOutname != 0 );
|
1999-03-09 02:13:56 +00:00
|
|
|
|
|
|
|
//-- allocate deflation buffers
|
|
|
|
Bytef *inbuf = (Bytef*)PR_Malloc(ZIP_BUFLEN);
|
|
|
|
Bytef *outbuf = (Bytef*)PR_Malloc(ZIP_BUFLEN);
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( inbuf == 0 || outbuf == 0 )
|
1999-03-09 02:13:56 +00:00
|
|
|
{
|
|
|
|
status = ZIP_ERR_MEMORY;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-- find start of file in archive
|
1999-08-05 22:08:29 +00:00
|
|
|
#ifndef STANDALONE
|
1999-03-09 02:13:56 +00:00
|
|
|
if ( PR_Seek( mFd, aItem->offset, PR_SEEK_SET ) != (PRInt32)aItem->offset )
|
1999-08-05 22:08:29 +00:00
|
|
|
#else
|
|
|
|
// For standalone, PR_Seek() is stubbed with fseek(), which returns 0
|
|
|
|
// if successfull, otherwise a non-zero.
|
|
|
|
if ( PR_Seek( mFd, aItem->offset, PR_SEEK_SET ) != 0)
|
|
|
|
#endif
|
1999-03-09 02:13:56 +00:00
|
|
|
{
|
|
|
|
status = ZIP_ERR_CORRUPT;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-- open output file
|
1999-05-19 03:21:03 +00:00
|
|
|
fOut = PR_Open( aOutname, ZFILE_CREATE, 0644);
|
|
|
|
if ( fOut == 0 )
|
1999-03-09 02:13:56 +00:00
|
|
|
{
|
|
|
|
status = ZIP_ERR_DISK;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-- set up the inflate
|
|
|
|
memset( &zs, 0, sizeof(zs) );
|
|
|
|
zerr = inflateInit2( &zs, -MAX_WBITS );
|
|
|
|
if ( zerr != Z_OK )
|
|
|
|
{
|
|
|
|
status = ZIP_ERR_GENERAL;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
bInflating = PR_TRUE;
|
|
|
|
|
|
|
|
|
|
|
|
//-- inflate loop
|
|
|
|
size = aItem->size;
|
|
|
|
outpos = inpos = 0;
|
|
|
|
zs.next_out = outbuf;
|
|
|
|
zs.avail_out = ZIP_BUFLEN;
|
|
|
|
while ( zerr == Z_OK )
|
|
|
|
{
|
1999-06-29 23:23:23 +00:00
|
|
|
bRead = PR_FALSE;
|
|
|
|
bWrote = PR_FALSE;
|
|
|
|
|
1999-03-09 02:13:56 +00:00
|
|
|
if ( zs.avail_in == 0 && zs.total_in < size )
|
|
|
|
{
|
|
|
|
//-- no data to inflate yet still more in file:
|
|
|
|
//-- read another chunk of compressed data
|
|
|
|
|
|
|
|
inpos = zs.total_in; // input position
|
|
|
|
chunk = ( inpos + ZIP_BUFLEN <= size ) ? ZIP_BUFLEN : size - inpos;
|
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( PR_Read( mFd, inbuf, chunk ) != (READTYPE)chunk )
|
1999-03-09 02:13:56 +00:00
|
|
|
{
|
|
|
|
//-- unexpected end of data
|
|
|
|
status = ZIP_ERR_CORRUPT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
zs.next_in = inbuf;
|
1999-06-29 23:23:23 +00:00
|
|
|
zs.avail_in = chunk;
|
|
|
|
bRead = PR_TRUE;
|
1999-03-09 02:13:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( zs.avail_out == 0 )
|
|
|
|
{
|
|
|
|
//-- write inflated buffer to disk and make space
|
|
|
|
if ( PR_Write( fOut, outbuf, ZIP_BUFLEN ) < ZIP_BUFLEN )
|
|
|
|
{
|
|
|
|
//-- Couldn't write all the data (disk full?)
|
|
|
|
status = ZIP_ERR_DISK;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
outpos = zs.total_out;
|
|
|
|
|
|
|
|
zs.next_out = outbuf;
|
1999-06-29 23:23:23 +00:00
|
|
|
zs.avail_out = ZIP_BUFLEN;
|
|
|
|
bWrote = PR_TRUE;
|
1999-03-09 02:13:56 +00:00
|
|
|
}
|
|
|
|
|
1999-06-29 23:23:23 +00:00
|
|
|
if(bRead || bWrote)
|
|
|
|
zerr = inflate( &zs, Z_PARTIAL_FLUSH );
|
|
|
|
else
|
|
|
|
zerr = Z_STREAM_END;
|
1999-03-09 02:13:56 +00:00
|
|
|
|
1999-08-06 07:58:15 +00:00
|
|
|
#ifdef STANDALONE
|
|
|
|
while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
|
|
|
|
{
|
|
|
|
TranslateMessage(&msg);
|
|
|
|
DispatchMessage(&msg);
|
|
|
|
}
|
|
|
|
#endif /* STANDALONE */
|
1999-03-09 02:13:56 +00:00
|
|
|
} // while
|
|
|
|
|
|
|
|
//-- write last inflated bit to disk
|
1999-03-09 23:06:11 +00:00
|
|
|
if ( zerr == Z_STREAM_END && outpos < zs.total_out )
|
1999-03-09 02:13:56 +00:00
|
|
|
{
|
|
|
|
chunk = zs.total_out - outpos;
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( PR_Write( fOut, outbuf, chunk ) < (READTYPE)chunk )
|
1999-03-09 02:13:56 +00:00
|
|
|
{
|
|
|
|
status = ZIP_ERR_DISK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//-- convert zlib error to return value
|
|
|
|
if ( status == ZIP_OK && zerr != Z_OK && zerr != Z_STREAM_END )
|
|
|
|
{
|
|
|
|
status = (zerr == Z_MEM_ERROR) ? ZIP_ERR_MEMORY : ZIP_ERR_CORRUPT;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-- if found no errors make sure we've converted the whole thing
|
|
|
|
PR_ASSERT( status != ZIP_OK || zs.total_in == aItem->size );
|
|
|
|
PR_ASSERT( status != ZIP_OK || zs.total_out == aItem->realsize );
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if ( bInflating )
|
|
|
|
{
|
|
|
|
//-- free zlib internal state
|
|
|
|
inflateEnd( &zs );
|
|
|
|
}
|
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( fOut != 0 )
|
1999-03-09 02:13:56 +00:00
|
|
|
PR_Close( fOut );
|
|
|
|
|
|
|
|
PR_FREEIF( inbuf );
|
|
|
|
PR_FREEIF( outbuf );
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//------------------------------------------
|
|
|
|
// nsZipArchive constructor and destructor
|
|
|
|
//------------------------------------------
|
|
|
|
|
|
|
|
nsZipArchive::nsZipArchive()
|
1999-05-19 03:21:03 +00:00
|
|
|
: kMagic(ZIP_MAGIC), mFd(0)
|
1999-03-09 02:13:56 +00:00
|
|
|
{
|
|
|
|
// initialize the table to NULL
|
|
|
|
for ( int i = 0; i < ZIP_TABSIZE; ++i) {
|
1999-05-19 03:21:03 +00:00
|
|
|
mFiles[i] = 0;
|
1999-03-09 02:13:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsZipArchive::~nsZipArchive()
|
|
|
|
{
|
|
|
|
// close the file if open
|
1999-05-19 03:21:03 +00:00
|
|
|
if ( mFd != 0 ) {
|
1999-03-09 02:13:56 +00:00
|
|
|
PR_Close(mFd);
|
|
|
|
}
|
|
|
|
|
|
|
|
// delete nsZipItems in table
|
|
|
|
nsZipItem* pItem;
|
|
|
|
for ( int i = 0; i < ZIP_TABSIZE; ++i)
|
|
|
|
{
|
|
|
|
pItem = mFiles[i];
|
1999-05-19 03:21:03 +00:00
|
|
|
while ( pItem != 0 )
|
1999-03-09 02:13:56 +00:00
|
|
|
{
|
|
|
|
mFiles[i] = pItem->next;
|
|
|
|
delete pItem;
|
|
|
|
pItem = mFiles[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
|
1999-03-09 02:13:56 +00:00
|
|
|
//------------------------------------------
|
|
|
|
// nsZipItem constructor and destructor
|
|
|
|
//------------------------------------------
|
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
nsZipItem::nsZipItem() : name(0),next(0) {}
|
1999-03-09 02:13:56 +00:00
|
|
|
|
|
|
|
nsZipItem::~nsZipItem()
|
|
|
|
{
|
1999-05-19 03:21:03 +00:00
|
|
|
if (name != 0 )
|
1999-03-09 02:13:56 +00:00
|
|
|
delete [] name;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
|
|
|
|
//------------------------------------------
|
|
|
|
// nsZipFind constructor and destructor
|
|
|
|
//------------------------------------------
|
|
|
|
|
|
|
|
nsZipFind::nsZipFind( nsZipArchive* aZip, char* aPattern, PRBool aRegExp )
|
|
|
|
: kMagic(ZIPFIND_MAGIC),
|
|
|
|
mArchive(aZip),
|
|
|
|
mPattern(aPattern),
|
|
|
|
mSlot(0),
|
|
|
|
mItem(0),
|
|
|
|
mRegExp(aRegExp)
|
|
|
|
{}
|
|
|
|
|
|
|
|
nsZipFind::~nsZipFind()
|
|
|
|
{
|
|
|
|
if (mPattern != 0 )
|
|
|
|
PL_strfree( mPattern );
|
|
|
|
}
|
|
|
|
|
1999-06-23 06:16:28 +00:00
|
|
|
//------------------------------------------
|
|
|
|
// nsZipFind::GetArchive
|
|
|
|
//------------------------------------------
|
|
|
|
nsZipArchive* nsZipFind::GetArchive()
|
|
|
|
{
|
|
|
|
if (!mArchive)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return mArchive;
|
|
|
|
}
|
|
|
|
|
1999-05-19 03:21:03 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
1999-03-09 02:13:56 +00:00
|
|
|
//------------------------------------------
|
1999-04-28 00:34:30 +00:00
|
|
|
// helper functions
|
1999-03-09 02:13:56 +00:00
|
|
|
//------------------------------------------
|
|
|
|
|
|
|
|
/*
|
|
|
|
* x t o i n t
|
|
|
|
*
|
|
|
|
* Converts a two byte ugly endianed integer
|
|
|
|
* to our platform's integer.
|
|
|
|
*/
|
|
|
|
static PRUint16 xtoint (unsigned char *ii)
|
|
|
|
{
|
1999-05-19 03:21:03 +00:00
|
|
|
return (PRUint16) ((ii [0]) | (ii [1] << 8));
|
1999-03-09 02:13:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* x t o l o n g
|
|
|
|
*
|
|
|
|
* Converts a four byte ugly endianed integer
|
|
|
|
* to our platform's integer.
|
|
|
|
*/
|
|
|
|
static PRUint32 xtolong (unsigned char *ll)
|
|
|
|
{
|
|
|
|
PRUint32 ret;
|
|
|
|
|
|
|
|
ret = (
|
|
|
|
(((PRUint32) ll [0]) << 0) |
|
|
|
|
(((PRUint32) ll [1]) << 8) |
|
|
|
|
(((PRUint32) ll [2]) << 16) |
|
|
|
|
(((PRUint32) ll [3]) << 24)
|
|
|
|
);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
1999-06-23 06:16:28 +00:00
|
|
|
|
|
|
|
|