Compare commits

..

1 Commits

Author SHA1 Message Date
(no author)
e47a46b213 This commit was manufactured by cvs2svn to create tag 'STABLE'.
git-svn-id: svn://10.0.0.236/tags/STABLE@39047 18797224-902f-48f8-a5cc-f745e15eee43
1999-07-13 02:46:57 +00:00
87 changed files with 6812 additions and 9037 deletions

View File

@@ -1,38 +0,0 @@
#
# The contents of this file are subject to the Netscape Public
# License Version 1.1 (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.org code.
#
# 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.
#
# Contributor(s):
#
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
DIRS = \
public \
memcache \
filecache \
mgr \
build \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@@ -1,33 +0,0 @@
#!gmake
#
# The contents of this file are subject to the Netscape Public
# License Version 1.1 (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.org code.
#
# 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.
#
# Contributor(s):
DEPTH=..\..
DIRS= \
public \
mgr \
memcache \
filecache \
build \
$(NULL)
include <$(DEPTH)\config\rules.mak>

View File

@@ -1,54 +0,0 @@
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
#
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = nkcache
LIBRARY_NAME = nkcache
IS_COMPONENT = 1
CPPSRCS = nsNetDataCacheModule.cpp
SHARED_LIBRARY_LIBS = \
$(DIST)/lib/libnkcachemgr_s.a \
$(DIST)/lib/libnkfilecache_s.a \
$(DIST)/lib/libnkmemcache_s.a \
$(DIST)/lib/libmozdbm_s.a \
$(DIST)/lib/libxpcomio_s.a \
$(NULL)
LOCAL_INCLUDES = \
-I$(DEPTH)/netwerk/cache/memcache \
-I$(DEPTH)/netwerk/cache/filecache \
-I$(DEPTH)/netwerk/cache/mgr \
$(NULL)
EXTRA_DSO_LDOPTS = \
$(MKSHLIB_FORCE_ALL) \
$(SHARED_LIBRARY_LIBS) \
$(MKSHLIB_UNFORCE_ALL) \
$(NULL)
include $(topsrcdir)/config/rules.mk
$(LIBRARY) $(SHARED_LIBRARY): $(SHARED_LIBRARY_LIBS) Makefile

View File

@@ -1,51 +0,0 @@
#!gmake
#
# The contents of this file are subject to the Netscape Public
# License Version 1.1 (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.org code.
#
# 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.
#
DEPTH=..\..\..
MODULE=nkcache
MAKE_OBJ_TYPE=DLL
DLLNAME=nkcache
DLL=.\$(OBJDIR)\$(DLLNAME).dll
CPP_OBJS= \
.\$(OBJDIR)\nsNetDataCacheModule.obj \
$(NULL)
LLIBS= \
$(DIST)\lib\nkcachemgr_s.lib \
$(DIST)\lib\nkfilecache_s.lib \
$(DIST)\lib\nkmemcache_s.lib \
$(DIST)\lib\dbm32.lib \
$(DIST)\lib\xpcom.lib \
$(LIBNSPR)
INCS = $(INCS) \
-I$(DEPTH)\netwerk\cache\memcache \
-I$(DEPTH)\netwerk\cache\filecache \
-I$(DEPTH)\netwerk\cache\mgr \
$(NULL)
include <$(DEPTH)\config\rules.mak>
install:: $(DLL)
$(MAKE_INSTALL) .\$(OBJDIR)\$(DLLNAME).dll $(DIST)\bin\components
$(MAKE_INSTALL) .\$(OBJDIR)\$(DLLNAME).lib $(DIST)\lib

View File

@@ -1,49 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
#include "nsCOMPtr.h"
#include "nsIModule.h"
#include "nscore.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsIGenericFactory.h"
#include "nsINetDataCache.h"
#include "nsINetDataCacheManager.h"
#include "nsMemCacheCID.h"
#include "nsMemCache.h"
#include "nsNetDiskCache.h"
#include "nsNetDiskCacheCID.h"
#include "nsCacheManager.h"
// Factory method to create a new nsMemCache instance. Used
// by nsNetDataCacheModule
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsMemCache, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsNetDiskCache, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsCacheManager, Init)
static nsModuleComponentInfo components[] = {
{ "Memory Cache", NS_MEM_CACHE_FACTORY_CID, NS_NETWORK_MEMORY_CACHE_PROGID, nsMemCacheConstructor },
{ "File Cache", NS_NETDISKCACHE_CID, NS_NETWORK_FILE_CACHE_PROGID, nsNetDiskCacheConstructor },
{ "Cache Manager",NS_CACHE_MANAGER_CID, NS_NETWORK_CACHE_MANAGER_PROGID,nsCacheManagerConstructor }
};
NS_IMPL_NSGETMODULE("Network Data Cache", components)

View File

@@ -1,56 +0,0 @@
#
# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (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/MPL/
#
# 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.
#
# The Initial Developer of the Original Code is Intel Corp.
# Portions created by Intel Corp. are
# Copyright (C) 1999, 1999 Intel Corp. All
# Rights Reserved.
#
# Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
# Carl Wong <carl.wong@intel.com>
#
DEPTH = ../../..
topsrcdir = @top_srcdir@
VPATH = @srcdir@
srcdir = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = nkcache
LIBRARY_NAME = nkfilecache_s
REQUIRES = nspr dbm
EXTRA_DSO_LDOPTS += -L$(DIST)/lib -lmozdbm_s
EXPORTS=nsNetDiskCacheCID.h
CPPSRCS = \
nsDBAccessor.cpp\
nsDBEnumerator.cpp \
nsNetDiskCache.cpp \
nsDiskCacheRecord.cpp \
nsDiskCacheRecordChannel.cpp \
$(NULL)
EXTRA_LIBS = $(NSPR_LIBS)
# we don't want the shared lib, but we want to force the creation of a
# static lib.
override NO_SHARED_LIB=1
override NO_STATIC_LIB=
include $(topsrcdir)/config/rules.mk

View File

@@ -1,44 +0,0 @@
#!gmake
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
DEPTH=..\..\..
include <$(DEPTH)/config/config.mak>
MODULE = nkcache
LIBRARY_NAME = nkfilecache_s
CPP_OBJS= \
.\$(OBJDIR)\nsDBAccessor.obj \
.\$(OBJDIR)\nsDBEnumerator.obj \
.\$(OBJDIR)\nsNetDiskCache.obj \
.\$(OBJDIR)\nsDiskCacheRecord.obj \
.\$(OBJDIR)\nsDiskCacheRecordChannel.obj \
$(NULL)
EXPORTS=nsNetDiskCacheCID.h
include <$(DEPTH)\config\rules.mak>
install:: $(LIBRARY)
$(MAKE_INSTALL) $(LIBRARY) $(DIST)\lib
clobber::
rm -rf $(OBJDIR)
rm -f $(DIST)\lib\$(LIBRARY_NAME).lib

View File

@@ -1,351 +0,0 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (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/MPL/
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
// FUR - Add overall description comment here
#include "nsDBAccessor.h"
#include "nscore.h"
#include "prtypes.h"
#include "plhash.h"
#include "nsCRT.h"
#include "nsAutoLock.h"
nsDBAccessor::nsDBAccessor() :
mDB(0) ,
mSessionID(0) ,
mSessionCntr(0)
{
NS_INIT_REFCNT();
}
nsDBAccessor::~nsDBAccessor()
{
printf(" ~nsDBAccessor\n") ;
Shutdown() ;
}
//
// Implement nsISupports methods
//
NS_IMPL_ISUPPORTS(nsDBAccessor, NS_GET_IID(nsIDBAccessor))
///////////////////////////////////////////////////////////
// nsIDBAccessor methods
NS_IMETHODIMP
nsDBAccessor::Init(nsIFileSpec* dbfile)
{
// FUR - lock not needed
m_Lock = PR_NewLock() ;
if(!m_Lock)
return NS_ERROR_OUT_OF_MEMORY ;
char* dbname ;
// this should cover all platforms.
dbfile->GetNativePath(&dbname) ;
// FUR - how is page size chosen ? It's worth putting a comment
// in here about the possible usefulness of tuning these parameters
HASHINFO hash_info = {
16*1024 , /* bucket size */
0 , /* fill factor */
0 , /* number of elements */
0 , /* bytes to cache */
0 , /* hash function */
0} ; /* byte order */
// FUR - lock not needed
nsAutoLock lock(m_Lock) ;
mDB = dbopen(dbname,
O_RDWR | O_CREAT ,
0600 ,
DB_HASH ,
& hash_info) ;
// FUR - does dbname have to be free'ed ?
if(!mDB)
return NS_ERROR_FAILURE ;
// set mSessionID
// FUR - Why the +1 ? (No need for key to be NUL-terminated string.)
PRUint32 len = PL_strlen(SessionKey)+1 ;
DBT db_key, db_data ;
db_key.data = NS_CONST_CAST(char*, SessionKey) ;
db_key.size = len ;
int status = (*mDB->get)(mDB, &db_key, &db_data, 0) ;
if(status == -1) {
NS_ERROR("ERROR: failed get session id in database.") ;
return NS_ERROR_FAILURE ;
}
if(status == 0) {
// get the last session id
PRInt16 *old_ID = NS_STATIC_CAST(PRInt16*, db_data.data) ;
if(*old_ID < ini_sessionID) {
NS_ERROR("ERROR: Bad Session ID in database, corrupted db.") ;
return NS_ERROR_FAILURE ;
}
// FUR - need to comment out all printfs, or turn them into PR_LOG statements
printf("found previous session, id = %d\n", *old_ID) ;
mSessionID = *old_ID + 1 ;
}
else if(status == 1) {
// must be a new db
mSessionID = ini_sessionID ;
}
db_data.data = NS_REINTERPRET_CAST(void*, &mSessionID) ;
db_data.size = sizeof(PRInt16) ;
// store the new session id
status = (*mDB->put)(mDB, &db_key, &db_data, 0) ;
if(status == 0) {
(*mDB->sync)(mDB, 0) ;
return NS_OK ;
}
else {
NS_ERROR("reset session ID failure.") ;
return NS_ERROR_FAILURE ;
}
}
NS_IMETHODIMP
nsDBAccessor::Shutdown(void)
{
if(mDB) {
(*mDB->sync)(mDB, 0) ;
(*mDB->close)(mDB) ;
mDB = nsnull ;
}
// FUR - locks not necessary
if(m_Lock)
PR_DestroyLock(m_Lock);
return NS_OK ;
}
NS_IMETHODIMP
nsDBAccessor::Get(PRInt32 aID, void** anEntry, PRUint32 *aLength)
{
if(!anEntry)
return NS_ERROR_NULL_POINTER ;
*anEntry = nsnull ;
*aLength = 0 ;
NS_ASSERTION(mDB, "no database") ;
// Lock the db
nsAutoLock lock(m_Lock) ;
DBT db_key, db_data ;
db_key.data = NS_REINTERPRET_CAST(void*, &aID) ;
db_key.size = sizeof(PRInt32) ;
int status = 0 ;
status = (*mDB->get)(mDB, &db_key, &db_data, 0) ;
if(status == 0) {
*anEntry = db_data.data ;
*aLength = db_data.size ;
return NS_OK ;
}
else if(status == 1)
return NS_OK ;
else
return NS_ERROR_FAILURE ;
}
NS_IMETHODIMP
nsDBAccessor::Put(PRInt32 aID, void* anEntry, PRUint32 aLength)
{
NS_ASSERTION(mDB, "no database") ;
// Lock the db
nsAutoLock lock(m_Lock) ;
DBT db_key, db_data ;
db_key.data = NS_REINTERPRET_CAST(void*, &aID) ;
db_key.size = sizeof(PRInt32) ;
db_data.data = anEntry ;
db_data.size = aLength ;
if(0 == (*mDB->put)(mDB, &db_key, &db_data, 0)) {
// FUR - I would avoid unnecessary sync'ing for performance's
// sake. Maybe you could limit sync to max rate of, say, once
// every few seconds by keeping track of last sync time, using PR_Now().
(*mDB->sync)(mDB, 0) ;
return NS_OK ;
}
else {
// FUR - Try to avoid using NS_ERROR unless error is unrecoverable and serious
NS_ERROR("ERROR: Failed to put anEntry into db.\n") ;
return NS_ERROR_FAILURE ;
}
}
/*
* It's more important to remove the id->metadata entry first since
* key->id mapping is just a reference
*/
NS_IMETHODIMP
nsDBAccessor::Del(PRInt32 aID, void* anEntry, PRUint32 aLength)
{
NS_ASSERTION(mDB, "no database") ;
// FUR - no locks necessary
// Lock the db
nsAutoLock lock(m_Lock) ;
DBT db_key ;
// delete recordID->metadata
db_key.data = NS_REINTERPRET_CAST(void*, &aID) ;
db_key.size = sizeof(PRInt32) ;
PRInt32 status = -1 ;
status = (*mDB->del)(mDB, &db_key, 0) ;
if(-1 == status) {
// FUR - no printf's, use PR_LOG, NS_WARNING, or NS_ASSERTION, as the situation warrants
printf(" delete error\n") ;
return NS_ERROR_FAILURE ;
}
// delete key->recordID
db_key.data = anEntry ;
db_key.size = aLength ;
status = (*mDB->del)(mDB, &db_key, 0) ;
if(-1 == status) {
// FUR - no printf's
printf(" delete error\n") ;
return NS_ERROR_FAILURE ;
}
// FUR - Defer sync ? See above
(*mDB->sync)(mDB, 0) ;
return NS_OK ;
}
NS_IMETHODIMP
nsDBAccessor::GetID(const char* key, PRUint32 length, PRInt32* aID)
{
NS_ASSERTION(mDB, "no database") ;
// Lock the db
nsAutoLock lock(m_Lock) ;
DBT db_key, db_data ;
db_key.data = NS_CONST_CAST(char*, key) ;
db_key.size = length ;
int status = (*mDB->get)(mDB, &db_key, &db_data, 0) ;
if(status == 0) {
// found recordID
*aID = *(NS_REINTERPRET_CAST(PRInt32*, db_data.data)) ;
return NS_OK ;
}
else if(status == 1) {
// create a new one
PRInt32 id = 0 ;
id = mSessionID << 16 | mSessionCntr++ ;
// add new id into mDB
db_data.data = NS_REINTERPRET_CAST(void*, &id) ;
db_data.size = sizeof(PRInt32) ;
status = (*mDB->put)(mDB, &db_key, &db_data, 0) ;
if(status != 0) {
NS_ERROR("updating db failure.") ;
return NS_ERROR_FAILURE ;
}
// FUR - defer sync ?
(*mDB->sync)(mDB, 0) ;
*aID = id ;
return NS_OK ;
}
else {
NS_ERROR("ERROR: keydb failure.") ;
return NS_ERROR_FAILURE ;
}
}
NS_IMETHODIMP
nsDBAccessor::EnumEntry(void** anEntry, PRUint32* aLength, PRBool bReset)
{
if(!anEntry)
return NS_ERROR_NULL_POINTER ;
*anEntry = nsnull ;
*aLength = 0 ;
NS_ASSERTION(mDB, "no database") ;
PRUint32 flag ;
if(bReset)
flag = R_FIRST ;
else
flag = R_NEXT ;
// Lock the db
nsAutoLock lock(m_Lock) ;
DBT db_key, db_data ;
// FUR - +1 unnecessary ?
PRUint32 len = PL_strlen(SessionKey)+1 ;
int status ;
do {
status = (*mDB->seq)(mDB, &db_key, &db_data, flag) ;
flag = R_NEXT ;
if(status == -1)
return NS_ERROR_FAILURE ;
// get next if it's a key->recordID
if(db_key.size > sizeof(PRInt32) && db_data.size == sizeof(PRInt32))
continue ;
// get next if it's a sessionID entry
if(db_key.size == len && db_data.size == sizeof(PRInt16))
continue ;
// recordID is always 32 bits long
if(db_key.size == sizeof(PRInt32))
break ;
} while(!status) ;
if (0 == status) {
*anEntry = db_data.data ;
*aLength = db_data.size ;
}
return NS_OK ;
}

View File

@@ -1,68 +0,0 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (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/MPL/
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
// FUR - Add overall description comment here
#ifndef _NSIDBACCESSOR_H_
#define _NSIDBACCESSOR_H_
#include "nsIDBAccessor.h"
#include "mcom_db.h"
// bogus string for the key of session id
// FUR - suggest "SK" instead of "^^"
static const char * const SessionKey = "^^" ;
// initial session id number
static const PRInt16 ini_sessionID = 0xff ;
class nsDBAccessor : public nsIDBAccessor
{
public:
NS_DECL_ISUPPORTS
nsDBAccessor() ;
virtual ~nsDBAccessor() ;
NS_IMETHOD Init(nsIFileSpec* dbfile) ;
NS_IMETHOD Shutdown(void) ;
NS_IMETHOD Put(PRInt32 aID, void* anEntry, PRUint32 aLength) ;
NS_IMETHOD Get(PRInt32 aID, void** anEntry, PRUint32 *aLength) ;
NS_IMETHOD Del(PRInt32 aID, void* anEntry, PRUint32 aLength) ;
NS_IMETHOD GetID(const char* key, PRUint32 length, PRInt32* aID) ;
NS_IMETHOD EnumEntry(void* *anEntry, PRUint32* aLength, PRBool bReset) ;
protected:
private:
DB * mDB ;
PRInt16 mSessionID ;
PRInt16 mSessionCntr ;
PRLock * m_Lock ;
} ;
#endif // _NSIDBACCESSOR_H_

View File

@@ -1,97 +0,0 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (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/MPL/
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
// FUR - Add overall description comment here
#include "nsDBEnumerator.h"
#include "nsDiskCacheRecord.h"
nsDBEnumerator::nsDBEnumerator(nsIDBAccessor* aDB, nsNetDiskCache* aCache) :
m_DB(aDB) ,
m_DiskCache(aCache) ,
tempEntry(0) ,
tempEntry_length(0) ,
m_CacheEntry(0) ,
bReset(PR_TRUE)
{
NS_INIT_REFCNT();
}
nsDBEnumerator::~nsDBEnumerator()
{
// printf(" ~nsDBEnumerator()\n") ;
NS_IF_RELEASE(m_CacheEntry) ;
}
//
// Implement nsISupports methods
//
NS_IMPL_ISUPPORTS(nsDBEnumerator, NS_GET_IID(nsIEnumerator))
/////////////////////////////////////////////////////////////////
// nsISimpleEnumerator methods
NS_IMETHODIMP
nsDBEnumerator::HasMoreElements(PRBool *_retval)
{
*_retval = PR_FALSE ;
m_DB->EnumEntry(&tempEntry, &tempEntry_length, bReset) ;
bReset = PR_FALSE ;
if(tempEntry && tempEntry_length != 0)
*_retval = PR_TRUE ;
return NS_OK ;
}
// this routine does not create a new item by itself
// Rather it reuses the item inside the object. So if you need to use the
// item later, you have to
// create a new item specifically, using copy constructor or some other dup
// function. And don't forget to release it after you're done
//
NS_IMETHODIMP
nsDBEnumerator::GetNext(nsISupports **_retval)
{
if(!m_CacheEntry) {
m_CacheEntry = new nsDiskCacheRecord(m_DB, m_DiskCache) ;
if(m_CacheEntry)
NS_ADDREF(m_CacheEntry) ;
else
return NS_ERROR_OUT_OF_MEMORY ;
}
if(!_retval)
return NS_ERROR_NULL_POINTER ;
*_retval = nsnull ;
nsresult rv = m_CacheEntry->RetrieveInfo(tempEntry, tempEntry_length) ;
if(NS_FAILED(rv))
return rv ;
*_retval = NS_STATIC_CAST(nsISupports*, m_CacheEntry) ;
NS_ADDREF(*_retval) ; // all good getter addref
return NS_OK ;
}

View File

@@ -1,61 +0,0 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (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/MPL/
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
// FUR - Add overall description comment here
#ifndef _NS_DBENUMERATOR_H_
#define _NS_DBENUMERATOR_H_
#include "nsISimpleEnumerator.h"
#include "nsINetDataCacheRecord.h"
#include "nsIDBAccessor.h"
#include "nsCOMPtr.h"
#include "nsNetDiskCache.h"
#include "nsDiskCacheRecord.h"
class nsCachedDiskData ; /* forward decl */
class nsDBEnumerator : public nsISimpleEnumerator {
public:
NS_DECL_ISUPPORTS
// FUR can use NS_DECL_NSISIMPLEENUMERATOR here
/* boolean HasMoreElements (); */
NS_IMETHOD HasMoreElements(PRBool *_retval) ;
/* nsISupports GetNext (); */
NS_IMETHOD GetNext(nsISupports **_retval) ;
nsDBEnumerator(nsIDBAccessor* aDB, nsNetDiskCache* aCache) ;
virtual ~nsDBEnumerator() ;
// FUR all members should be prefixed by 'm', e.g. mbReset
private:
nsCOMPtr<nsIDBAccessor> m_DB ;
nsCOMPtr<nsNetDiskCache> m_DiskCache ;
void * tempEntry ;
PRUint32 tempEntry_length ;
nsDiskCacheRecord* m_CacheEntry ;
PRBool bReset ;
};
#endif // _NS_DBENUMERATOR_H_

View File

@@ -1,451 +0,0 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (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/MPL/
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#include "nsDiskCacheRecord.h"
#include "nsINetDataDiskCache.h"
#include "nsNetDiskCacheCID.h"
#include "nsDiskCacheRecordChannel.h"
#include "nsFileStream.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsIProtocolHandler.h"
#include "nsIIOService.h"
#include "nsIAllocator.h"
#include "plstr.h"
#include "prprf.h"
#include "prmem.h"
#include "prlog.h"
#include "prtypes.h"
#include "netCore.h"
#include "nsDBAccessor.h"
#if !defined(IS_LITTLE_ENDIAN) && !defined(IS_BIG_ENDIAN)
ERROR! Must have a byte order
#endif
#ifdef IS_LITTLE_ENDIAN
#define COPY_INT32(_a,_b) memcpy(_a, _b, sizeof(int32))
#else
#define COPY_INT32(_a,_b) /* swap */ \
do { \
((char *)(_a))[0] = ((char *)(_b))[3]; \
((char *)(_a))[1] = ((char *)(_b))[2]; \
((char *)(_a))[2] = ((char *)(_b))[1]; \
((char *)(_a))[3] = ((char *)(_b))[0]; \
} while(0)
#endif
nsDiskCacheRecord::nsDiskCacheRecord(nsIDBAccessor* db, nsNetDiskCache* aCache) :
mKey(0) ,
mKeyLength(0) ,
mRecordID(0) ,
mMetaData(0) ,
mMetaDataLength(0) ,
mDB(db) ,
mInfo(0) ,
mInfoSize(0) ,
mNumChannels(0) ,
mDiskCache(aCache)
{
NS_INIT_REFCNT();
}
// mem alloced. so caller should do free() on key.
NS_IMETHODIMP
nsDiskCacheRecord::Init(const char* key, PRUint32 length)
{
NS_NewFileSpec(getter_AddRefs(mFile));
if(!mFile)
return NS_ERROR_OUT_OF_MEMORY ;
// copy key
mKeyLength = length ;
mKey = NS_STATIC_CAST(char*, nsAllocator::Alloc(mKeyLength*sizeof(char))) ;
if(!mKey)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(mKey, key, length) ;
// get RecordID
// FUR!! Another disk access ? If called from GetCachedData, ID is already known
mDB->GetID(key, length, &mRecordID) ;
// FUR - check for GetID failure
// setup the file name
nsCOMPtr<nsIFileSpec> dbFolder ;
mDiskCache->GetDiskCacheFolder(getter_AddRefs(dbFolder)) ;
nsresult rv = mFile->FromFileSpec(dbFolder) ;
if(NS_FAILED(rv))
return NS_ERROR_FAILURE ;
// dir is a hash result of mRecordID%32, hope it's enough
char filename[9], dirName[3] ;
// FUR!! - should the format string be "%.02x". How does this work !?
PR_snprintf(dirName, 3, "%.2x", (((PRUint32)mRecordID) % 32)) ;
mFile->AppendRelativeUnixPath(dirName) ;
// FUR!! - should the format string be "%.08x". How does this work !?
PR_snprintf(filename, 9, "%.8x", mRecordID) ;
mFile->AppendRelativeUnixPath(filename) ;
return NS_OK ;
}
nsDiskCacheRecord::~nsDiskCacheRecord()
{
// printf(" ~nsDiskCacheRecord()\n") ;
if(mKey)
nsAllocator::Free(mKey) ;
if(mMetaData)
nsAllocator::Free(mMetaData) ;
}
//
// Implement nsISupports methods
//
NS_IMPL_ISUPPORTS(nsDiskCacheRecord, NS_GET_IID(nsINetDataCacheRecord))
///////////////////////////////////////////////////////////////////////
// nsINetDataCacheRecord methods
// yes, mem alloced on *_retval.
NS_IMETHODIMP
nsDiskCacheRecord::GetKey(PRUint32 *length, char** _retval)
{
if(!_retval)
return NS_ERROR_NULL_POINTER ;
*length = mKeyLength ;
*_retval = NS_STATIC_CAST(char*, nsAllocator::Alloc(mKeyLength*sizeof(char))) ;
if(!*_retval)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(*_retval, mKey, mKeyLength) ;
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecord::GetRecordID(PRInt32* aRecordID)
{
*aRecordID = mRecordID ;
return NS_OK ;
}
// yes, mem alloced on *_retval.
NS_IMETHODIMP
nsDiskCacheRecord::GetMetaData(PRUint32 *length, char **_retval)
{
if(!_retval)
return NS_ERROR_NULL_POINTER ;
// always null the return value first.
*_retval = nsnull ;
*length = mMetaDataLength ;
if(mMetaDataLength) {
*_retval = NS_STATIC_CAST(char*, nsAllocator::Alloc(mMetaDataLength*sizeof(char))) ;
if(!*_retval)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(*_retval, mMetaData, mMetaDataLength) ;
}
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecord::SetMetaData(PRUint32 length, const char* data)
{
// set the mMetaData
mMetaDataLength = length ;
if(mMetaData)
nsAllocator::Free(mMetaData) ;
mMetaData = NS_STATIC_CAST(char*, nsAllocator::Alloc(mMetaDataLength*sizeof(char))) ;
if(!mMetaData) {
return NS_ERROR_OUT_OF_MEMORY ;
}
memcpy(mMetaData, data, length) ;
// Generate mInfo
nsresult rv = GenInfo() ;
if(NS_FAILED(rv))
return rv ;
// write through into mDB
rv = mDB->Put(mRecordID, mInfo, mInfoSize) ;
// FUR - mInfo leaking ?
return rv ;
}
NS_IMETHODIMP
nsDiskCacheRecord::GetStoredContentLength(PRUint32 *aStoredContentLength)
{
return mFile->GetFileSize(aStoredContentLength) ;
}
// untill nsIFileSpec::Truncate() is in, we have to do all this ugly stuff
NS_IMETHODIMP
nsDiskCacheRecord::SetStoredContentLength(PRUint32 aStoredContentLength)
{
PRUint32 len = 0 ;
nsresult rv = mFile->GetFileSize(&len) ;
if(NS_FAILED(rv))
return rv ;
if(len < aStoredContentLength)
{
NS_ERROR("Error: can not set filesize to something bigger than itself.\n") ;
return NS_ERROR_FAILURE ;
}
else
return mFile->Truncate(aStoredContentLength) ;
}
NS_IMETHODIMP
nsDiskCacheRecord::Delete(void)
{
if(mNumChannels)
return NS_ERROR_NOT_AVAILABLE ;
PRUint32 len ;
mFile->GetFileSize(&len) ;
nsFileSpec cache_file ;
nsresult rv = mFile->GetFileSpec(&cache_file) ;
if(NS_FAILED(rv))
return NS_ERROR_FAILURE ;
cache_file.Delete(PR_TRUE) ;
// updata the storage size
mDiskCache->m_StorageInUse -= len ;
rv = mDB->Del(mRecordID, mKey, mKeyLength) ;
if(NS_FAILED(rv))
return NS_ERROR_FAILURE ;
else
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecord::GetFilename(nsIFileSpec * *aFilename)
{
if(!aFilename)
return NS_ERROR_NULL_POINTER ;
*aFilename = mFile ;
NS_ADDREF(*aFilename) ;
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecord::NewChannel(nsILoadGroup *loadGroup, nsIChannel **_retval)
{
nsDiskCacheRecordChannel* channel = new nsDiskCacheRecordChannel(this, loadGroup) ;
if(!channel)
return NS_ERROR_OUT_OF_MEMORY ;
nsresult rv = channel->Init() ;
if(NS_FAILED(rv))
return rv ;
NS_ADDREF(channel) ;
*_retval = NS_STATIC_CAST(nsIChannel*, channel) ;
return NS_OK ;
}
//////////////////////////////////////////////////////////////////////////
// nsDiskCacheRecord methods
// file name is represented by a url string. I hope this would be more
// generic
nsresult
nsDiskCacheRecord::GenInfo()
{
if(mInfo)
nsAllocator::Free(mInfo) ;
char* file_url=nsnull ;
PRUint32 name_len ;
mFile->GetURLString(&file_url) ;
name_len = PL_strlen(file_url)+1 ;
mInfoSize = sizeof(PRUint32) ; // checksum for mInfoSize
mInfoSize += sizeof(PRInt32) ; // RecordID
mInfoSize += sizeof(PRUint32) ; // key length
mInfoSize += mKeyLength ; // key
mInfoSize += sizeof(PRUint32) ; // metadata length
mInfoSize += mMetaDataLength ; // metadata
mInfoSize += sizeof(PRUint32) ; // filename length
mInfoSize += name_len ; // filename
void* newInfo = nsAllocator::Alloc(mInfoSize*sizeof(char)) ;
if(!newInfo) {
return NS_ERROR_OUT_OF_MEMORY ;
}
// copy the checksum mInfoSize
char* cur_ptr = NS_STATIC_CAST(char*, newInfo) ;
COPY_INT32(cur_ptr, &mInfoSize) ;
cur_ptr += sizeof(PRUint32) ;
// copy RecordID
COPY_INT32(cur_ptr, &mRecordID) ;
cur_ptr += sizeof(PRInt32) ;
// copy key length
COPY_INT32(cur_ptr, &mKeyLength) ;
cur_ptr += sizeof(PRUint32) ;
// copy key
memcpy(cur_ptr, mKey, mKeyLength) ;
cur_ptr += mKeyLength ;
// copy metadata length
COPY_INT32(cur_ptr, &mMetaDataLength) ;
cur_ptr += sizeof(PRUint32) ;
// copy metadata
memcpy(cur_ptr, mMetaData, mMetaDataLength) ;
cur_ptr += mMetaDataLength ;
// copy file name length
COPY_INT32(cur_ptr, &name_len) ;
cur_ptr += sizeof(PRUint32) ;
// copy file name
memcpy(cur_ptr, file_url, name_len) ;
cur_ptr += name_len ;
PR_ASSERT(cur_ptr == NS_STATIC_CAST(char*, newInfo) + mInfoSize);
mInfo = newInfo ;
return NS_OK ;
}
/*
* This Method suppose to get all the info from the db record
* and set them to accroding members. the original values
* will all be overwritten. only minimal error checking is performed.
*/
NS_IMETHODIMP
nsDiskCacheRecord::RetrieveInfo(void* aInfo, PRUint32 aInfoLength)
{
// reset everything
if(mInfo) {
nsAllocator::Free(mInfo) ;
mInfo = nsnull ;
}
if(mKey) {
nsAllocator::Free(mKey) ;
mKey = nsnull ;
}
if(mMetaData) {
nsAllocator::Free(mMetaData) ;
mMetaData = nsnull ;
}
char * cur_ptr = NS_STATIC_CAST(char*, aInfo) ;
char* file_url ;
PRUint32 name_len ;
// set mInfoSize
COPY_INT32(&mInfoSize, cur_ptr) ;
cur_ptr += sizeof(PRUint32) ;
// check this at least
if(mInfoSize != aInfoLength)
return NS_ERROR_FAILURE ;
// set mRecordID
COPY_INT32(&mRecordID, cur_ptr) ;
cur_ptr += sizeof(PRInt32) ;
// set mKeyLength
COPY_INT32(&mKeyLength, cur_ptr) ;
cur_ptr += sizeof(PRUint32) ;
// set mKey
mKey = NS_STATIC_CAST(char*, nsAllocator::Alloc(mKeyLength*sizeof(char))) ;
if(!mKey)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(mKey, cur_ptr, mKeyLength) ;
cur_ptr += mKeyLength ;
PRInt32 id ;
mDB->GetID(mKey, mKeyLength, &id) ;
NS_ASSERTION(id==mRecordID, "\t ++++++ bad record, somethings wrong\n") ;
// set mMetaDataLength
COPY_INT32(&mMetaDataLength, cur_ptr) ;
cur_ptr += sizeof(PRUint32) ;
// set mMetaData
mMetaData = NS_STATIC_CAST(char*, nsAllocator::Alloc(mMetaDataLength*sizeof(char))) ;
if(!mMetaData)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(mMetaData, cur_ptr, mMetaDataLength) ;
cur_ptr += mMetaDataLength ;
// get mFile name length
COPY_INT32(&name_len, cur_ptr) ;
cur_ptr += sizeof(PRUint32) ;
// get mFile native name
file_url = NS_STATIC_CAST(char*, nsAllocator::Alloc(name_len*sizeof(char))) ;
if(!file_url)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(file_url, cur_ptr, name_len) ;
cur_ptr += name_len ;
PR_ASSERT(cur_ptr == NS_STATIC_CAST(char*, aInfo) + mInfoSize);
// create mFile if Init() isn't called
if(!mFile) {
NS_NewFileSpec(getter_AddRefs(mFile));
if(!mFile)
return NS_ERROR_OUT_OF_MEMORY ;
}
// setup mFile
mFile->SetURLString(file_url) ;
return NS_OK ;
}

View File

@@ -1,70 +0,0 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (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/MPL/
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#ifndef _NET_CACHEDDISKDATA_H_
#define _NET_CACHEDDISKDATA_H_
#include "nsINetDataCacheRecord.h"
#include "nsCOMPtr.h"
#include "nsIDBAccessor.h"
#include "prtypes.h"
#include "nsILoadGroup.h"
#include "nsIFileChannel.h"
#include "nsNetDiskCache.h"
class nsDiskCacheRecord : public nsINetDataCacheRecord
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSINETDATACACHERECORD
protected:
nsDiskCacheRecord(nsIDBAccessor* db, nsNetDiskCache* aCache) ;
virtual ~nsDiskCacheRecord() ;
NS_IMETHOD RetrieveInfo(void* aInfo, PRUint32 aInfoLength) ;
NS_IMETHOD Init(const char* key, PRUint32 length) ;
nsresult GenInfo(void) ;
private:
char* mKey ;
PRUint32 mKeyLength ;
PRInt32 mRecordID ;
char* mMetaData ;
PRUint32 mMetaDataLength ;
nsCOMPtr<nsIFileSpec> mFile ;
nsCOMPtr<nsIDBAccessor> mDB ;
void* mInfo ;
PRUint32 mInfoSize ;
PRUint32 mNumChannels ;
nsCOMPtr<nsNetDiskCache> mDiskCache ;
friend class nsDiskCacheRecordChannel ;
friend class nsDBEnumerator ;
friend class nsNetDiskCache ;
} ;
#endif // _NET_CACHEDDISKDATA_H_

View File

@@ -1,392 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (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/MPL/
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#include "nsDiskCacheRecordChannel.h"
//#include "nsFileTransport.h"
#include "nsIIOService.h"
#include "nsIServiceManager.h"
#include "nsIOutputStream.h"
static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
// This is copied from nsMemCacheChannel, We should consolidate these two.
class WriteStreamWrapper : public nsIOutputStream
{
public:
WriteStreamWrapper(nsDiskCacheRecordChannel* aChannel,
nsIOutputStream *aBaseStream) ;
virtual ~WriteStreamWrapper() ;
static nsresult
Create(nsDiskCacheRecordChannel* aChannel, nsIOutputStream *aBaseStream, nsIOutputStream* *aWrapper) ;
NS_DECL_ISUPPORTS
NS_DECL_NSIBASESTREAM
NS_DECL_NSIOUTPUTSTREAM
private:
nsDiskCacheRecordChannel* mChannel;
nsCOMPtr<nsIOutputStream> mBaseStream;
} ;
// implement nsISupports
NS_IMPL_ISUPPORTS(WriteStreamWrapper, NS_GET_IID(nsIOutputStream))
WriteStreamWrapper::WriteStreamWrapper(nsDiskCacheRecordChannel* aChannel,
nsIOutputStream *aBaseStream)
: mChannel(aChannel), mBaseStream(aBaseStream)
{
NS_INIT_REFCNT();
NS_ADDREF(mChannel);
}
WriteStreamWrapper::~WriteStreamWrapper()
{
NS_RELEASE(mChannel);
}
nsresult
WriteStreamWrapper::Create(nsDiskCacheRecordChannel*aChannel, nsIOutputStream *aBaseStream, nsIOutputStream* * aWrapper)
{
WriteStreamWrapper *wrapper = new WriteStreamWrapper(aChannel, aBaseStream);
if (!wrapper) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(wrapper);
*aWrapper = wrapper;
return NS_OK;
}
NS_IMETHODIMP
WriteStreamWrapper::Write(const char *aBuffer, PRUint32 aCount, PRUint32 *aNumWritten)
{
*aNumWritten = 0;
nsresult rv = mBaseStream->Write(aBuffer, aCount, aNumWritten);
mChannel->NotifyStorageInUse(*aNumWritten);
return rv;
}
NS_IMETHODIMP
WriteStreamWrapper::Flush()
{
return mBaseStream->Flush();
}
NS_IMETHODIMP
WriteStreamWrapper::Close()
{
return mBaseStream->Close();
}
nsDiskCacheRecordChannel::nsDiskCacheRecordChannel(nsDiskCacheRecord *aRecord,
nsILoadGroup *aLoadGroup)
: mRecord(aRecord) ,
mLoadGroup(aLoadGroup)
{
NS_INIT_REFCNT() ;
mRecord->mNumChannels++ ;
}
nsDiskCacheRecordChannel::~nsDiskCacheRecordChannel()
{
mRecord->mNumChannels-- ;
}
// FUR!!
//
// I know that I gave conflicting advice on the issue of file
// transport versus file protocol handler, but I thought that the
// last word was that we would use the raw transport, when I wrote:
//
// > I just thought of an argument for the other side of the coin, i.e. the
// > benefit of *not* reusing the file protocol handler: On the Mac, it's
// > expensive to convert from a string URL to an nsFileSpec, because the Mac
// > is brain-dead and scans every directory on the path to the file. It's
// > cheaper to create a nsFileSpec for a cache file by combining a single,
// > static nsFileSpec that corresponds to the cache directory with the
// > relative path to the cache file (using nsFileSpec's operator +). This
// > operation is optimized on the Mac to avoid the scanning operation.
//
// The Mac guys will eat us alive if we do path string to nsFileSpec
// conversions for every cache file we open.
nsresult
nsDiskCacheRecordChannel::Init(void)
{
char* urlStr ;
mRecord->mFile->GetURLString(&urlStr) ;
nsresult rv ;
NS_WITH_SERVICE(nsIIOService, serv, kIOServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
rv = serv->NewChannel("load", // XXX what should this be?
urlStr,
nsnull, // no base uri
mLoadGroup,
nsnull, // no eventsink getter
0,
nsnull, // no original URI
0,
0,
getter_AddRefs(mFileTransport));
return rv ;
}
nsresult
nsDiskCacheRecordChannel::NotifyStorageInUse(PRInt32 aBytesUsed)
{
return mRecord->mDiskCache->m_StorageInUse += aBytesUsed ;
}
// implement nsISupports
NS_IMPL_ISUPPORTS(nsDiskCacheRecordChannel, NS_GET_IID(nsIChannel))
// implement nsIRequest
NS_IMETHODIMP
nsDiskCacheRecordChannel::IsPending(PRBool *aIsPending)
{
*aIsPending = PR_FALSE ;
if(!mFileTransport)
return NS_OK ;
return mFileTransport->IsPending(aIsPending) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::Cancel(void)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->Cancel() ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::Suspend(void)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->Suspend() ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::Resume(void)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->Resume() ;
}
// implement nsIChannel
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetURI(nsIURI * *aURI)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->GetURI(aURI) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::OpenInputStream(PRUint32 aStartPosition,
PRInt32 aReadCount,
nsIInputStream* *aResult)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->OpenInputStream(aStartPosition,
aReadCount,
aResult) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::OpenOutputStream(PRUint32 startPosition,
nsIOutputStream* *aResult)
{
nsresult rv ;
NS_ENSURE_ARG(aResult) ;
nsCOMPtr<nsIOutputStream> outputStream ;
PRUint32 oldLength ;
mRecord->GetStoredContentLength(&oldLength) ;
if(startPosition < oldLength) {
NotifyStorageInUse(startPosition - oldLength) ;
// we should truncate the file at here.
rv = mRecord->SetStoredContentLength(startPosition) ;
if(NS_FAILED(rv)) {
printf(" failed to truncate\n") ;
return rv ;
}
}
rv = mFileTransport->OpenOutputStream(startPosition, getter_AddRefs(outputStream)) ;
if(NS_FAILED(rv)) return rv ;
return WriteStreamWrapper::Create(this, outputStream, aResult) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::AsyncOpen(nsIStreamObserver *observer,
nsISupports *ctxt)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->AsyncOpen(observer, ctxt) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::AsyncRead(PRUint32 aStartPosition,
PRInt32 aReadCount,
nsISupports *aContext,
nsIStreamListener *aListener)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->AsyncRead(aStartPosition ,
aReadCount ,
aContext ,
aListener) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::AsyncWrite(nsIInputStream *fromStream,
PRUint32 startPosition,
PRInt32 writeCount,
nsISupports *ctxt,
nsIStreamObserver *observer)
{
/*
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->AsyncWrite(fromStream,
startPosition,
writeCount,
ctxt,
observer) ;
*/
// I can't do this since the write is not monitored, and I won't be
// able to updata the storage.
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetLoadAttributes(nsLoadFlags *aLoadAttributes)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->GetLoadAttributes(aLoadAttributes) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::SetLoadAttributes(nsLoadFlags aLoadAttributes)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->SetLoadAttributes(aLoadAttributes) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetContentType(char * *aContentType)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->GetContentType(aContentType) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetContentLength(PRInt32 *aContentLength)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->GetContentLength(aContentLength) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetOwner(nsISupports* *aOwner)
{
*aOwner = mOwner.get() ;
NS_IF_ADDREF(*aOwner) ;
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::SetOwner(nsISupports* aOwner)
{
mOwner = aOwner ;
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetOriginalURI(nsIURI* *aURI)
{
// FUR - might need to implement this - not sure
return NS_ERROR_NOT_IMPLEMENTED ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsDiskCacheRecordChannel method unexpectedly called");
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsDiskCacheRecordChannel method unexpectedly called");
return NS_OK;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsDiskCacheRecordChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsDiskCacheRecordChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}

View File

@@ -1,65 +0,0 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (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/MPL/
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#ifndef _ns_DiskCacheRecordChannel_h_
#define _ns_DiskCacheRecordChannel_h_
#include "nsIChannel.h"
#include "nsCOMPtr.h"
#include "nsDiskCacheRecord.h"
/*
* This class is plagiarized from nsMemCacheChannel
*/
class nsDiskCacheRecordChannel : public nsIChannel
{
public:
nsDiskCacheRecordChannel(nsDiskCacheRecord *aRecord, nsILoadGroup *aLoadGroup);
virtual ~nsDiskCacheRecordChannel() ;
// Declare nsISupports methods
NS_DECL_ISUPPORTS
// Declare nsIRequest methods
NS_DECL_NSIREQUEST
// Declare nsIChannel methods
NS_DECL_NSICHANNEL
nsresult Init(void) ;
private:
nsresult NotifyStorageInUse(PRInt32 aBytesUsed) ;
nsCOMPtr<nsDiskCacheRecord> mRecord ;
nsCOMPtr<nsILoadGroup> mLoadGroup ;
nsCOMPtr<nsISupports> mOwner ;
nsCOMPtr<nsIChannel> mFileTransport ;
friend class WriteStreamWrapper ;
} ;
#endif // _ns_DiskCacheRecordChannel_h_

View File

@@ -1,60 +0,0 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (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/MPL/
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#ifndef _NS_IDBACCESSOR_H_
#define _NS_IDBACCESSOR_H_
#include "nsISupports.h"
#include "nsIFileSpec.h"
// nsIDBAccessorIID {6AADD4D0-7785-11d3-87FE-000629D01344}
#define NS_IDBACCESSOR_IID \
{ 0x6aadd4d0, 0x7785, 0x11d3, \
{0x87, 0xfe, 0x0, 0x6, 0x29, 0xd0, 0x13, 0x44}}
// nsDBAccessorCID {6AADD4D1-7785-11d3-87FE-000629D01344}
#define NS_DBACCESSOR_CID \
{ 0x6aadd4d1, 0x7785, 0x11d3, \
{ 0x87, 0xfe, 0x0, 0x6, 0x29, 0xd0, 0x13, 0x44 }}
class nsIDBAccessor : public nsISupports
{
public:
NS_DEFINE_STATIC_IID_ACCESSOR(NS_IDBACCESSOR_IID)
NS_IMETHOD Init(nsIFileSpec* DBFile) = 0 ;
NS_IMETHOD Shutdown(void) = 0 ;
NS_IMETHOD Put(PRInt32 aID, void* anEntry, PRUint32 aLength) = 0 ;
NS_IMETHOD Get(PRInt32 aID, void** anEntry, PRUint32 *aLength) = 0 ;
NS_IMETHOD Del(PRInt32 aID, void* anEntry, PRUint32 aLength) = 0 ;
NS_IMETHOD GetID(const char* key, PRUint32 length, PRInt32* aID) = 0 ;
NS_IMETHOD EnumEntry(void* *anEntry, PRUint32* aLength, PRBool bReset) = 0 ;
} ;
#endif // _NS_IDBACCESSOR_H_

View File

@@ -1,691 +0,0 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (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/MPL/
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#include "nsNetDiskCache.h"
#include "nscore.h"
#include "plstr.h"
#include "prprf.h"
#include "prtypes.h"
#include "prio.h"
#include "prsystem.h" // Directory Seperator
#include "plhash.h"
#include "prclist.h"
#include "prmem.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsIPref.h"
#include "mcom_db.h"
#include "nsDBEnumerator.h"
#include "nsDiskCacheRecord.h"
static NS_DEFINE_CID(kPrefCID, NS_PREF_CID) ;
static NS_DEFINE_CID(kDBAccessorCID, NS_DBACCESSOR_CID) ;
static const PRUint32 DISK_CACHE_SIZE_DEFAULT = 5*1024*1024 ; // 5MB
static const char * const DISK_CACHE_PREF = "browser.cache.disk_cache_size";
static const char * const CACHE_DIR_PREF = "browser.cache.directory";
class nsDiskCacheRecord ;
nsNetDiskCache::nsNetDiskCache() :
m_Enabled(PR_TRUE) ,
m_NumEntries(0) ,
m_pNextCache(0) ,
m_pDiskCacheFolder(0) ,
m_StorageInUse(0) ,
m_DB(0) ,
m_BaseDirNum(32)
{
// set it to INF for now
m_MaxEntries = (PRUint32)-1 ;
NS_INIT_REFCNT();
}
nsNetDiskCache::~nsNetDiskCache()
{
printf("~nsNetDiskCache\n") ;
NS_IF_RELEASE(m_DB) ;
// FUR!!
// You shouldn't rely on the value of m_BaseDirNum to diagnose whether or not
// a cache corruption has occurred since it's possible that the app does not
// shut down cleanly and a corrupted cache has still not been cleaned up from
// a previous session. My suggestion is that you pick a different scheme for
// renaming the dirs, e.g. rename them as "trash*" and remove all directories
// with this name pattern on shutdown.
// FUR
// I think that, eventually, we also want a distinguished key in the DB which
// means "clean cache shutdown". You clear this flag when the db is first
// opened and set it just before the db is closed. If the db wasn't shutdown
// cleanly in a prior session, i.e. because the app crashed, on startup you
// scan all the individual files in directories and look for "orphans",
// i.e. cache files which don't have corresponding entries in the db. That's
// also when storage-in-use and number of entries would be recomputed.
//
// We don't necessarily need all this functionality immediately, though.
if(m_BaseDirNum > 32)
RemoveDirs(32) ;
}
NS_IMETHODIMP
nsNetDiskCache::Init(void)
{
nsresult rv ;
// FUR!!
// I really don't think prefs belong here, since that breaks modularity. It
// presupposes that the file cache code is embedded in the browser or some
// other application that uses the prefs, i.e. the code might be used in a
// standalone cache manipulation tool or, someday, in server code. Pref
// reading belongs at a higher level, either in the application itself or
// possibly the I/O manager.
// Also, Init() needs to be lazy, since folder name is not set on startup,
// i.e. need a call to MaybeInit() at the beginning of every public method
NS_WITH_SERVICE(nsIPref, pref, kPrefCID, &rv) ;
if (NS_FAILED(rv))
NS_ERROR("Failed to get globle preference!\n") ;
rv = NS_NewFileSpec(getter_AddRefs(m_pDiskCacheFolder));
if (!m_pDiskCacheFolder) {
NS_ERROR("ERROR: Could not make a file spec.\n") ;
return NS_ERROR_OUT_OF_MEMORY ;
}
char* tempPref = 0 ;
if(pref) {
PRInt32 nTemp = 0 ;
/*
rv = pref->CopyCharPref(CACHE_DIR_PREF, &tempPref) ;
if (NS_SUCCEEDED(rv)) {
printf("cache dir is %s\n", tempPref) ;
m_pDiskCacheFolder->SetUnixStyleFilePath(tempPref) ;
PR_Free(tempPref) ;
} else */
{
m_pDiskCacheFolder->SetUnixStyleFilePath("/tmp") ;
printf("using default folder, /tmp\n") ;
}
}
else {
// temp hack for now. change later for other platform
m_pDiskCacheFolder->SetUnixStyleFilePath("/tmp") ;
}
// FUR - suggest you use nsCOMPtr for m_DB - it will eliminate
// manual addref/release and reduce likelihood of bugs
NS_IF_RELEASE(m_DB) ;
m_DB = new nsDBAccessor() ;
if(!m_DB)
return NS_ERROR_OUT_OF_MEMORY ;
else
NS_ADDREF(m_DB) ;
rv = InitDB() ;
// try once for recovery
if(rv == NS_ERROR_FAILURE) {
rv = DBRecovery() ;
return rv ;
}
rv = UpdateInfo() ;
return rv ;
}
NS_IMETHODIMP
nsNetDiskCache::InitDB(void)
{
// create cache sub directories
nsresult rv ;
nsCOMPtr<nsIFileSpec> cacheSubDir;
rv = NS_NewFileSpec(getter_AddRefs(cacheSubDir));
// FUR - any way to avoid doing this, if it's already been done ?
for (int i=0; i < 32; i++) {
rv = cacheSubDir->FromFileSpec(m_pDiskCacheFolder) ;
if(NS_FAILED(rv))
return rv ;
char dirName[3];
PR_snprintf (dirName, 3, "%0.2x", i);
cacheSubDir->AppendRelativeUnixPath (dirName) ;
CreateDir(cacheSubDir);
}
NS_NewFileSpec(getter_AddRefs(m_DBFile)) ;
// FUR - check for NS_NewFileSpec failure
rv = m_DBFile->FromFileSpec(m_pDiskCacheFolder) ;
if(NS_FAILED(rv))
return rv ;
m_DBFile->AppendRelativeUnixPath("cache.db") ;
rv = m_DB->Init(m_DBFile) ;
return rv ;
}
//////////////////////////////////////////////////////////////////////////
// nsISupports methods
// FUR - Suggest you use NS_IMPL_ISUPPORTS3() macro instead
NS_IMETHODIMP
nsNetDiskCache::QueryInterface(const nsIID& aIID, void** aInstancePtr)
{
NS_ASSERTION(aInstancePtr, "no instance pointer");
if(aIID.Equals(NS_GET_IID(nsINetDataDiskCache)) ||
aIID.Equals(NS_GET_IID(nsINetDataCache)) ||
aIID.Equals(NS_GET_IID(nsISupports))) {
*aInstancePtr = NS_STATIC_CAST(nsINetDataDiskCache*, this);
NS_ADDREF_THIS();
return NS_OK;
}
else
return NS_NOINTERFACE ;
}
NS_IMPL_ADDREF(nsNetDiskCache) ;
NS_IMPL_RELEASE(nsNetDiskCache) ;
///////////////////////////////////////////////////////////////////////////
// nsINetDataCache Method
NS_IMETHODIMP
nsNetDiskCache::GetDescription(PRUnichar* *aDescription)
{
nsAutoString description("Disk Cache") ;
*aDescription = description.ToNewUnicode() ;
if(!*aDescription)
return NS_ERROR_OUT_OF_MEMORY ;
return NS_OK ;
}
/* don't alloc mem for nsICachedNetData.
* RecordID is generated using the same scheme in nsCacheDiskData,
* see GetCachedNetData() for detail.
*/
NS_IMETHODIMP
nsNetDiskCache::Contains(const char* key, PRUint32 length, PRBool *_retval)
{
*_retval = PR_FALSE ;
NS_ASSERTION(m_DB, "no db.") ;
PRInt32 id = 0 ;
m_DB->GetID(key, length, &id) ;
// FUR - Check for GetID failure ?
void* info = 0 ;
PRUint32 info_size = 0 ;
nsresult rv = m_DB->Get(id, &info, &info_size) ;
if(NS_SUCCEEDED(rv) && info)
*_retval = PR_TRUE ;
return NS_OK ;
}
/* regardless if it's cached or not, a copy of nsNetDiskCache would
* always be returned. so release it appropriately.
* if mem alloced, updata m_NumEntries also.
* for now, the new nsCachedNetData is not written into db yet since
* we have nothing to write.
*/
NS_IMETHODIMP
nsNetDiskCache::GetCachedNetData(const char* key, PRUint32 length, nsINetDataCacheRecord **_retval)
{
NS_ASSERTION(m_DB, "no db.") ;
nsresult rv = 0 ;
if (!_retval)
return NS_ERROR_NULL_POINTER ;
*_retval = nsnull ;
PRInt32 id = 0 ;
m_DB->GetID(key, length, &id) ;
// FUR - Check for GetID failure ?
// construct an empty record
nsDiskCacheRecord* newRecord = new nsDiskCacheRecord(m_DB, this) ;
if(!newRecord)
return NS_ERROR_OUT_OF_MEMORY ;
rv = newRecord->Init(key, length) ;
if(NS_FAILED(rv)) {
delete newRecord ;
return rv ;
}
NS_ADDREF(newRecord) ; // addref for _retval
*_retval = (nsINetDataCacheRecord*) newRecord ;
void* info = 0 ;
PRUint32 info_size = 0 ;
rv = m_DB->Get(id, &info, &info_size) ;
if(NS_SUCCEEDED(rv) && info) {
nsresult r1 ;
r1 = newRecord->RetrieveInfo(info, info_size) ;
// FUR!! need to release and return error if RetrieveInfo() fails
if(NS_SUCCEEDED(rv))
return NS_OK ;
else
return r1;
} else if (NS_SUCCEEDED(rv) && !info) {
// this is a new record.
m_NumEntries ++ ;
return NS_OK ;
} else
return rv ;
}
/* get an nsICachedNetData, mem needs to be de-alloced if not found. */
NS_IMETHODIMP
nsNetDiskCache::GetCachedNetDataByID(PRInt32 RecordID, nsINetDataCacheRecord **_retval)
{
NS_ASSERTION(m_DB, "no db.") ;
if (!_retval)
return NS_ERROR_NULL_POINTER ;
*_retval = nsnull ;
nsresult rv ;
void* info = 0 ;
PRUint32 info_size = 0 ;
rv = m_DB->Get(RecordID, &info, &info_size) ;
if(NS_SUCCEEDED(rv) && info) {
// construct an empty record if only found in db
nsDiskCacheRecord* newRecord = new nsDiskCacheRecord(m_DB, this) ;
if(!newRecord)
return NS_ERROR_OUT_OF_MEMORY ;
NS_ADDREF(newRecord) ; // addref for _retval
rv = newRecord->RetrieveInfo(info, info_size) ;
if(NS_SUCCEEDED(rv)) {
*_retval = (nsINetDataCacheRecord*) newRecord ;
return NS_OK ;
}
else {
// bad record, I guess
NS_RELEASE(newRecord) ; // release if bad things happen
return rv ;
}
} else {
NS_ERROR("Error: RecordID not in DB\n") ;
return rv ;
}
}
NS_IMETHODIMP
nsNetDiskCache::GetEnabled(PRBool *aEnabled)
{
*aEnabled = m_Enabled ;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::SetEnabled(PRBool aEnabled)
{
m_Enabled = aEnabled ;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::GetFlags(PRUint32 *aFlags)
{
*aFlags = FILE_PER_URL_CACHE;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::GetNumEntries(PRUint32 *aNumEntries)
{
*aNumEntries = m_NumEntries ;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::GetMaxEntries(PRUint32 *aMaxEntries)
{
*aMaxEntries = m_MaxEntries ;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::NewCacheEntryIterator(nsISimpleEnumerator **_retval)
{
if(!_retval)
return NS_ERROR_NULL_POINTER ;
*_retval = nsnull ;
nsISimpleEnumerator* enumerator = new nsDBEnumerator(m_DB, this) ;
if(enumerator) {
NS_ADDREF(enumerator) ;
*_retval = enumerator ;
return NS_OK ;
}
else
return NS_ERROR_OUT_OF_MEMORY ;
}
NS_IMETHODIMP
nsNetDiskCache::GetNextCache(nsINetDataCache * *aNextCache)
{
if(!aNextCache)
return NS_ERROR_NULL_POINTER ;
*aNextCache = m_pNextCache ;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::SetNextCache(nsINetDataCache *aNextCache)
{
m_pNextCache = aNextCache ;
return NS_OK ;
}
// db size can always be measured at the last minute. Since it's hard
// to know before hand.
NS_IMETHODIMP
nsNetDiskCache::GetStorageInUse(PRUint32 *aStorageInUse)
{
PRUint32 total_size = m_StorageInUse, len = 0 ;
// FUR!!
// GetStorageInUse() can be called hundreds of times per second, i.e. every
// time a buffer of data is written to the cache, so we can't afford to stat
// the db file on every call. I would suggest caching the size of the db and
// invalidating that cached value every time a record is written to the db,
// or even every ten written records.
// add the size of the db.
// m_DBFile->GetFileSize(&len) ;
// total_size += len ;
// we need size in kB
total_size = total_size >> 10 ;
*aStorageInUse = total_size ;
return NS_OK ;
}
/*
* The whole cache dirs can be whiped clean since all the cache
* files are resides in seperate hashed dirs. It's safe to do so.
*/
NS_IMETHODIMP
nsNetDiskCache::RemoveAll(void)
{
nsresult rv = RemoveDirs(0) ;
if(NS_FAILED(rv))
return rv ;
// don't forget the db file itself
m_DB->Shutdown() ;
nsFileSpec dbfile ;
m_DBFile->GetFileSpec(&dbfile) ;
dbfile.Delete(PR_TRUE) ;
// reinitilize
rv = InitDB() ;
if(NS_FAILED(rv))
return rv ;
rv = UpdateInfo() ;
return rv ;
}
//////////////////////////////////////////////////////////////////
// nsINetDataDiskCache methods
NS_IMETHODIMP
nsNetDiskCache::GetDiskCacheFolder(nsIFileSpec * *aDiskCacheFolder)
{
*aDiskCacheFolder = m_pDiskCacheFolder ;
NS_ADDREF(*aDiskCacheFolder) ;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::SetDiskCacheFolder(nsIFileSpec * aDiskCacheFolder)
{
char *newfolder, *oldfolder ;
m_pDiskCacheFolder->GetNativePath(&oldfolder) ;
aDiskCacheFolder->GetNativePath(&newfolder) ;
if(PL_strcmp(newfolder, oldfolder) == 0) {
m_pDiskCacheFolder = aDiskCacheFolder ;
// should we do this?
// FUR - no
nsresult rv = RemoveAll() ;
return rv ;
}
else
// FUR
// Need to blow away old cache, build new one
return NS_OK ;
}
//////////////////////////////////////////////////////////////////
// nsNetDiskCache methods
// create a directory (recursively)
NS_IMETHODIMP
nsNetDiskCache::CreateDir(nsIFileSpec* dir_spec)
{
PRBool does_exist ;
nsCOMPtr<nsIFileSpec> p_spec ;
dir_spec->Exists(&does_exist) ;
if(does_exist)
return NS_OK ;
dir_spec->GetParent(getter_AddRefs(p_spec)) ;
// FUR - check return value
p_spec->Exists(&does_exist) ;
if(!does_exist) {
CreateDir(p_spec) ;
dir_spec->CreateDir() ;
// FUR - check return value
}
else {
dir_spec->CreateDir() ;
// FUR - check return value
}
return NS_OK ;
}
// FUR!!
// We can't afford to make a *separate* pass over the whole db on every
// startup, just to figure out m_NumEntries and m_StorageInUse. (This is a
// several second operation on a large db). We'll likely need to store
// distinguished keys in the db that contain these values and update them
// incrementally, except when failure to shut down the db cleanly is detected.
// this will walk through db and update m_NumEntries and m_StorageInUse
NS_IMETHODIMP
nsNetDiskCache::UpdateInfo(void)
{
// count num of entries in db
// NS_ADDREF(this) ; // addref before assign to a nsCOMPtr.
nsISimpleEnumerator* dbEnumerator = new nsDBEnumerator(m_DB, this) ;
if(dbEnumerator)
NS_ADDREF(dbEnumerator) ;
else
return NS_ERROR_FAILURE ;
PRUint32 numEntries = 0, storageInUse = 0, len = 0 ;
PRBool more = PR_FALSE ;
do {
dbEnumerator->HasMoreElements(&more) ;
if(more) {
// update entry number
numEntries++ ;
// update storage in use
nsINetDataCacheRecord* record ;
dbEnumerator->GetNext((nsISupports**)&record) ;
record->GetStoredContentLength(&len) ;
storageInUse += len ;
NS_IF_RELEASE(record) ;
}
} while (more) ;
NS_IF_RELEASE(dbEnumerator) ;
m_NumEntries = numEntries ;
m_StorageInUse = storageInUse ;
printf(" m_NumEntries = %d, size is %d.\n", m_NumEntries, m_StorageInUse) ;
return NS_OK ;
}
// this routine will add m_BaseDirNum to current CacheSubDir names.
// e.g. 00->20, 1f->5f. and update the m_BaseDirNum to another 32.
// the idea is as long as we remember the base number,
// we know how many dirs needs to be removed during shutdown period
// it will be from 0x20 to m_BaseDirNum.
// also, we assume that this operation will not be performed 3 times more
// within a single session. it is part of scavenging routine.
NS_IMETHODIMP
nsNetDiskCache::RenameCacheSubDirs(void)
{
nsCOMPtr<nsIFileSpec> cacheSubDir;
nsresult rv = NS_NewFileSpec(getter_AddRefs(cacheSubDir)) ;
for (int i=0; i < 32; i++) {
rv = cacheSubDir->FromFileSpec(m_pDiskCacheFolder) ;
if(NS_FAILED(rv))
return rv ;
char dirName[3];
PR_snprintf(dirName, 3, "%0.2x", i) ;
cacheSubDir->AppendRelativeUnixPath(dirName) ;
// re-name the directory
PR_snprintf(dirName, 3, "%0.2x", i+m_BaseDirNum) ;
rv = cacheSubDir->Rename(dirName) ;
if(NS_FAILED(rv))
return NS_ERROR_FAILURE ;
}
// update m_BaseDirNum
m_BaseDirNum += 32 ;
return NS_OK ;
}
// this routine will be called everytime we have a db corruption.
NS_IMETHODIMP
nsNetDiskCache::DBRecovery(void)
{
nsresult rv = RenameCacheSubDirs() ;
if(NS_FAILED(rv))
return rv ;
// remove corrupted db file
rv = m_DB->Shutdown() ;
// FUR!!
// You shouldn't return if this fails. Otherwise, it might prevent db deletion
if(NS_FAILED(rv))
return rv ;
nsFileSpec dbfile ;
m_DBFile->GetFileSpec(&dbfile) ;
dbfile.Delete(PR_TRUE) ;
// make sure it's not there any more
PRBool exists = dbfile.Exists() ;
if(exists) {
NS_ERROR("can't remove old db.") ;
return NS_ERROR_FAILURE ;
}
// reinitilize
rv = InitDB() ;
if(NS_FAILED(rv))
return rv ;
rv = UpdateInfo() ;
return rv ;
}
// this routine is used by dtor and RemoveAll() to clean up dirs.
// All directory named from aNum - m_BasedDirNum will be deleted.
NS_IMETHODIMP
nsNetDiskCache::RemoveDirs(PRUint32 aNum)
{
nsCOMPtr<nsIFileSpec> cacheSubDir;
nsresult rv = NS_NewFileSpec(getter_AddRefs(cacheSubDir));
if(NS_FAILED(rv))
return NS_ERROR_FAILURE ;
for (int i=aNum; i < m_BaseDirNum; i++) {
cacheSubDir->FromFileSpec(m_pDiskCacheFolder) ;
char dirName[3];
PR_snprintf (dirName, 3, "%0.2x", i);
cacheSubDir->AppendRelativeUnixPath (dirName) ;
nsFileSpec subdir ;
cacheSubDir->GetFileSpec(&subdir) ;
for(nsDirectoryIterator di(subdir, PR_FALSE); di.Exists(); di++) {
di.Spec().Delete(PR_TRUE) ;
}
subdir.Delete(PR_FALSE) ; // recursive delete
}
return NS_OK ;
}

View File

@@ -1,82 +0,0 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (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/MPL/
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
// FUR - Add overall description comment here
#ifndef __gen_nsNetDiskCache_h__
#define __gen_nsNetDiskCache_h__
#include "nsINetDataDiskCache.h"
#include "nsNetDiskCacheCID.h"
#include "nsCOMPtr.h"
#include "nsIPref.h"
#include "nsDBAccessor.h"
class nsIURI; /* forward decl */
class nsICachedNetData; /* forward decl */
class nsISimpleEnumerator; /* forward decl */
class nsIFileSpec; /* forward decl */
/* starting interface: nsNetDiskCache */
class nsNetDiskCache : public nsINetDataDiskCache {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSINETDATACACHE
NS_DECL_NSINETDATADISKCACHE
NS_IMETHOD Init(void) ;
nsNetDiskCache() ;
virtual ~nsNetDiskCache() ;
protected:
NS_IMETHOD InitDB(void) ;
NS_IMETHOD CreateDir(nsIFileSpec* dir_spec) ;
NS_IMETHOD UpdateInfo(void) ;
NS_IMETHOD RenameCacheSubDirs(void) ;
NS_IMETHOD DBRecovery(void) ;
NS_IMETHOD RemoveDirs(PRUint32 aNum) ;
private:
PRBool m_Enabled ;
PRUint32 m_NumEntries ;
nsCOMPtr<nsINetDataCache> m_pNextCache ;
nsCOMPtr<nsIFileSpec> m_pDiskCacheFolder ;
nsCOMPtr<nsIFileSpec> m_DBFile ;
PRUint32 m_MaxEntries ;
PRUint32 m_StorageInUse ;
nsIDBAccessor* m_DB ;
// this is used to indicate a db corruption
PRInt32 m_BaseDirNum ;
friend class nsDiskCacheRecord ;
friend class nsDiskCacheRecordChannel ;
} ;
#endif /* __gen_nsNetDiskCache_h__ */

View File

@@ -1,32 +0,0 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (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/MPL/
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#ifndef _nsNetDiskCacheCID_h_
#define _nsNetDiskCacheCID_h_
#define NS_NETDISKCACHE_CID_STR "ECFEEA00-7201-11d3-87FE-000629D01344"
#define NS_NETDISKCACHE_CID \
{ 0xecfeea00, 0x7201, 0x11d3, \
{ 0x87, 0xfe, 0x0, 0x6, 0x29, 0xd0, 0x13, 0x44 }}
#endif /* _nsNetDiskCacheCID_h_ */

View File

@@ -1,50 +0,0 @@
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
#
DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
REQUIRES = libreg xpcom
CPPSRCS = \
diskcache.cpp \
$(NULL)
SIMPLE_PROGRAMS = $(CPPSRCS:.cpp=)
ifdef NO_LD_ARCHIVE_FLAGS
LOST_SYM_LIBS = -lxpcomds_s -lxptinfo -lmozreg_s
endif
LIBS = \
-lmozjs \
-lxpcom \
-lmozdbm_s \
$(MOZ_NECKO_UTIL_LIBS) \
$(LOST_SYM_LIBS) \
$(NSPR_LIBS) \
$(NULL)
include $(topsrcdir)/config/rules.mk
LOCAL_INCLUDES = -I$(srcdir)/..
DEFINES += -DUSE_NSREG -DCACHE

View File

@@ -1,836 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsIStreamListener.h"
#include "nsIStreamObserver.h"
#include "nsIServiceManager.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
#include "nsIEventQueue.h"
#include "nsIEventQueueService.h"
#include "nsIChannel.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include <stdio.h>
#include <unistd.h>
#include "nsINetDataCache.h"
#include "nsINetDataCacheRecord.h"
//#include "nsMemCacheCID.h"
#include "nsNetDiskCache.h"
#include "nsIPref.h"
#include "prenv.h"
#include "nsIFileStream.h"
// Number of test entries to be placed in the cache
#define NUM_CACHE_ENTRIES 250
// Cache content stream length will have random length between zero and
// MAX_CONTENT_LENGTH bytes
#define MAX_CONTENT_LENGTH 20000
// Length of random-data cache entry key
#define CACHE_KEY_LENGTH 15
// Length of random-data cache entry meta-data
#define CACHE_METADATA_LENGTH 100
//static NS_DEFINE_CID(kMemCacheCID, NS_MEM_CACHE_FACTORY_CID);
static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
static NS_DEFINE_CID(kDiskCacheCID, NS_NETDISKCACHE_CID) ;
static NS_DEFINE_CID(kPrefCID, NS_PREF_CID);
static NS_DEFINE_IID(kIPrefIID, NS_IPREF_IID);
// Mapping from test case number to RecordID
static PRInt32 recordID[NUM_CACHE_ENTRIES];
static PRInt32
mapRecordIdToTestNum(PRInt32 aRecordID)
{
int i;
for (i = 0; i < NUM_CACHE_ENTRIES; i++) {
if (recordID[i] == aRecordID)
return i;
}
return -1;
}
// A supply of stream data to either store or compare with
class nsITestDataStream {
public:
virtual ~nsITestDataStream() {};
virtual PRUint32 Next() = 0;
virtual void Read(char* aBuf, PRUint32 aCount) = 0;
virtual PRBool Match(char* aBuf, PRUint32 aCount) = 0;
virtual void Skip(PRUint32 aCount) = 0;
};
// A reproducible stream of random data.
class RandomStream : public nsITestDataStream {
public:
RandomStream(PRUint32 aSeed) {
mStartSeed = mState = aSeed;
}
PRUint32 GetStartSeed() {
return mStartSeed;
}
PRUint32 Next() {
mState = 1103515245 * mState + 12345;
return mState;
}
void Read(char* aBuf, PRUint32 aCount) {
PRUint32 i;
for (i = 0; i < aCount; i++) {
*aBuf++ = Next();
}
}
PRBool
Match(char* aBuf, PRUint32 aCount) {
PRUint32 i;
for (i = 0; i < aCount; i++) {
if (*aBuf++ != (char)(Next() & 0xff))
return PR_FALSE;
}
return PR_TRUE;
}
void
Skip(PRUint32 aCount) {
while (aCount--)
Next();
}
protected:
PRUint32 mState;
PRUint32 mStartSeed;
};
// A stream of data that increments on each byte that is read, modulo 256
class CounterStream : public nsITestDataStream {
public:
CounterStream(PRUint32 aSeed) {
mStartSeed = mState = aSeed;
}
PRUint32 GetStartSeed() {
return mStartSeed;
}
PRUint32 Next() {
mState += 1;
mState &= 0xff;
return mState;
}
void Read(char* aBuf, PRUint32 aCount) {
PRUint32 i;
for (i = 0; i < aCount; i++) {
*aBuf++ = Next();
}
}
PRBool
Match(char* aBuf, PRUint32 aCount) {
PRUint32 i;
for (i = 0; i < aCount; i++) {
if (*aBuf++ != (char)Next())
return PR_FALSE;
}
return PR_TRUE;
}
void
Skip(PRUint32 aCount) {
mState += aCount;
mState &= 0xff;
}
protected:
PRUint32 mState;
PRUint32 mStartSeed;
};
static int gNumReaders = 0;
static PRUint32 gTotalBytesRead = 0;
static PRUint32 gTotalDuration = 0;
class nsReader : public nsIStreamListener {
public:
NS_DECL_ISUPPORTS
nsReader()
: mStartTime(0), mBytesRead(0)
{
NS_INIT_REFCNT();
gNumReaders++;
}
virtual ~nsReader() {
delete mTestDataStream;
gNumReaders--;
}
nsresult
Init(nsIChannel *aChannel, nsITestDataStream* aRandomStream, PRUint32 aExpectedStreamLength) {
mChannel = aChannel;
mTestDataStream = aRandomStream;
mExpectedStreamLength = aExpectedStreamLength;
mRefCnt = 1;
return NS_OK;
}
NS_IMETHOD OnStartRequest(nsIChannel* channel,
nsISupports* context) {
mStartTime = PR_IntervalNow();
return NS_OK;
}
NS_IMETHOD OnDataAvailable(nsIChannel* channel,
nsISupports* context,
nsIInputStream *aIStream,
PRUint32 aSourceOffset,
PRUint32 aLength) {
char buf[1025];
while (aLength > 0) {
PRUint32 amt;
PRBool match;
aIStream->Read(buf, sizeof buf, &amt);
if (amt == 0) break;
aLength -= amt;
mBytesRead += amt;
match = mTestDataStream->Match(buf, amt);
NS_ASSERTION(match, "Stored data was corrupted on read");
}
return NS_OK;
}
NS_IMETHOD OnStopRequest(nsIChannel* channel,
nsISupports* context,
nsresult aStatus,
const PRUnichar* aMsg) {
PRIntervalTime endTime;
PRIntervalTime duration;
endTime = PR_IntervalNow();
duration = (endTime - mStartTime);
if (NS_FAILED(aStatus)) printf("channel failed.\n");
// printf("read %d bytes\n", mBytesRead);
NS_ASSERTION(mBytesRead == mExpectedStreamLength,
"Stream in cache is wrong length");
gTotalBytesRead += mBytesRead;
gTotalDuration += duration;
// Release channel
mChannel = 0;
return NS_OK;
}
protected:
PRIntervalTime mStartTime;
PRUint32 mBytesRead;
nsITestDataStream* mTestDataStream;
PRUint32 mExpectedStreamLength;
nsCOMPtr<nsIChannel> mChannel;
};
NS_IMPL_ISUPPORTS2(nsReader, nsIStreamListener, nsIStreamObserver)
static nsIEventQueue* eventQueue;
nsresult
InitQueue() {
nsresult rv;
NS_WITH_SERVICE(nsIEventQueueService, eventQService, kEventQueueServiceCID, &rv);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get event queue service");
rv = eventQService->CreateThreadEventQueue();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't create event queue");
rv = eventQService->GetThreadEventQueue(PR_CurrentThread(), &eventQueue);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get event queue for main thread");
return NS_OK;
}
// Process events until all streams are OnStopRequest'ed
nsresult
WaitForEvents() {
while (gNumReaders) {
eventQueue->ProcessPendingEvents();
}
return NS_OK;
}
// Read data for a single cache record and compare against testDataStream
nsresult
TestReadStream(nsINetDataCacheRecord *record, nsITestDataStream *testDataStream,
PRUint32 expectedStreamLength)
{
nsCOMPtr<nsIChannel> channel;
nsresult rv;
PRUint32 actualContentLength;
rv = record->NewChannel(0, getter_AddRefs(channel));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
rv = record->GetStoredContentLength(&actualContentLength);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(actualContentLength == expectedStreamLength,
"nsINetDataCacheRecord::GetContentLength() busted ?");
nsReader *reader = new nsReader;
reader->AddRef();
rv = reader->Init(channel, testDataStream, expectedStreamLength);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
rv = channel->AsyncRead(0, -1, 0, reader);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
reader->Release();
return NS_OK;
}
// Check that records can be retrieved using their record-ID, in addition
// to using the opaque key.
nsresult
TestRecordID(nsINetDataCache *cache)
{
nsresult rv;
nsCOMPtr<nsINetDataCacheRecord> record;
RandomStream *randomStream;
PRUint32 metaDataLength;
char cacheKey[CACHE_KEY_LENGTH];
char *metaData;
PRUint32 testNum;
PRBool match;
for (testNum = 0; testNum < NUM_CACHE_ENTRIES; testNum++) {
randomStream = new RandomStream(testNum);
randomStream->Read(cacheKey, sizeof cacheKey);
rv = cache->GetCachedNetDataByID(recordID[testNum], getter_AddRefs(record));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't obtain record using record ID");
// Match against previously stored meta-data
rv = record->GetMetaData(&metaDataLength, &metaData);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get record meta-data");
match = randomStream->Match(metaData, metaDataLength);
NS_ASSERTION(match, "Meta-data corrupted or incorrect");
nsAllocator::Free(metaData);
delete randomStream;
}
return NS_OK;
}
// Check that all cache entries in the database are enumerated and that
// no duplicates appear.
nsresult
TestEnumeration(nsINetDataCache *cache)
{
nsresult rv;
nsCOMPtr<nsINetDataCacheRecord> record;
nsCOMPtr<nsISupports> tempISupports;
nsCOMPtr<nsISimpleEnumerator> iterator;
RandomStream *randomStream;
PRUint32 metaDataLength;
char cacheKey[CACHE_KEY_LENGTH];
char *metaData;
PRUint32 testNum;
PRBool match;
PRInt32 recID;
int numRecords = 0;
// Iterate over all records in the cache
rv = cache->NewCacheEntryIterator(getter_AddRefs(iterator));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't create new cache entry iterator");
PRBool notDone;
while (1) {
// Done iterating ?
rv = iterator->HasMoreElements(&notDone);
if (NS_FAILED(rv)) return rv;
if (!notDone)
break;
// Get next record in iteration
rv = iterator->GetNext(getter_AddRefs(tempISupports));
NS_ASSERTION(NS_SUCCEEDED(rv), "iterator bustage");
record = do_QueryInterface(tempISupports);
numRecords++;
// Get record ID
rv = record->GetRecordID(&recID);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get Record ID");
testNum = mapRecordIdToTestNum(recID);
NS_ASSERTION(testNum != -1, "Corrupted Record ID ?");
// Erase mapping from table, so that duplicate enumerations are detected
recordID[testNum] = -1;
// Make sure stream matches test data
randomStream = new RandomStream(testNum);
randomStream->Read(cacheKey, sizeof cacheKey);
// Match against previously stored meta-data
rv = record->GetMetaData(&metaDataLength, &metaData);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get record meta-data");
match = randomStream->Match(metaData, metaDataLength);
NS_ASSERTION(match, "Meta-data corrupted or incorrect");
nsAllocator::Free(metaData);
delete randomStream;
}
NS_ASSERTION(numRecords == NUM_CACHE_ENTRIES, "Iteration bug");
return NS_OK;
}
// Read the test data that was written in FillCache(), checking for
// corruption, truncation.
nsresult
TestRead(nsINetDataCache *cache)
{
nsresult rv;
PRBool inCache;
nsCOMPtr<nsINetDataCacheRecord> record;
RandomStream *randomStream;
PRUint32 metaDataLength;
char cacheKey[CACHE_KEY_LENGTH];
char *metaData, *storedCacheKey;
PRUint32 testNum, storedCacheKeyLength;
PRBool match;
for (testNum = 0; testNum < NUM_CACHE_ENTRIES; testNum++) {
randomStream = new RandomStream(testNum);
randomStream->Read(cacheKey, sizeof cacheKey);
// Ensure that entry is in the cache
rv = cache->Contains(cacheKey, sizeof cacheKey, &inCache);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(inCache, "nsINetDataCache::Contains error");
rv = cache->GetCachedNetData(cacheKey, sizeof cacheKey, getter_AddRefs(record));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
// Match against previously stored meta-data
match = record->GetMetaData(&metaDataLength, &metaData);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
match = randomStream->Match(metaData, metaDataLength);
NS_ASSERTION(match, "Meta-data corrupted or incorrect");
nsAllocator::Free(metaData);
// Test GetKey() method
rv = record->GetKey(&storedCacheKeyLength, &storedCacheKey);
NS_ASSERTION(NS_SUCCEEDED(rv) &&
(storedCacheKeyLength == sizeof cacheKey) &&
!memcmp(storedCacheKey, &cacheKey[0], sizeof cacheKey),
"nsINetDataCacheRecord::GetKey failed");
nsAllocator::Free(storedCacheKey);
PRUint32 expectedStreamLength = randomStream->Next() & 0xffff;
TestReadStream(record, randomStream, expectedStreamLength);
}
WaitForEvents();
// Compute rate in MB/s
double rate = gTotalBytesRead / PR_IntervalToMilliseconds(gTotalDuration);
rate *= NUM_CACHE_ENTRIES;
rate *= 1000;
rate /= (1024 * 1024);
printf("Read %d bytes at a rate of %5.1f MB per second \n",
gTotalBytesRead, rate);
return NS_OK;
}
// Repeatedly call SetStoredContentLength() on a cache entry and make
// read the stream's data to ensure that it's not corrupted by the effect
nsresult
TestTruncation(nsINetDataCache *cache)
{
nsresult rv;
nsCOMPtr<nsINetDataCacheRecord> record;
RandomStream *randomStream;
char cacheKey[CACHE_KEY_LENGTH];
randomStream = new RandomStream(0);
randomStream->Read(cacheKey, sizeof cacheKey);
rv = cache->GetCachedNetData(cacheKey, sizeof cacheKey, getter_AddRefs(record));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
randomStream->Skip(CACHE_METADATA_LENGTH);
PRUint32 initialStreamLength = randomStream->Next() & 0xffff;
delete randomStream;
PRUint32 i;
PRUint32 delta = initialStreamLength / 64;
for (i = initialStreamLength; i >= delta; i -= delta) {
PRUint32 expectedStreamLength = i;
// Do the truncation
record->SetStoredContentLength(expectedStreamLength);
randomStream = new RandomStream(0);
randomStream->Skip(CACHE_KEY_LENGTH + CACHE_METADATA_LENGTH + 1);
TestReadStream(record, randomStream, expectedStreamLength);
WaitForEvents();
}
return NS_OK;
}
// Write known data to random offsets in a single cache entry and test
// resulting stream for correctness.
nsresult
TestOffsetWrites(nsINetDataCache *cache)
{
nsresult rv;
nsCOMPtr<nsINetDataCacheRecord> record;
nsCOMPtr<nsIChannel> channel;
nsCOMPtr<nsIOutputStream> outStream;
char buf[512];
char cacheKey[CACHE_KEY_LENGTH];
RandomStream *randomStream;
randomStream = new RandomStream(0);
randomStream->Read(cacheKey, sizeof cacheKey);
rv = cache->GetCachedNetData(cacheKey, sizeof cacheKey, getter_AddRefs(record));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't access record via opaque cache key");
nsCOMPtr<nsIFileSpec> file ;
record->GetFilename(getter_AddRefs(file)) ;
char* name ;
file->GetUnixStyleFilePath(&name) ;
printf(" file name is %s \n", name) ;
// Write buffer-fulls of data at random offsets into the cache entry.
// Data written is (offset % 0xff)
PRUint32 startingOffset;
PRUint32 streamLength = 0;
PRUint32 len = 0 ;
CounterStream *counterStream;
int i = 0;
for (i = 0; i < 257; i++) {
rv = record->NewChannel(0, getter_AddRefs(channel));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
startingOffset = streamLength ? streamLength - (randomStream->Next() % sizeof buf): 0;
rv = channel->OpenOutputStream(startingOffset, getter_AddRefs(outStream));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
truncate(name, startingOffset) ;
counterStream = new CounterStream(startingOffset);
counterStream->Read(buf, sizeof buf);
nsresult status ;
nsCOMPtr<nsIRandomAccessStore> ras = do_QueryInterface(outStream, &status);
if (NS_FAILED(status)) {
// mState = END_WRITE;
return NS_ERROR_FAILURE;
}
PRIntn offset ;
ras->Tell(&offset) ;
// printf(" offset is %d \n", offset) ;
PRUint32 numWritten;
rv = outStream->Write(buf, sizeof buf, &numWritten);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(numWritten == sizeof buf, "Write() bug?");
streamLength = startingOffset + sizeof buf;
rv = outStream->Close();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't close channel");
delete counterStream;
record->GetStoredContentLength(&len) ;
if(len != streamLength)
printf(" offset = %d is wrong, filesize = %d\n", startingOffset, len) ;
}
/*
rv = record->NewChannel(0, getter_AddRefs(channel));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
startingOffset = 208;
rv = channel->OpenOutputStream(startingOffset, getter_AddRefs(outStream));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
counterStream = new CounterStream(startingOffset);
counterStream->Read(buf, sizeof buf);
nsresult status ;
nsCOMPtr<nsIRandomAccessStore> ras = do_QueryInterface(outStream, &status);
if (NS_FAILED(status)) {
// mState = END_WRITE;
return NS_ERROR_FAILURE;
}
PRIntn offset = 0 ;
ras->Tell(&offset) ;
printf(" offset is %d \n", offset) ;
PRUint32 numWritten;
rv = outStream->Write(buf, sizeof buf, &numWritten);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(numWritten == sizeof buf, "Write() bug?");
streamLength = startingOffset + sizeof buf;
rv = outStream->Close();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't close channel");
delete counterStream;
record->GetStoredContentLength(&len) ;
if(len != streamLength)
printf(" offset = %d is wrong, filesize = %d\n", startingOffset, len) ;
*/
delete randomStream;
counterStream = new CounterStream(0);
TestReadStream(record, counterStream, streamLength);
WaitForEvents();
return NS_OK;
}
// Create entries in the network data cache, using random data for the
// key, the meta-data and the stored content data.
nsresult
FillCache(nsINetDataCache *cache)
{
nsresult rv;
PRBool inCache;
nsCOMPtr<nsINetDataCacheRecord> record;
nsCOMPtr<nsIChannel> channel;
nsCOMPtr<nsIOutputStream> outStream;
char buf[1000];
PRUint32 metaDataLength;
char cacheKey[CACHE_KEY_LENGTH];
char metaData[CACHE_METADATA_LENGTH];
PRUint32 testNum;
char *data;
RandomStream *randomStream;
PRIntervalTime startTime = PR_IntervalNow();
for (testNum = 0; testNum < NUM_CACHE_ENTRIES; testNum++) {
randomStream = new RandomStream(testNum);
randomStream->Read(cacheKey, sizeof cacheKey);
// No entry should be in cache until we add it
rv = cache->Contains(cacheKey, sizeof cacheKey, &inCache);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(!inCache, "nsINetDataCache::Contains error");
rv = cache->GetCachedNetData(cacheKey, sizeof cacheKey, getter_AddRefs(record));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't access record via opaque cache key");
// Test nsINetDataCacheRecord::GetRecordID()
rv = record->GetRecordID(&recordID[testNum]);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get Record ID");
// Test nsINetDataCache::GetNumEntries()
PRUint32 numEntries = (PRUint32)-1;
rv = cache->GetNumEntries(&numEntries);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get number of cache entries");
NS_ASSERTION(numEntries == testNum + 1, "GetNumEntries failure");
// Record meta-data should be initially empty
rv = record->GetMetaData(&metaDataLength, &data);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
if ((metaDataLength != 0) || (data != 0))
return NS_ERROR_FAILURE;
// Store random data as meta-data
randomStream->Read(metaData, sizeof metaData);
record->SetMetaData(sizeof metaData, metaData);
rv = record->NewChannel(0, getter_AddRefs(channel));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
rv = channel->OpenOutputStream(0, getter_AddRefs(outStream));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
PRUint32 beforeOccupancy;
rv = cache->GetStorageInUse(&beforeOccupancy);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get cache occupancy");
int streamLength = randomStream->Next() & 0xffff;
int remaining = streamLength;
while (remaining) {
PRUint32 numWritten;
int amount = PR_MIN(sizeof buf, remaining);
randomStream->Read(buf, amount);
rv = outStream->Write(buf, amount, &numWritten);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(numWritten == (PRUint32)amount, "Write() bug?");
remaining -= amount;
}
outStream->Close();
PRUint32 afterOccupancy;
rv = cache->GetStorageInUse(&afterOccupancy);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get cache occupancy");
PRUint32 streamLengthInKB = streamLength >> 10;
NS_ASSERTION((afterOccupancy - beforeOccupancy) >= streamLengthInKB,
"nsINetDataCache::GetStorageInUse() is busted");
// *Now* there should be an entry in the cache
rv = cache->Contains(cacheKey, sizeof cacheKey, &inCache);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(inCache, "nsINetDataCache::Contains error");
delete randomStream;
}
PRIntervalTime endTime = PR_IntervalNow();
return NS_OK;
}
nsresult NS_AutoregisterComponents()
{
nsresult rv = nsComponentManager::AutoRegister(nsIComponentManager::NS_Startup,
NULL /* default */);
return rv;
}
PRBool initPref ()
{
nsresult rv;
NS_WITH_SERVICE(nsIPref, prefPtr, kPrefCID, &rv);
if (NS_FAILED(rv))
return false;
nsCOMPtr<nsIFileSpec> fileSpec;
rv = NS_NewFileSpec (getter_AddRefs(fileSpec));
if (NS_FAILED(rv))
return false;
nsCString defaultPrefFile = PR_GetEnv ("MOZILLA_FIVE_HOME");
if (defaultPrefFile.Length())
defaultPrefFile += "/";
else
defaultPrefFile = "./";
defaultPrefFile += "default_prefs.js";
fileSpec->SetUnixStyleFilePath (defaultPrefFile.GetBuffer());
PRBool exists = false;
fileSpec->Exists(&exists);
if (exists)
prefPtr->ReadUserPrefsFrom(fileSpec);
else
return false;
return true;
}
int
main(int argc, char* argv[])
{
initPref() ;
nsresult rv;
nsCOMPtr<nsINetDataCache> cache;
rv = NS_AutoregisterComponents();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't register XPCOM components");
rv = nsComponentManager::CreateInstance(kDiskCacheCID, nsnull,
NS_GET_IID(nsINetDataCache),
getter_AddRefs(cache));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't create memory cache factory");
InitQueue();
PRUnichar* description;
rv = cache->GetDescription(&description);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get cache description");
nsCAutoString descStr(description);
printf("Testing: %s\n", descStr.GetBuffer());
rv = cache->RemoveAll();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't clear cache");
PRUint32 startOccupancy;
rv = cache->GetStorageInUse(&startOccupancy);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get cache occupancy");
PRUint32 numEntries = (PRUint32)-1;
rv = cache->GetNumEntries(&numEntries);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get number of cache entries");
NS_ASSERTION(numEntries == 0, "Couldn't clear cache");
rv = FillCache(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't fill cache with random test data");
rv = TestRead(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't read random test data from cache");
rv = TestRecordID(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't index records using record ID");
rv = TestEnumeration(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't successfully enumerate records");
rv = TestTruncation(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't successfully truncate records");
rv = TestOffsetWrites(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't successfully write to records using non-zero offsets");
rv = cache->RemoveAll();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't clear cache");
rv = cache->GetNumEntries(&numEntries);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get number of cache entries");
NS_ASSERTION(numEntries == 0, "Couldn't clear cache");
PRUint32 endOccupancy;
rv = cache->GetStorageInUse(&endOccupancy);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get cache occupancy");
NS_ASSERTION(startOccupancy == endOccupancy, "Cache occupancy not correctly computed ?");
return 0;
}

View File

@@ -1,46 +0,0 @@
# Generated automatically from Makefile.in by configure.
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
#
DEPTH = ../../..
topsrcdir = @top_srcdir@
VPATH = @srcdir@
srcdir = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = nkcache
LIBRARY_NAME = nkmemcache_s
REQUIRES = nspr dbm
EXPORTS=nsMemCacheCID.h
CPPSRCS = \
nsMemCache.cpp \
nsMemCacheRecord.cpp \
nsMemCacheChannel.cpp \
$(NULL)
# we don't want the shared lib, but we want to force the creation of a
# static lib.
override NO_SHARED_LIB=1
override NO_STATIC_LIB=
include $(topsrcdir)/config/rules.mk

View File

@@ -1,42 +0,0 @@
#!nmake
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
DEPTH=..\..\..
include <$(DEPTH)/config/config.mak>
MODULE = nkcache
LIBRARY_NAME = nkmemcache_s
CPP_OBJS = \
.\$(OBJDIR)\nsMemCache.obj \
.\$(OBJDIR)\nsMemCacheRecord.obj \
.\$(OBJDIR)\nsMemCacheChannel.obj \
$(NULL)
EXPORTS=nsMemCacheCID.h
include <$(DEPTH)\config\rules.mak>
install:: $(LIBRARY)
$(MAKE_INSTALL) $(LIBRARY) $(DIST)\lib
clobber::
rm -rf $(OBJDIR)
rm -f $(DIST)\lib\$(LIBRARY_NAME).lib

View File

@@ -1,334 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
/**
* nsMemCache is the implementation of an in-memory network-data
* cache, used to cache the responses to network retrieval commands.
* Each cache entry may contain both content, e.g. GIF image data, and
* associated metadata, e.g. HTTP headers. Each entry is indexed by
* two different keys: a record id number and an opaque key, which is
* created by the cache manager by combining the URI with a "secondary
* key", e.g. HTTP post data.
*/
#include "nsMemCache.h"
#include "nsMemCacheRecord.h"
#include "nsIGenericFactory.h"
#include "nsString.h"
#include "nsHashtable.h"
#include "nsHashtableEnumerator.h"
#include "nsEnumeratorUtils.h"
PRInt32 nsMemCache::gRecordSerialNumber = 0;
nsMemCache::nsMemCache()
: mNumEntries(0), mOccupancy(0), mEnabled(PR_TRUE),
mHashTable(0)
{
NS_INIT_REFCNT();
}
nsMemCache::~nsMemCache()
{
nsresult rv;
rv = RemoveAll();
NS_ASSERTION(NS_SUCCEEDED(rv) && (mNumEntries == 0),
"Failure to shut down memory cache. "
"Somewhere, someone is holding references to at least one cache record");
delete mHashTable;
}
nsresult
nsMemCache::Init()
{
mHashTable = new nsHashtable(256);
if (!mHashTable)
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
NS_IMPL_ISUPPORTS(nsMemCache, NS_GET_IID(nsINetDataCache))
NS_IMETHODIMP
nsMemCache::GetDescription(PRUnichar * *aDescription)
{
nsAutoString description("Memory Cache");
*aDescription = description.ToNewUnicode();
if (!*aDescription)
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::Contains(const char *aKey, PRUint32 aKeyLength, PRBool *aFound)
{
nsOpaqueKey *opaqueKey = new nsOpaqueKey(aKey, aKeyLength);
if (!opaqueKey)
return NS_ERROR_OUT_OF_MEMORY;
*aFound = mHashTable->Exists(opaqueKey);
delete opaqueKey;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::GetCachedNetData(const char *aKey, PRUint32 aKeyLength,
nsINetDataCacheRecord* *aRecord)
{
nsresult rv;
nsMemCacheRecord* record = 0;
nsOpaqueKey *opaqueKey2 = 0;
nsOpaqueKey *opaqueKey3 = 0;
nsOpaqueKey *opaqueKey;
opaqueKey = new nsOpaqueKey(aKey, aKeyLength);
if (!opaqueKey)
goto out_of_memory;
record = (nsMemCacheRecord*)mHashTable->Get(opaqueKey);
delete opaqueKey;
// No existing cache database entry was found. Create a new one.
// This requires two mappings in the hash table:
// Record ID ==> record
// Opaque key ==> record
if (!record) {
record = new nsMemCacheRecord;
if (!record)
goto out_of_memory;
rv = record->Init(aKey, aKeyLength, ++gRecordSerialNumber, this);
if (NS_FAILED(rv)) goto out_of_memory;
// Index the record by opaque key
opaqueKey2 = new nsOpaqueKey(record->mKey, record->mKeyLength);
if (!opaqueKey2) goto out_of_memory;
mHashTable->Put(opaqueKey2, record);
// Index the record by it's record ID
char *recordIDbytes = NS_REINTERPRET_CAST(char *, &record->mRecordID);
opaqueKey3 = new nsOpaqueKey(recordIDbytes,
sizeof record->mRecordID);
if (!opaqueKey3) {
// Clean up the first record from the hash table
mHashTable->Remove(opaqueKey);
goto out_of_memory;
}
mHashTable->Put(opaqueKey3, record);
// The hash table holds on to the record
record->AddRef();
delete opaqueKey2;
delete opaqueKey3;
mNumEntries++;
}
record->AddRef();
*aRecord = record;
return NS_OK;
out_of_memory:
delete opaqueKey2;
delete opaqueKey3;
delete record;
return NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHODIMP
nsMemCache::GetCachedNetDataByID(PRInt32 RecordID,
nsINetDataCacheRecord* *aRecord)
{
nsOpaqueKey opaqueKey(NS_REINTERPRET_CAST(const char *, &RecordID),
sizeof RecordID);
*aRecord = (nsINetDataCacheRecord*)mHashTable->Get(&opaqueKey);
if (*aRecord) {
NS_ADDREF(*aRecord);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_METHOD
nsMemCache::Delete(nsMemCacheRecord* aRecord)
{
nsMemCacheRecord *removedRecord;
char *recordIDbytes = NS_REINTERPRET_CAST(char *, &aRecord->mRecordID);
nsOpaqueKey opaqueRecordIDKey(recordIDbytes,
sizeof aRecord->mRecordID);
removedRecord = (nsMemCacheRecord*)mHashTable->Remove(&opaqueRecordIDKey);
NS_ASSERTION(removedRecord == aRecord, "memory cache database inconsistent");
nsOpaqueKey opaqueKey(aRecord->mKey, aRecord->mKeyLength);
removedRecord = (nsMemCacheRecord*)mHashTable->Remove(&opaqueKey);
NS_ASSERTION(removedRecord == aRecord, "memory cache database inconsistent");
aRecord->Release();
mNumEntries--;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::GetEnabled(PRBool *aEnabled)
{
NS_ENSURE_ARG(aEnabled);
*aEnabled = mEnabled;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::SetEnabled(PRBool aEnabled)
{
mEnabled = aEnabled;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::GetFlags(PRUint32 *aFlags)
{
NS_ENSURE_ARG(aFlags);
*aFlags = MEMORY_CACHE;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::GetNumEntries(PRUint32 *aNumEntries)
{
NS_ENSURE_ARG(aNumEntries);
*aNumEntries = mNumEntries;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::GetMaxEntries(PRUint32 *aMaxEntries)
{
NS_ENSURE_ARG(aMaxEntries);
*aMaxEntries = MEM_CACHE_MAX_ENTRIES;
return NS_OK;
}
static NS_METHOD
HashEntryConverter(nsHashKey *aKey, void *aValue,
void *unused, nsISupports **retval)
{
nsMemCacheRecord *record;
nsOpaqueKey *opaqueKey;
record = (nsMemCacheRecord*)aValue;
opaqueKey = (nsOpaqueKey*)aKey;
// Hash table keys that index cache entries by their record ID
// shouldn't be enumerated.
if ((opaqueKey->GetKeyLength() == sizeof(PRInt32))) {
#ifdef DEBUG
PRInt32 recordID;
record->GetRecordID(&recordID);
NS_ASSERTION(*((PRInt32*)opaqueKey->GetKey()) == recordID,
"Key has incorrect key length");
#endif
return NS_ERROR_FAILURE;
}
NS_IF_ADDREF(record);
*retval = NS_STATIC_CAST(nsISupports*, record);
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::NewCacheEntryIterator(nsISimpleEnumerator* *aIterator)
{
nsCOMPtr<nsIEnumerator> iterator;
NS_ENSURE_ARG(aIterator);
NS_NewHashtableEnumerator(mHashTable, HashEntryConverter,
mHashTable, getter_AddRefs(iterator));
return NS_NewAdapterEnumerator(aIterator, iterator);
}
NS_IMETHODIMP
nsMemCache::GetNextCache(nsINetDataCache* *aNextCache)
{
NS_ENSURE_ARG(aNextCache);
*aNextCache = mNextCache;
NS_ADDREF(*aNextCache);
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::SetNextCache(nsINetDataCache* aNextCache)
{
mNextCache = aNextCache;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::GetStorageInUse(PRUint32 *aStorageInUse)
{
NS_ENSURE_ARG(aStorageInUse);
// Convert from bytes to KB
*aStorageInUse = (mOccupancy >> 10);
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::RemoveAll(void)
{
PRBool failed;
nsCOMPtr<nsISimpleEnumerator> iterator;
nsCOMPtr<nsISupports> recordSupports;
nsCOMPtr<nsINetDataCacheRecord> record;
nsresult rv;
failed = PR_FALSE;
rv = NewCacheEntryIterator(getter_AddRefs(iterator));
if (NS_FAILED(rv))
return rv;
PRBool notDone;
while (1) {
rv = iterator->HasMoreElements(&notDone);
if (NS_FAILED(rv)) return rv;
if (!notDone)
break;
iterator->GetNext(getter_AddRefs(recordSupports));
record = do_QueryInterface(recordSupports);
recordSupports = 0;
PRUint32 bytesUsed;
record->GetStoredContentLength(&bytesUsed);
rv = record->Delete();
if (NS_FAILED(rv)) {
failed = PR_TRUE;
continue;
}
mOccupancy -= bytesUsed;
}
if (failed)
return NS_ERROR_FAILURE;
return NS_OK;
}

View File

@@ -1,83 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
/**
* nsMemCache is the implementation of an in-memory network-data
* cache, used to cache the responses to network retrieval commands.
* Each cache entry may contain both content, e.g. GIF image data, and
* associated metadata, e.g. HTTP headers. Each entry is indexed by
* two different keys: a record id number and an opaque key, which is
* created by the cache manager by combining the URI with a "secondary
* key", e.g. HTTP post data.
*/
#ifndef _nsMemCache_h_
#define _nsMemCache_h_
#include "nsINetDataCache.h"
// Maximum number of URIs that may be resident in the cache
#define MEM_CACHE_MAX_ENTRIES 1000
#define MEM_CACHE_SEGMENT_SIZE (1 << 12)
#define MEM_CACHE_MAX_ENTRY_SIZE (1 << 20)
class nsHashtable;
class nsMemCacheRecord;
class nsMemCache : public nsINetDataCache
{
public:
nsMemCache();
virtual ~nsMemCache();
nsresult Init();
// nsISupports methods
NS_DECL_ISUPPORTS
// nsINetDataCache methods
NS_DECL_NSINETDATACACHE
// Factory
static NS_METHOD nsMemCacheConstructor(nsISupports *aOuter, REFNSIID aIID,
void **aResult);
protected:
PRUint32 mNumEntries;
PRUint32 mOccupancy; // Memory used, in bytes
PRBool mEnabled; // If false, bypass mem cache
nsINetDataCache* mNextCache;
// Mapping from either opaque key or record ID to nsMemCacheRecord
nsHashtable* mHashTable;
// Used to assign record ID's
static PRInt32 gRecordSerialNumber;
NS_METHOD Delete(nsMemCacheRecord* aRecord);
friend class nsMemCacheRecord;
friend class nsMemCacheChannel;
};
#endif // _nsMemCache_h_

View File

@@ -1,36 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
// XPCOM Class ID for the network data in-memory cache
#ifndef nsMEMCACHECID_h__
#define nsMEMCACHECID_h__
// {e4710560-7de2-11d3-90cb-0040056a906e}
#define NS_MEM_CACHE_FACTORY_CID \
{ \
0xe4710560, \
0x7de2, \
0x11d3, \
{0x90, 0xcb, 0x00, 0x40, 0x05, 0x6a, 0x90, 0x6e} \
}
#endif // nsMEMCACHECID_h__

View File

@@ -1,464 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
#include "nsMemCache.h"
#include "nsMemCacheChannel.h"
#include "nsIStreamListener.h"
#include "nsIChannel.h"
#include "nsIStorageStream.h"
#include "nsIOutputStream.h"
#include "nsIServiceManager.h"
#include "nsIEventQueueService.h"
#include "nsNetUtil.h"
#include "nsILoadGroup.h"
static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
static NS_DEFINE_CID(kEventQueueService, NS_EVENTQUEUESERVICE_CID);
NS_IMPL_ISUPPORTS(nsMemCacheChannel, NS_GET_IID(nsIChannel))
void
nsMemCacheChannel::NotifyStorageInUse(PRInt32 aBytesUsed)
{
mRecord->mCache->mOccupancy += aBytesUsed;
}
/**
* This class acts as an adaptor around a synchronous input stream to add async
* read capabilities. It adds methods for initiating, suspending, resuming and
* cancelling async reads.
*/
class AsyncReadStreamAdaptor : public nsIInputStream {
public:
AsyncReadStreamAdaptor(nsMemCacheChannel* aChannel, nsIInputStream *aSyncStream):
mSyncStream(aSyncStream), mDataAvailCursor(0),
mRemaining(0), mChannel(aChannel), mAvailable(0), mAborted(false), mSuspended(false)
{
NS_INIT_REFCNT();
NS_ADDREF(mChannel);
}
virtual ~AsyncReadStreamAdaptor() {
mChannel->mAsyncReadStream = 0;
NS_RELEASE(mChannel);
}
NS_DECL_ISUPPORTS
nsresult
IsPending(PRBool* aIsPending) {
*aIsPending = (mRemaining != 0) && !mAborted;
return NS_OK;
}
nsresult
Cancel(void) {
mAborted = true;
return mStreamListener->OnStopRequest(mChannel, mContext, NS_BINDING_ABORTED, nsnull);
}
nsresult
Suspend(void) { mSuspended = true; return NS_OK; }
nsresult
Resume(void) {
if (!mSuspended)
return NS_ERROR_FAILURE;
mSuspended = false;
return NextListenerEvent();
}
NS_IMETHOD
Available(PRUint32 *aNumBytes) { return mAvailable; }
NS_IMETHOD
Read(char* aBuf, PRUint32 aCount, PRUint32 *aBytesRead) {
if (mAborted)
return NS_ERROR_ABORT;
*aBytesRead = 0;
aCount = PR_MIN(aCount, mAvailable);
nsresult rv = mSyncStream->Read(aBuf, aCount, aBytesRead);
mAvailable -= *aBytesRead;
if (NS_FAILED(rv) && (rv != NS_BASE_STREAM_WOULD_BLOCK)) {
Fail();
return rv;
}
if (!mSuspended && !mAvailable) {
rv = NextListenerEvent();
if (NS_FAILED(rv)) {
Fail();
return rv;
}
}
return NS_OK;
}
NS_IMETHOD
Close() {
nsresult rv = mSyncStream->Close();
mSyncStream = 0;
mContext = 0;
mStreamListener = 0;
return rv;
}
nsresult
AsyncRead(PRUint32 aStartPosition, PRInt32 aReadCount,
nsISupports* aContext, nsIStreamListener* aListener) {
nsresult rv;
nsIEventQueue *eventQ;
mContext = aContext;
mStreamListener = aListener;
mRemaining = aReadCount;
NS_WITH_SERVICE(nsIIOService, serv, kIOServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
NS_WITH_SERVICE(nsIEventQueueService, eventQService, kEventQueueService, &rv);
if (NS_FAILED(rv)) return rv;
rv = eventQService->GetThreadEventQueue(PR_CurrentThread(), &eventQ);
if (NS_FAILED(rv)) return rv;
rv = NS_NewAsyncStreamListener(aListener, eventQ,
getter_AddRefs(mStreamListener));
NS_RELEASE(eventQ);
if (NS_FAILED(rv)) return rv;
rv = mStreamListener->OnStartRequest(mChannel, aContext);
if (NS_FAILED(rv)) return rv;
return NextListenerEvent();
}
protected:
nsresult
Fail(void) {
mAborted = true;
return mStreamListener->OnStopRequest(mChannel, mContext, NS_BINDING_FAILED, nsnull);
}
nsresult
NextListenerEvent() {
PRUint32 available;
nsresult rv = mSyncStream->Available(&available);
if (NS_FAILED(rv)) return rv;
available -= mAvailable;
available = PR_MIN(available, mRemaining);
if (available) {
PRUint32 size = PR_MIN(available, MEM_CACHE_SEGMENT_SIZE);
rv = mStreamListener->OnDataAvailable(mChannel, mContext, this,
mDataAvailCursor, size);
mDataAvailCursor += size;
mRemaining -= size;
mAvailable += size;
return rv;
} else {
rv = mStreamListener->OnStopRequest(mChannel, mContext, NS_OK, nsnull);
AsyncReadStreamAdaptor* thisAlias = this;
NS_RELEASE(thisAlias);
return rv;
}
}
private:
nsCOMPtr<nsISupports> mContext; // Opaque context passed to AsyncRead()
nsCOMPtr<nsIStreamListener> mStreamListener; // Stream listener that has been proxied
nsCOMPtr<nsIInputStream> mSyncStream; // Underlying synchronous stream that is
// being converted to an async stream
PRUint32 mDataAvailCursor;
PRUint32 mRemaining; // Size of AsyncRead request less bytes for
// consumer OnDataAvailable's that were fired
PRUint32 mAvailable; // Number of bytes for which OnDataAvailable fired
nsMemCacheChannel* mChannel; // Associated memory cache channel, strong link
// but can not use nsCOMPtr
bool mAborted; // Abort() has been called
bool mSuspended; // Suspend() has been called
};
NS_IMPL_ISUPPORTS(AsyncReadStreamAdaptor, NS_GET_IID(nsIInputStream))
// The only purpose of this output stream wrapper is to adjust the cache's
// overall occupancy as new data flows into the cache entry.
class MemCacheWriteStreamWrapper : public nsIOutputStream {
public:
MemCacheWriteStreamWrapper(nsMemCacheChannel* aChannel, nsIOutputStream *aBaseStream):
mBaseStream(aBaseStream), mChannel(aChannel)
{
NS_INIT_REFCNT();
NS_ADDREF(mChannel);
}
virtual ~MemCacheWriteStreamWrapper() { NS_RELEASE(mChannel); };
static nsresult
Create(nsMemCacheChannel* aChannel, nsIOutputStream *aBaseStream, nsIOutputStream* *aWrapper) {
MemCacheWriteStreamWrapper *wrapper =
new MemCacheWriteStreamWrapper(aChannel, aBaseStream);
if (!wrapper) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(wrapper);
*aWrapper = wrapper;
return NS_OK;
}
NS_DECL_ISUPPORTS
NS_IMETHOD
Write(const char *aBuffer, PRUint32 aCount, PRUint32 *aNumWritten) {
*aNumWritten = 0;
nsresult rv = mBaseStream->Write(aBuffer, aCount, aNumWritten);
mChannel->NotifyStorageInUse(*aNumWritten);
return rv;
}
NS_IMETHOD
Flush() { return mBaseStream->Flush(); }
NS_IMETHOD
Close() { return mBaseStream->Close(); }
private:
nsCOMPtr<nsIOutputStream> mBaseStream;
nsMemCacheChannel* mChannel;
};
NS_IMPL_ISUPPORTS(MemCacheWriteStreamWrapper, NS_GET_IID(nsIOutputStream))
nsMemCacheChannel::nsMemCacheChannel(nsMemCacheRecord *aRecord, nsILoadGroup *aLoadGroup)
: mRecord(aRecord)
{
NS_INIT_REFCNT();
mRecord->mNumChannels++;
}
nsMemCacheChannel::~nsMemCacheChannel()
{
mRecord->mNumChannels--;
}
NS_IMETHODIMP
nsMemCacheChannel::IsPending(PRBool* aIsPending)
{
*aIsPending = PR_FALSE;
if (!mAsyncReadStream)
return NS_OK;
return mAsyncReadStream->IsPending(aIsPending);
}
NS_IMETHODIMP
nsMemCacheChannel::Cancel(void)
{
if (!mAsyncReadStream)
return NS_ERROR_FAILURE;
return mAsyncReadStream->Cancel();
}
NS_IMETHODIMP
nsMemCacheChannel::Suspend(void)
{
if (!mAsyncReadStream)
return NS_ERROR_FAILURE;
return mAsyncReadStream->Suspend();
}
NS_IMETHODIMP
nsMemCacheChannel::Resume(void)
{
if (!mAsyncReadStream)
return NS_ERROR_FAILURE;
return mAsyncReadStream->Resume();
}
NS_IMETHODIMP
nsMemCacheChannel::GetOriginalURI(nsIURI * *aURI)
{
// Not required
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::GetURI(nsIURI * *aURI)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::OpenInputStream(PRUint32 aStartPosition, PRInt32 aReadCount,
nsIInputStream* *aResult)
{
nsresult rv;
NS_ENSURE_ARG(aResult);
if (mInputStream)
return NS_ERROR_NOT_AVAILABLE;
rv = mRecord->mStorageStream->NewInputStream(aStartPosition, getter_AddRefs(mInputStream));
*aResult = mInputStream;
NS_ADDREF(*aResult);
return rv;
}
NS_IMETHODIMP
nsMemCacheChannel::OpenOutputStream(PRUint32 startPosition, nsIOutputStream* *aResult)
{
nsresult rv;
NS_ENSURE_ARG(aResult);
nsCOMPtr<nsIOutputStream> outputStream;
PRUint32 oldLength;
mRecord->mStorageStream->GetLength(&oldLength);
rv = mRecord->mStorageStream->GetOutputStream(startPosition, getter_AddRefs(outputStream));
if (NS_FAILED(rv)) return rv;
if (startPosition < oldLength)
NotifyStorageInUse(startPosition - oldLength);
return MemCacheWriteStreamWrapper::Create(this, outputStream, aResult);
}
NS_IMETHODIMP
nsMemCacheChannel::AsyncOpen(nsIStreamObserver *observer, nsISupports *ctxt)
{
// Not required
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::AsyncRead(PRUint32 aStartPosition, PRInt32 aReadCount,
nsISupports *aContext, nsIStreamListener *aListener)
{
nsCOMPtr<nsIInputStream> inputStream;
nsresult rv = OpenInputStream(aStartPosition, aReadCount, getter_AddRefs(inputStream));
if (NS_FAILED(rv)) return rv;
AsyncReadStreamAdaptor *asyncReadStreamAdaptor;
asyncReadStreamAdaptor = new AsyncReadStreamAdaptor(this, inputStream);
if (!asyncReadStreamAdaptor)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(asyncReadStreamAdaptor);
mAsyncReadStream = asyncReadStreamAdaptor;
rv = asyncReadStreamAdaptor->AsyncRead(aStartPosition, aReadCount, aContext, aListener);
if (NS_FAILED(rv))
delete asyncReadStreamAdaptor;
return rv;
}
NS_IMETHODIMP
nsMemCacheChannel::AsyncWrite(nsIInputStream *fromStream, PRUint32 startPosition,
PRInt32 writeCount, nsISupports *ctxt,
nsIStreamObserver *observer)
{
// Not required to be implemented
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::GetLoadAttributes(nsLoadFlags *aLoadAttributes)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::SetLoadAttributes(nsLoadFlags aLoadAttributes)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::GetContentType(char* *aContentType)
{
// Not required to be implemented, since it is implemented by cache manager
// NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
// FIXME - lying for the purpose of testing
*aContentType = strdup("text/html");
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheChannel::GetContentLength(PRInt32 *aContentLength)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::GetOwner(nsISupports* *aOwner)
{
*aOwner = mOwner.get();
NS_IF_ADDREF(*aOwner);
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheChannel::SetOwner(nsISupports* aOwner)
{
// Not required to be implemented, since it is implemented by cache manager
mOwner = aOwner;
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}

View File

@@ -1,61 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
#ifndef _nsMemCacheChannel_h_
#define _nsMemCacheChannel_h_
#include "nsMemCacheRecord.h"
#include "nsIChannel.h"
#include "nsIInputStream.h"
#include "nsCOMPtr.h"
class AsyncReadStreamAdaptor;
class nsMemCacheChannel : public nsIChannel
{
public:
// Constructors and Destructor
nsMemCacheChannel(nsMemCacheRecord *aRecord, nsILoadGroup *aLoadGroup);
virtual ~nsMemCacheChannel();
// Declare nsISupports methods
NS_DECL_ISUPPORTS
// Declare nsIRequest methods
NS_DECL_NSIREQUEST
// Declare nsIChannel methods
NS_DECL_NSICHANNEL
protected:
void NotifyStorageInUse(PRInt32 aBytesUsed);
nsCOMPtr<nsMemCacheRecord> mRecord;
nsCOMPtr<nsIInputStream> mInputStream;
nsCOMPtr<nsISupports> mOwner;
AsyncReadStreamAdaptor* mAsyncReadStream; // non-owning pointer
friend class MemCacheWriteStreamWrapper;
friend class AsyncReadStreamAdaptor;
};
#endif // _nsMemCacheChannel_h_

View File

@@ -1,164 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
#include "nsMemCache.h"
#include "nsMemCacheRecord.h"
#include "nsMemCacheChannel.h"
#include "nsIAllocator.h"
#include "nsStorageStream.h"
static NS_DEFINE_IID(kINetDataCacheRecord, NS_INETDATACACHERECORD_IID);
nsMemCacheRecord::nsMemCacheRecord()
: mKey(0), mKeyLength(0), mMetaData(0), mMetaDataLength(0), mNumChannels(0)
{
NS_INIT_REFCNT();
}
nsMemCacheRecord::~nsMemCacheRecord()
{
if (mMetaData)
delete[] mMetaData;
if (mKey)
delete[] mKey;
}
NS_IMPL_ISUPPORTS(nsMemCacheRecord, NS_GET_IID(nsINetDataCacheRecord))
NS_IMETHODIMP
nsMemCacheRecord::GetKey(PRUint32 *aLength, char **aResult)
{
NS_ENSURE_ARG(aResult);
*aResult = (char *)nsAllocator::Alloc(mKeyLength);
if (!*aResult)
return NS_ERROR_OUT_OF_MEMORY;
memcpy(*aResult, mKey, mKeyLength);
*aLength = mKeyLength;
return NS_OK;
}
nsresult
nsMemCacheRecord::Init(const char *aKey, PRUint32 aKeyLength,
PRUint32 aRecordID, nsMemCache *aCache)
{
nsresult rv;
NS_ASSERTION(!mKey, "Memory cache record key set multiple times");
rv = NS_NewStorageStream(MEM_CACHE_SEGMENT_SIZE, MEM_CACHE_MAX_ENTRY_SIZE,
getter_AddRefs(mStorageStream));
if (NS_FAILED(rv)) return rv;
mKey = new char[aKeyLength];
if (!mKey)
return NS_ERROR_OUT_OF_MEMORY;
memcpy(mKey, aKey, aKeyLength);
mKeyLength = aKeyLength;
mRecordID = aRecordID;
mCache = aCache;
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheRecord::GetRecordID(PRInt32 *aRecordID)
{
NS_ENSURE_ARG(aRecordID);
*aRecordID = mRecordID;
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheRecord::GetMetaData(PRUint32 *aLength, char **aResult)
{
NS_ENSURE_ARG(aResult);
*aResult = 0;
if (mMetaDataLength) {
*aResult = (char*)nsAllocator::Alloc(mMetaDataLength);
if (!*aResult)
return NS_ERROR_OUT_OF_MEMORY;
memcpy(*aResult, mMetaData, mMetaDataLength);
}
*aLength = mMetaDataLength;
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheRecord::SetMetaData(PRUint32 aLength, const char *aData)
{
if (mMetaData)
delete[] mMetaData;
mMetaData = new char[aLength];
if (!mMetaData)
return NS_ERROR_OUT_OF_MEMORY;
memcpy(mMetaData, aData, aLength);
mMetaDataLength = aLength;
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheRecord::GetStoredContentLength(PRUint32 *aStoredContentLength)
{
NS_ENSURE_ARG(aStoredContentLength);
return mStorageStream->GetLength(aStoredContentLength);
}
NS_IMETHODIMP
nsMemCacheRecord::SetStoredContentLength(PRUint32 aStoredContentLength)
{
PRUint32 before, after;
mStorageStream->GetLength(&before);
nsresult rv = mStorageStream->SetLength(aStoredContentLength);
if (NS_FAILED(rv)) return rv;
mStorageStream->GetLength(&after);
mCache->mOccupancy -= (before - after);
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheRecord::Delete(void)
{
if (mNumChannels)
return NS_ERROR_NOT_AVAILABLE;
return mCache->Delete(this);
}
NS_IMETHODIMP
nsMemCacheRecord::GetFilename(nsIFileSpec* *aFilename)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheRecord::NewChannel(nsILoadGroup *aLoadGroup, nsIChannel* *aResult)
{
NS_ENSURE_ARG(aResult);
nsMemCacheChannel* channel = new nsMemCacheChannel(this, aLoadGroup);
if (!channel)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(channel);
*aResult = NS_STATIC_CAST(nsIChannel*, channel);
return NS_OK;
}

View File

@@ -1,65 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
#ifndef _nsMemCacheRecord_h_
#define _nsMemCacheRecord_h_
#include "nsINetDataCacheRecord.h"
#include "nsCOMPtr.h"
class nsMemCache;
class nsIStorageStream;
class nsMemCacheRecord : public nsINetDataCacheRecord
{
public:
// Declare interface methods
NS_DECL_ISUPPORTS
NS_DECL_NSINETDATACACHERECORD
protected:
// Constructors and Destructor
nsMemCacheRecord();
virtual ~nsMemCacheRecord();
nsresult Init(const char *aKey, PRUint32 aKeyLength,
PRUint32 aRecordID, nsMemCache *aCache);
char* mKey; // opaque database key for this record
PRUint32 mKeyLength; // length, in bytes, of mKey
PRInt32 mRecordID; // An alternate key for this record
char* mMetaData; // opaque URI metadata
PRUint32 mMetaDataLength; // length, in bytes, of mMetaData
nsMemCache* mCache; // weak pointer to the cache database
// that this record inhabits
nsCOMPtr<nsIStorageStream> mStorageStream;
PRUint32 mNumChannels; // Count un-Release'ed nsIChannels
friend class nsMemCache;
friend class nsMemCacheChannel;
};
#endif // _nsMemCacheRecord_h_

View File

@@ -1,51 +0,0 @@
#
# The contents of this file are subject to the Netscape Public
# License Version 1.1 (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.org code.
#
# 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.
#
# Contributor(s):
#
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = nkcache
LIBRARY_NAME = nkcachemgr_s
REQUIRES = nspr
CPPSRCS = \
nsCacheManager.cpp \
nsCachedNetData.cpp \
nsReplacementPolicy.cpp \
nsCacheEntryChannel.cpp \
$(NULL)
LOCAL_INCLUDES = -I$(srcdir)/../public -I$(srcdir)/../include
EXTRA_LIBS = $(NSPR_LIBS)
# we don't want the shared lib, but we want to force the creation of a
# static lib.
override NO_SHARED_LIB=1
override NO_STATIC_LIB=
include $(topsrcdir)/config/rules.mk

View File

@@ -1,45 +0,0 @@
#!gmake
#
# The contents of this file are subject to the Netscape Public
# License Version 1.1 (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.org code.
#
# 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.
#
# Contributor(s):
DEPTH=..\..\..
include <$(DEPTH)/config/config.mak>
MODULE = nkcache
LIBRARY_NAME = nkcachemgr_s
CPP_OBJS = \
.\$(OBJDIR)\nsCacheManager.obj \
.\$(OBJDIR)\nsCachedNetData.obj \
.\$(OBJDIR)\nsReplacementPolicy.obj \
.\$(OBJDIR)\nsCacheEntryChannel.obj \
$(NULL)
include <$(DEPTH)\config\rules.mak>
install:: $(LIBRARY)
$(MAKE_INSTALL) $(LIBRARY) $(DIST)\lib
clobber::
rm -rf $(OBJDIR)
rm -f $(DIST)\lib\$(LIBRARY_NAME).lib

View File

@@ -1,260 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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) 1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#include "nsCacheManager.h"
#include "nsCacheEntryChannel.h"
#include "nsIOutputStream.h"
#include "nsIIOService.h"
#include "nsIServiceManager.h"
#include "nsIStreamListener.h"
nsCacheEntryChannel::nsCacheEntryChannel(nsCachedNetData* aCacheEntry, nsIChannel* aChannel,
nsILoadGroup* aLoadGroup):
nsChannelProxy(aChannel), mCacheEntry(aCacheEntry), mLoadGroup(aLoadGroup), mLoadAttributes(0)
{
NS_ASSERTION(aCacheEntry->mChannelCount < 0xFF, "Overflowed channel counter");
mCacheEntry->mChannelCount++;
NS_INIT_REFCNT();
}
nsCacheEntryChannel::~nsCacheEntryChannel()
{
mCacheEntry->mChannelCount--;
}
NS_IMPL_ISUPPORTS3(nsCacheEntryChannel, nsISupports, nsIChannel, nsIRequest)
// A proxy for nsIOutputStream
class CacheOutputStream : public nsIOutputStream {
public:
CacheOutputStream(nsIOutputStream *aOutputStream, nsCachedNetData *aCacheEntry):
mOutputStream(aOutputStream), mCacheEntry(aCacheEntry), mStartTime(PR_Now())
{ NS_INIT_REFCNT(); }
virtual ~CacheOutputStream() {}
NS_DECL_ISUPPORTS
NS_IMETHOD Close() {
mCacheEntry->NoteDownloadTime(mStartTime, PR_Now());
mCacheEntry->ClearFlag(nsCachedNetData::UPDATE_IN_PROGRESS);
return mOutputStream->Close();
}
NS_IMETHOD Flush() { return mOutputStream->Flush(); }
NS_IMETHOD
Write(const char *aBuf, PRUint32 aCount, PRUint32 *aActualBytes) {
nsresult rv;
*aActualBytes = 0;
rv = mOutputStream->Write(aBuf, aCount, aActualBytes);
mCacheEntry->mLogicalLength += *aActualBytes;
if (NS_FAILED(rv)) return rv;
nsCacheManager::LimitCacheSize();
return rv;
}
protected:
nsCOMPtr<nsIOutputStream> mOutputStream;
nsCOMPtr<nsCachedNetData> mCacheEntry;
// Time at which stream was opened
PRTime mStartTime;
};
NS_IMPL_ISUPPORTS(CacheOutputStream, NS_GET_IID(nsIOutputStream))
NS_IMETHODIMP
nsCacheEntryChannel::OpenOutputStream(PRUint32 aStartPosition, nsIOutputStream* *aOutputStream)
{
nsresult rv;
nsCOMPtr<nsIOutputStream> baseOutputStream;
rv = mChannel->OpenOutputStream(aStartPosition, getter_AddRefs(baseOutputStream));
if (NS_FAILED(rv)) return rv;
mCacheEntry->NoteUpdate();
mCacheEntry->NoteAccess();
mCacheEntry->mLogicalLength = aStartPosition;
*aOutputStream = new CacheOutputStream(baseOutputStream, mCacheEntry);
if (!*aOutputStream)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*aOutputStream);
return NS_OK;
}
NS_IMETHODIMP
nsCacheEntryChannel::OpenInputStream(PRUint32 aStartPosition, PRInt32 aReadCount,
nsIInputStream* *aInputStream)
{
mCacheEntry->NoteAccess();
return mChannel->OpenInputStream(aStartPosition, aReadCount, aInputStream);
}
class CacheManagerStreamListener: public nsIStreamListener {
public:
CacheManagerStreamListener(nsIStreamListener *aListener,
nsILoadGroup *aLoadGroup, nsIChannel *aChannel):
mListener(aListener), mLoadGroup(aLoadGroup), mChannel(aChannel)
{ NS_INIT_REFCNT(); }
virtual ~CacheManagerStreamListener() {}
private:
NS_DECL_ISUPPORTS
NS_IMETHOD
OnDataAvailable(nsIChannel *channel, nsISupports *aContext,
nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count) {
return mListener->OnDataAvailable(mChannel, aContext, inStr, sourceOffset, count);
}
NS_IMETHOD
OnStartRequest(nsIChannel *channel, nsISupports *aContext) {
if (mLoadGroup)
mLoadGroup->AddChannel(mChannel, aContext);
return mListener->OnStartRequest(mChannel, aContext);
}
NS_IMETHOD
OnStopRequest(nsIChannel *channel, nsISupports *aContext,
nsresult status, const PRUnichar *errorMsg) {
nsresult rv;
rv = mListener->OnStopRequest(mChannel, aContext, status, errorMsg);
if (mLoadGroup)
mLoadGroup->RemoveChannel(mChannel, aContext, status, errorMsg);
return rv;
}
private:
nsCOMPtr<nsIStreamListener> mListener;
nsCOMPtr<nsILoadGroup> mLoadGroup;
nsCOMPtr<nsIChannel> mChannel;
};
NS_IMPL_ISUPPORTS2(CacheManagerStreamListener, nsIStreamListener, nsIStreamObserver)
NS_IMETHODIMP
nsCacheEntryChannel::AsyncRead(PRUint32 aStartPosition, PRInt32 aReadCount,
nsISupports *aContext, nsIStreamListener *aListener)
{
nsresult rv;
mCacheEntry->NoteAccess();
nsCOMPtr<nsIStreamListener> headListener;
if (mLoadGroup) {
mLoadGroup->GetDefaultLoadAttributes(&mLoadAttributes);
// Create a load group "proxy" listener...
nsCOMPtr<nsILoadGroupListenerFactory> factory;
rv = mLoadGroup->GetGroupListenerFactory(getter_AddRefs(factory));
if (NS_SUCCEEDED(rv) && factory) {
rv = factory->CreateLoadGroupListener(aListener,
getter_AddRefs(headListener));
if (NS_FAILED(rv)) return rv;
}
} else {
headListener = aListener;
}
CacheManagerStreamListener* cacheManagerStreamListener;
nsIChannel *channelForListener;
channelForListener = mProxyChannel ? mProxyChannel : this;
cacheManagerStreamListener =
new CacheManagerStreamListener(headListener, mLoadGroup, channelForListener);
if (!cacheManagerStreamListener) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(cacheManagerStreamListener);
rv = mChannel->AsyncRead(aStartPosition, aReadCount, aContext,
cacheManagerStreamListener);
NS_RELEASE(cacheManagerStreamListener);
return rv;
}
// No async writes allowed to the cache yet
NS_IMETHODIMP
nsCacheEntryChannel::AsyncWrite(nsIInputStream *aFromStream, PRUint32 aStartPosition,
PRInt32 aWriteCount, nsISupports *aContext,
nsIStreamObserver *aObserver)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsCacheEntryChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
{
*aLoadGroup = mLoadGroup;
return NS_OK;
}
NS_IMETHODIMP
nsCacheEntryChannel::GetLoadAttributes(nsLoadFlags *aLoadAttributes)
{
*aLoadAttributes = mLoadAttributes;
return NS_OK;
}
NS_IMETHODIMP
nsCacheEntryChannel::SetLoadAttributes(nsLoadFlags aLoadAttributes)
{
mLoadAttributes = aLoadAttributes;
return NS_OK;
}
static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
NS_IMETHODIMP
nsCacheEntryChannel::GetURI(nsIURI * *aURI)
{
char* spec;
nsresult rv;
rv = mCacheEntry->GetUriSpec(&spec);
if (NS_FAILED(rv)) return rv;
NS_WITH_SERVICE(nsIIOService, serv, kIOServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
rv = serv->NewURI(spec, 0, aURI);
nsAllocator::Free(spec);
return rv;
}
NS_IMETHODIMP
nsCacheEntryChannel::GetOriginalURI(nsIURI * *aURI)
{
// FIXME - should return original URI passed into NewChannel() ?
return NS_ERROR_NOT_IMPLEMENTED;
}

View File

@@ -1,82 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#ifndef _nsCacheEntryChannel_h_
#define _nsCacheEntryChannel_h_
#include "nsCOMPtr.h"
#include "nsIChannel.h"
#include "nsCachedNetData.h"
#include "nsILoadGroup.h"
class nsIStreamListener;
// A proxy for an nsIChannel, useful when only a few nsIChannel
// methods must be overridden
class nsChannelProxy : public nsIChannel {
public:
NS_FORWARD_NSICHANNEL(mChannel->)
NS_FORWARD_NSIREQUEST(mChannel->)
protected:
nsChannelProxy(nsIChannel* aChannel):mChannel(aChannel) {};
virtual ~nsChannelProxy() {};
nsCOMPtr<nsIChannel> mChannel;
};
// Override several nsIChannel methods so that they interact with the cache manager
class nsCacheEntryChannel : public nsChannelProxy {
public:
NS_DECL_ISUPPORTS
NS_IMETHOD OpenOutputStream(PRUint32 aStartPosition, nsIOutputStream* *aOutputStream);
NS_IMETHOD OpenInputStream(PRUint32 aStartPosition, PRInt32 aReadCount,
nsIInputStream* *aInputStream);
NS_IMETHOD AsyncRead(PRUint32 aStartPosition, PRInt32 aReadCount,
nsISupports *aContext, nsIStreamListener *aListener);
NS_IMETHOD AsyncWrite(nsIInputStream *aFromStream, PRUint32 aStartPosition,
PRInt32 aWriteCount, nsISupports *aContext,
nsIStreamObserver *aObserver);
NS_IMETHOD GetLoadAttributes(nsLoadFlags *aLoadAttributes);
NS_IMETHOD SetLoadAttributes(nsLoadFlags aLoadAttributes);
NS_IMETHOD GetLoadGroup(nsILoadGroup* *aLoadGroup);
NS_IMETHOD GetURI(nsIURI * *aURI);
NS_IMETHOD GetOriginalURI(nsIURI * *aURI);
protected:
nsCacheEntryChannel(nsCachedNetData* aCacheEntry, nsIChannel* aChannel, nsILoadGroup* aLoadGroup);
virtual ~nsCacheEntryChannel();
friend class nsCachedNetData;
private:
nsCOMPtr<nsCachedNetData> mCacheEntry;
nsCOMPtr<nsILoadGroup> mLoadGroup;
nsCOMPtr<nsIChannel> mProxyChannel;
nsLoadFlags mLoadAttributes;
};
#endif // _nsCacheEntryChannel_h_

View File

@@ -1,496 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#include "nsINetDataCache.h"
#include "nsCacheManager.h"
#include "nsCachedNetData.h"
#include "nsReplacementPolicy.h"
#include "nsString.h"
#include "nsIURI.h"
#include "nsHashtable.h"
#include "nsIComponentManager.h"
#include "nsINetDataDiskCache.h"
// Limit the number of entries in the cache to conserve memory space
// in the nsReplacementPolicy code
#define MAX_MEM_CACHE_ENTRIES 800
#define MAX_DISK_CACHE_ENTRIES 3200
// Cache capacities in MB, overridable via APIs
#define DEFAULT_MEMORY_CACHE_CAPACITY 2000
#define DEFAULT_DISK_CACHE_CAPACITY 10000
#define CACHE_HIGH_WATER_MARK(capacity) ((PRUint32)(0.98 * (capacity)))
#define CACHE_LOW_WATER_MARK(capacity) ((PRUint32)(0.97 * (capacity)))
nsCacheManager* gCacheManager = 0;
NS_IMPL_ISUPPORTS(nsCacheManager, NS_GET_IID(nsINetDataCacheManager))
nsCacheManager::nsCacheManager()
: mActiveCacheRecords(0),
mDiskCacheCapacity(DEFAULT_DISK_CACHE_CAPACITY),
mMemCacheCapacity(DEFAULT_MEMORY_CACHE_CAPACITY)
{
NS_ASSERTION(!gCacheManager, "Multiple cache managers created");
gCacheManager = this;
NS_INIT_REFCNT();
}
nsCacheManager::~nsCacheManager()
{
gCacheManager = 0;
delete mActiveCacheRecords;
delete mMemSpaceManager;
delete mDiskSpaceManager;
}
nsresult
nsCacheManager::Init()
{
nsresult rv;
mActiveCacheRecords = new nsHashtable(64);
if (!mActiveCacheRecords)
return NS_ERROR_OUT_OF_MEMORY;
// Instantiate the memory cache component
rv = nsComponentManager::CreateInstance(NS_NETWORK_MEMORY_CACHE_PROGID,
nsnull,
NS_GET_IID(nsINetDataCache),
getter_AddRefs(mMemCache));
if (NS_FAILED(rv))
return rv;
rv = nsComponentManager::CreateInstance(NS_NETWORK_FLAT_CACHE_PROGID,
nsnull,
NS_GET_IID(nsINetDataCache),
getter_AddRefs(mFlatCache));
if (NS_FAILED(rv)) {
// For now, we don't require a flat cache module to be present
if (rv != NS_ERROR_FACTORY_NOT_REGISTERED)
return rv;
}
#ifdef FILE_CACHE_IS_READY
// Instantiate the file cache component
rv = nsComponentManager::CreateInstance(NS_NETWORK_FILE_CACHE_PROGID,
nsnull,
NS_GET_IID(nsINetDataCache),
getter_AddRefs(mFileCache));
if (NS_FAILED(rv)) {
NS_WARNING("No disk cache present");
}
#endif
// Set up linked list of caches in search order
mCacheSearchChain = mMemCache;
if (mFlatCache) {
mMemCache->SetNextCache(mFlatCache);
mFlatCache->SetNextCache(mFileCache);
} else {
mMemCache->SetNextCache(mFileCache);
}
// TODO - Load any extension caches here
// Initialize replacement policy for memory cache module
mMemSpaceManager = new nsReplacementPolicy;
if (!mMemSpaceManager)
return NS_ERROR_OUT_OF_MEMORY;
rv = mMemSpaceManager->Init(MAX_MEM_CACHE_ENTRIES);
if (NS_FAILED(rv)) return rv;
rv = mMemSpaceManager->AddCache(mMemCache);
// Initialize replacement policy for disk cache modules (file
// cache and flat cache)
mDiskSpaceManager = new nsReplacementPolicy;
if (!mDiskSpaceManager)
return NS_ERROR_OUT_OF_MEMORY;
rv = mDiskSpaceManager->Init(MAX_DISK_CACHE_ENTRIES);
if (NS_FAILED(rv)) return rv;
if (mFileCache) {
rv = mDiskSpaceManager->AddCache(mFileCache);
if (NS_FAILED(rv)) return rv;
}
if (mFlatCache) {
rv = mDiskSpaceManager->AddCache(mFlatCache);
if (NS_FAILED(rv)) return rv;
}
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::GetCachedNetData(const char *aUriSpec, const char *aSecondaryKey,
PRUint32 aSecondaryKeyLength,
PRUint32 aFlags, nsICachedNetData* *aResult)
{
nsCachedNetData *cachedData;
nsresult rv;
nsINetDataCache *cache;
nsReplacementPolicy *spaceManager;
if (aFlags & CACHE_AS_FILE) {
cache = mFileCache;
spaceManager = mDiskSpaceManager;
// Ensure that cache is initialized
if (mDiskCacheCapacity == (PRUint32)-1)
return NS_ERROR_NOT_AVAILABLE;
} else if ((aFlags & BYPASS_PERSISTENT_CACHE) || !mDiskCacheCapacity) {
cache = mMemCache;
spaceManager = mMemSpaceManager;
} else {
cache = mFlatCache ? mFlatCache : mFileCache;
spaceManager = mDiskSpaceManager;
}
// Construct the cache key by appending the secondary key to the URI spec
nsCAutoString cacheKey(aUriSpec);
// Insert NUL at end of URI spec
cacheKey += '\0';
if (aSecondaryKey)
cacheKey.Append(aSecondaryKey, aSecondaryKeyLength);
nsStringKey key(cacheKey);
cachedData = (nsCachedNetData*)mActiveCacheRecords->Get(&key);
// There is no existing instance of nsCachedNetData for this URL.
// Make one from the corresponding record in the cache module.
if (cachedData) {
NS_ASSERTION(cache == cachedData->mCache,
"Cannot yet handle simultaneously active requests for the "
"same URL using different caches");
NS_ADDREF(cachedData);
} else {
rv = spaceManager->GetCachedNetData(cacheKey.GetBuffer(), cacheKey.Length(),
cache, &cachedData);
if (NS_FAILED(rv)) return rv;
mActiveCacheRecords->Put(&key, cachedData);
}
*aResult = cachedData;
return NS_OK;
}
// Remove this cache entry from the list of active ones
nsresult
nsCacheManager::NoteDormant(nsCachedNetData* aEntry)
{
nsresult rv;
PRUint32 keyLength;
char* key;
nsCOMPtr<nsINetDataCacheRecord> record;
nsCachedNetData* deletedEntry;
rv = aEntry->GetRecord(getter_AddRefs(record));
if (NS_FAILED(rv)) return rv;
rv = record->GetKey(&keyLength, &key);
if (NS_FAILED(rv)) return rv;
nsStringKey hashTableKey(nsCString(key, keyLength));
deletedEntry = (nsCachedNetData*)gCacheManager->mActiveCacheRecords->Remove(&hashTableKey);
NS_ASSERTION(deletedEntry == aEntry, "Hash table inconsistency");
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::Contains(const char *aUriSpec, const char *aSecondaryKey,
PRUint32 aSecondaryKeyLength,
PRUint32 aFlags, PRBool *aResult)
{
nsINetDataCache *cache;
nsReplacementPolicy *spaceManager;
nsCachedNetData *cachedData;
if (aFlags & CACHE_AS_FILE) {
cache = mFileCache;
spaceManager = mDiskSpaceManager;
} else if ((aFlags & BYPASS_PERSISTENT_CACHE) ||
(!mFileCache && !mFlatCache) || !mDiskCacheCapacity) {
cache = mMemCache;
spaceManager = mMemSpaceManager;
} else {
cache = mFlatCache ? mFlatCache : mFileCache;
spaceManager = mDiskSpaceManager;
}
// Construct the cache key by appending the secondary key to the URI spec
nsCAutoString cacheKey(aUriSpec);
// Insert NUL between URI spec and secondary key
cacheKey += '\0';
cacheKey.Append(aSecondaryKey, aSecondaryKeyLength);
// Locate the record using (URI + secondary key)
nsStringKey key(cacheKey);
cachedData = (nsCachedNetData*)mActiveCacheRecords->Get(&key);
if (cachedData && (cache == cachedData->mCache)) {
*aResult = PR_TRUE;
return NS_OK;
} else {
// No active cache entry, see if there is a dormant one
return cache->Contains(cacheKey.GetBuffer(), cacheKey.Length(), aResult);
}
}
NS_IMETHODIMP
nsCacheManager::GetNumEntries(PRUint32 *aNumEntries)
{
nsresult rv;
nsCOMPtr<nsISimpleEnumerator> iterator;
nsCOMPtr<nsISupports> cacheSupports;
nsCOMPtr<nsINetDataCache> cache;
PRUint32 totalEntries = 0;
rv = NewCacheModuleIterator(getter_AddRefs(iterator));
if (NS_FAILED(rv)) return rv;
while (1) {
PRBool notDone;
rv = iterator->HasMoreElements(&notDone);
if (NS_FAILED(rv)) return rv;
if (!notDone)
break;
iterator->GetNext(getter_AddRefs(cacheSupports));
cache = do_QueryInterface(cacheSupports);
PRUint32 numEntries;
rv = cache->GetNumEntries(&numEntries);
if (NS_FAILED(rv)) return rv;
totalEntries += numEntries;
}
*aNumEntries = totalEntries;
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::NewCacheEntryIterator(nsISimpleEnumerator* *aResult)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
class CacheEnumerator : public nsISimpleEnumerator
{
public:
CacheEnumerator(nsINetDataCache* aFirstCache):mCache(aFirstCache)
{ NS_INIT_REFCNT(); }
virtual ~CacheEnumerator() {};
NS_DECL_ISUPPORTS
NS_IMETHODIMP
HasMoreElements(PRBool* aMoreElements) {
*aMoreElements = (mCache != 0);
return NS_OK;
}
NS_IMETHODIMP
GetNext(nsISupports* *aSupports) {
*aSupports = mCache;
if (!mCache)
return NS_ERROR_FAILURE;
NS_ADDREF(*aSupports);
nsCOMPtr<nsINetDataCache> nextCache;
nsresult rv = mCache->GetNextCache(getter_AddRefs(nextCache));
mCache = nextCache;
return rv;
}
private:
nsCOMPtr<nsINetDataCache> mCache;
};
NS_IMPL_ISUPPORTS(CacheEnumerator, NS_GET_IID(nsISimpleEnumerator))
NS_IMETHODIMP
nsCacheManager::NewCacheModuleIterator(nsISimpleEnumerator* *aResult)
{
*aResult = new CacheEnumerator(mCacheSearchChain);
if (!*aResult)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*aResult);
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::RemoveAll(void)
{
nsresult rv, result;
nsCOMPtr<nsISimpleEnumerator> iterator;
nsCOMPtr<nsINetDataCache> cache;
nsCOMPtr<nsISupports> iSupports;
result = NS_OK;
rv = NewCacheModuleIterator(getter_AddRefs(iterator));
if (NS_FAILED(rv)) return rv;
while (1) {
PRBool notDone;
rv = iterator->HasMoreElements(&notDone);
if (NS_FAILED(rv)) return rv;
if (!notDone)
break;
iterator->GetNext(getter_AddRefs(iSupports));
cache = do_QueryInterface(iSupports);
PRUint32 cacheFlags;
rv = cache->GetFlags(&cacheFlags);
if (NS_FAILED(rv)) return rv;
if ((cacheFlags & nsINetDataCache::READ_ONLY) == 0) {
rv = cache->RemoveAll();
if (NS_FAILED(rv))
result = rv;
}
}
return result;
}
nsresult
nsCacheManager::LimitMemCacheSize()
{
nsresult rv;
nsReplacementPolicy* spaceManager;
NS_ASSERTION(gCacheManager, "No cache manager");
spaceManager = gCacheManager->mMemSpaceManager;
PRUint32 occupancy;
rv = spaceManager->GetStorageInUse(&occupancy);
if (NS_FAILED(rv)) return rv;
PRUint32 memCacheCapacity = gCacheManager->mMemCacheCapacity;
if (occupancy > CACHE_HIGH_WATER_MARK(memCacheCapacity))
return spaceManager->Evict(CACHE_LOW_WATER_MARK(memCacheCapacity));
return NS_OK;
}
nsresult
nsCacheManager::LimitDiskCacheSize()
{
nsresult rv;
nsReplacementPolicy* spaceManager;
NS_ASSERTION(gCacheManager, "No cache manager");
spaceManager = gCacheManager->mDiskSpaceManager;
PRUint32 occupancy;
rv = spaceManager->GetStorageInUse(&occupancy);
if (NS_FAILED(rv)) return rv;
PRUint32 diskCacheCapacity = gCacheManager->mDiskCacheCapacity;
if (occupancy > CACHE_HIGH_WATER_MARK(diskCacheCapacity))
return spaceManager->Evict(CACHE_LOW_WATER_MARK(diskCacheCapacity));
return NS_OK;
}
nsresult
nsCacheManager::LimitCacheSize()
{
nsresult rv;
rv = LimitDiskCacheSize();
if (NS_FAILED(rv)) return rv;
rv = LimitMemCacheSize();
if (NS_FAILED(rv)) return rv;
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::SetMemCacheCapacity(PRUint32 aCapacity)
{
mMemCacheCapacity = aCapacity;
LimitCacheSize();
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::GetMemCacheCapacity(PRUint32* aCapacity)
{
NS_ENSURE_ARG_POINTER(aCapacity);
*aCapacity = mMemCacheCapacity;
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::SetDiskCacheCapacity(PRUint32 aCapacity)
{
mDiskCacheCapacity = aCapacity;
LimitCacheSize();
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::GetDiskCacheCapacity(PRUint32* aCapacity)
{
NS_ENSURE_ARG_POINTER(aCapacity);
*aCapacity = mDiskCacheCapacity;
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::SetDiskCacheFolder(nsIFileSpec* aFolder)
{
NS_ENSURE_ARG(aFolder);
if (!mFileCache)
return NS_ERROR_NOT_AVAILABLE;
nsCOMPtr<nsINetDataDiskCache> fileCache;
fileCache = do_QueryInterface(mFileCache);
return fileCache->SetDiskCacheFolder(aFolder);
}
NS_IMETHODIMP
nsCacheManager::GetDiskCacheFolder(nsIFileSpec* *aFolder)
{
NS_ENSURE_ARG(aFolder);
if (!mFileCache)
return NS_ERROR_NOT_AVAILABLE;
nsCOMPtr<nsINetDataDiskCache> fileCache;
fileCache = do_QueryInterface(mFileCache);
return fileCache->GetDiskCacheFolder(aFolder);
}

View File

@@ -1,99 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#ifndef _nsCacheManager_h_
#define _nsCacheManager_h_
// 2030f0b0-9567-11d3-90d3-0040056a906e
#define NS_CACHE_MANAGER_CID \
{ \
0x2030f0b0, \
0x9567, \
0x11d3, \
{0x90, 0xd3, 0x00, 0x40, 0x05, 0x6a, 0x90, 0x6e} \
}
#include "nsINetDataCacheManager.h"
#include "nsCOMPtr.h"
class nsHashtable;
class nsReplacementPolicy;
class nsCachedNetData;
class nsCacheManager : public nsINetDataCacheManager {
public:
nsCacheManager();
virtual ~nsCacheManager();
NS_METHOD Init();
// nsISupports methods
NS_DECL_ISUPPORTS
// nsINetDataCacheManager methods
NS_DECL_NSINETDATACACHEMANAGER
private:
// Mapping from cache key to nsCachedNetData, but only for those cache
// entries with external references, i.e. those referred to outside the
// cache manager
nsHashtable* mActiveCacheRecords;
// Memory cache
nsCOMPtr<nsINetDataCache> mMemCache;
// Flat-file database cache; All content aggregated into single disk file
nsCOMPtr<nsINetDataCache> mFlatCache;
// stream-as-file cache
nsCOMPtr<nsINetDataCache> mFileCache;
// Unified replacement policy for flat-cache and file-cache
nsReplacementPolicy* mDiskSpaceManager;
// Replacement policy for memory cache
nsReplacementPolicy* mMemSpaceManager;
// List of caches in search order
nsINetDataCache* mCacheSearchChain;
// Combined file/flat cache capacity, in KB
PRUint32 mDiskCacheCapacity;
// Memory cache capacity, in KB
PRUint32 mMemCacheCapacity;
protected:
static nsresult NoteDormant(nsCachedNetData* aEntry);
static nsresult LimitCacheSize();
static nsresult LimitMemCacheSize();
static nsresult LimitDiskCacheSize();
friend class nsCachedNetData;
friend class CacheOutputStream;
};
#endif // _nsCacheManager_h_

File diff suppressed because it is too large Load Diff

View File

@@ -1,242 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#ifndef _nsCachedNetData_h_
#define _nsCachedNetData_h_
#include "nsICachedNetData.h"
#include "nsCOMPtr.h"
#include "nsINetDataCacheRecord.h"
class nsINetDataCache;
class nsIStreamAsFileObserver;
class nsIStreamAsFile;
class nsIArena;
class StreamAsFileObserverClosure;
class CacheMetaData;
// Number of recent access times recorded
#define MAX_K 3
/**
* FIXME - add comment. There are a lot of these data structures resident in
* memory, so be careful about adding members unnecessarily.
*/
class nsCachedNetData : public nsICachedNetData {
public:
NS_DECL_ISUPPORTS
// nsICachedNetData methods
NS_DECL_NSICACHEDNETDATA
NS_METHOD Init(nsINetDataCacheRecord *aRecord, nsINetDataCache *aCache);
protected:
// Bits for mFlags, below
typedef enum {
DIRTY = 1 << 0, // Cache entry data needs to be flushed to database
// ==== Flags that can be set by the protocol handler ====
ALLOW_PARTIAL = 1 << 1, // Protocol handler supports partial fetching
UPDATE_IN_PROGRESS = 1 << 2, // Protocol handler now modifying cache data
// ==== Cache-entry state flags. At most one of these flags can be set ====
TRUNCATED_CONTENT = 1 << 4, // Entry contains valid content, but it has
// been truncated by cache manager
// A previously-used cache entry, which has been purged of all cached
// content and protocol-private data. This cache entry can be refilled
// with new content or it may be retained in this vestigial state
// because the usage statistics it contains will be used by the
// replacement policy if the same URI is ever cached again.
VESTIGIAL = 1 << 5,
// ==== Memory usage status bits. At most one of these flags can be set ====
RECYCLED = 1 << 8, // Previously associated database record has
// been deleted; This cache entry is available
// for recycling.
DORMANT = 1 << 9, // No references to this cache entry, except by
// the cache manager itself
// ==== Setter bits ====
LAST_MODIFIED_KNOWN = 1 <<12, // Protocol handler called SetLastModifiedTime()
EXPIRATION_KNOWN = 1 <<13, // Protocol handler called SetExpirationTime()
STALE_TIME_KNOWN = 1 <<14, // Protocol handler called SetStaleTime()
// ==== Useful flag combinations ====
// Cache entry not eligible for eviction
UNEVICTABLE = VESTIGIAL | RECYCLED | UPDATE_IN_PROGRESS,
// State flags that are in-memory only, i.e. not persistent
TRANSIENT_FLAGS = DIRTY | RECYCLED | DORMANT
} Flag;
PRBool GetFlag(Flag aFlag) { return (mFlags & aFlag) != 0; }
nsresult GetFlag(PRBool *aResult, Flag aFlag) { *aResult = GetFlag(aFlag); return NS_OK; }
// Set a boolean flag for the cache entry
nsresult SetFlag(PRBool aValue, Flag aFlag);
nsresult SetFlag(Flag aFlag) { return SetFlag(PR_TRUE, aFlag); }
nsresult ClearFlag(Flag aFlag) { return SetFlag(PR_FALSE, aFlag); }
void ComputeProfit(PRUint32 aCurrentTime);
static int Compare(const void *a, const void *b, void *unused);
void NoteAccess();
void NoteUpdate();
// Get underlying raw cache database record.
nsresult GetRecord(nsINetDataCacheRecord* *aRecord);
nsresult GetRecordID(PRInt32 *aRecordID);
nsresult Evict(PRUint32 aTruncatedContentLength);
nsresult GetFileSpec(nsIFileSpec* *aFileSpec);
void NoteDownloadTime(PRTime start, PRTime end);
// placement new for arena-allocation
void *operator new (size_t aSize, nsIArena *aArena);
friend class nsReplacementPolicy;
friend class nsCacheManager;
friend class StreamAsFile;
friend class nsCacheEntryChannel;
friend class CacheOutputStream;
friend class InterceptStreamListener;
private:
nsCachedNetData() {};
virtual ~nsCachedNetData() {};
// Initialize internal fields of this nsCachedNetData instance from the
// underlying raw cache database record.
nsresult Deserialize(bool aDeserializeFlags);
// Notify stream-as-file observers about change in cache entry status
nsresult Notify(PRUint32 aMessage, nsresult aError);
// Add/Remove stream-as-file observers
nsresult AddObserver(nsIStreamAsFile *aStreamAsFile, nsIStreamAsFileObserver* aObserver);
nsresult RemoveObserver(nsIStreamAsFileObserver* aObserver);
// Mark cache entry to indicate a write out to the cache database is required
void SetDirty() { mFlags |= DIRTY; }
nsresult Resurrect(nsINetDataCacheRecord *aRecord);
nsresult CommitFlags();
CacheMetaData* FindTaggedMetaData(const char* aTag, PRBool aCreate);
private:
// List of nsIStreamAsFileObserver's that will receive notification events
// when the cache manager or a client desires to delete/truncate a cache
// entry file.
StreamAsFileObserverClosure* mObservers;
// Protocol-specific meta-data, opaque to the cache manager
CacheMetaData *mMetaData;
// Next in chain for a single bucket in the replacement policy hash table
// that maps from record ID to nsCachedNetData
nsCachedNetData* mNext;
// See flag bits, above
// NOTE: 16 bit member is combined with members below for
// struct packing efficiency. Do not change order of members!
PRUint16 mFlags;
protected:
// Number of nsCacheEntryChannels referring to this record
PRUint8 mChannelCount;
// Below members are statistics kept per cache-entry, used to decide how
// profitable it will be to evict a record from the cache relative to other
// existing records. Note: times are measured in *seconds* since the
// 1/1/70 epoch, same as a unix time_t.
// Number of accesses for this cache record
// NOTE: 8 bit member is combined with members above for
// struct packing efficiency. Do not change order of members!
PRUint8 mNumAccesses;
// A reference to the underlying, raw cache database record, either as a
// pointer to an in-memory object or as a database record identifier
union {
nsINetDataCacheRecord* mRecord;
// Database record ID of associated cache record. See
// nsINetDataCache::GetRecordByID().
PRInt32 mRecordID;
};
// Weak link to parent cache
nsINetDataCache* mCache;
// Length of stored content, which may be less than storage consumed if
// compression is used
PRUint32 mLogicalLength;
// Most recent cache entry access times, used to compute access frequency
PRUint32 mAccessTime[MAX_K];
// We use modification time of the original document for replacement policy
// computations, i.e. to compute a document's age, but if we don't know it,
// we use the time that the document was last written to the cache.
union {
// Document modification time, if known.
PRUint32 mLastModifiedTime;
// Time of last cache update for this doc
PRUint32 mLastUpdateTime;
};
union {
// Time until which document is fresh, i.e. does not have to be validated
// with server and, therefore, data in cache is guaranteed usable
PRUint32 mExpirationTime;
// Heuristic time at which cached document is likely to be out-of-date
// with respect to canonical copy on server. Used for cache replacement
// policy, not for validation.
PRUint32 mStaleTime;
};
// Download time per byte, measure roughly in units of KB/s
float mDownloadRate;
// Heuristic estimate of cache entry future benefits, based on above values
float mProfit;
};
#endif // _nsCachedNetData_h_

View File

@@ -1,658 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#include "nsReplacementPolicy.h"
#include "nsCachedNetData.h"
#include "nsQuickSort.h"
#include "nsIAllocator.h"
#include "nsIEnumerator.h"
#include "prtime.h"
#include "prbit.h"
#include "nsCOMPtr.h"
#include <math.h>
// Constant used to estimate frequency of access to a document based on size
#define CACHE_CONST_B 1.35
// A cache whose space is managed by this replacement policy
class nsReplacementPolicy::CacheInfo {
public:
CacheInfo(nsINetDataCache* aCache):mCache(aCache),mNext(0) {}
nsINetDataCache* mCache;
CacheInfo* mNext;
};
nsReplacementPolicy::nsReplacementPolicy()
: mRankedEntries(0), mCaches(0), mRecordsRemovedSinceLastRanking(0),
mNumEntries(0), mCapacityRankedEntriesArray(0), mLastRankTime(0) {}
nsReplacementPolicy::~nsReplacementPolicy()
{
if (mRankedEntries)
nsAllocator::Free(mRankedEntries);
if (mMapRecordIdToEntry)
nsAllocator::Free(mMapRecordIdToEntry);
}
nsresult
nsReplacementPolicy::Init(PRUint32 aMaxCacheEntries)
{
nsresult rv;
rv = NS_NewHeapArena(getter_AddRefs(mArena), sizeof(nsCachedNetData) * 32);
if (NS_FAILED(rv)) return rv;
mMaxEntries = aMaxCacheEntries;
mHashArrayLength = PR_CeilingLog2(aMaxCacheEntries) >> 3;
size_t numBytes = mHashArrayLength * sizeof(*mMapRecordIdToEntry);
mMapRecordIdToEntry = (nsCachedNetData**)nsAllocator::Alloc(numBytes);
if (!mMapRecordIdToEntry)
return NS_ERROR_OUT_OF_MEMORY;
nsCRT::zero(mMapRecordIdToEntry, numBytes);
return NS_OK;
}
nsresult
nsReplacementPolicy::AddCache(nsINetDataCache *aCache)
{
CacheInfo *cacheInfo = new CacheInfo(aCache);
if (!cacheInfo)
return NS_ERROR_OUT_OF_MEMORY;
cacheInfo->mNext = mCaches;
mCaches = cacheInfo;
return NS_OK;
}
PRUint32
nsReplacementPolicy::HashRecordID(PRInt32 aRecordID)
{
return ((aRecordID >> 16) ^ aRecordID) & (mHashArrayLength - 1);
}
nsCachedNetData*
nsReplacementPolicy::FindCacheEntryByRecordID(PRInt32 aRecordID, nsINetDataCache *aCache)
{
nsresult rv;
nsCachedNetData* cacheEntry;
PRUint32 bucket = HashRecordID(aRecordID);
cacheEntry = mMapRecordIdToEntry[bucket];
for (;cacheEntry; cacheEntry = cacheEntry->mNext) {
PRInt32 recordID;
rv = cacheEntry->GetRecordID(&recordID);
if (NS_FAILED(rv))
continue;
if ((recordID == aRecordID) && (cacheEntry->mCache == aCache))
return cacheEntry;
}
return 0;
}
// Add a cache entry to the hash table that maps record ID to entries
void
nsReplacementPolicy::AddCacheEntry(nsCachedNetData* aCacheEntry, PRInt32 aRecordID)
{
nsCachedNetData** cacheEntryp;
PRUint32 bucket = HashRecordID(aRecordID);
cacheEntryp = &mMapRecordIdToEntry[bucket];
while (*cacheEntryp)
cacheEntryp = &(*cacheEntryp)->mNext;
*cacheEntryp = aCacheEntry;
aCacheEntry->mNext = 0;
}
// Delete a cache entry from the hash table that maps record ID to entries
nsresult
nsReplacementPolicy::DeleteCacheEntry(nsCachedNetData* aCacheEntry)
{
nsresult rv;
PRInt32 recordID;
rv = aCacheEntry->GetRecordID(&recordID);
if (NS_FAILED(rv)) return rv;
PRUint32 bucket = HashRecordID(recordID);
nsCachedNetData** cacheEntryp;
cacheEntryp = &mMapRecordIdToEntry[bucket];
while (*cacheEntryp) {
if (*cacheEntryp == aCacheEntry) {
*cacheEntryp = aCacheEntry->mNext;
return NS_OK;
}
cacheEntryp = &(*cacheEntryp)->mNext;
}
NS_ASSERTION(0, "hash table inconsistency");
return NS_ERROR_FAILURE;
}
nsresult
nsReplacementPolicy::AddAllRecordsInCache(nsINetDataCache *aCache)
{
nsresult rv;
nsCOMPtr<nsISimpleEnumerator> iterator;
nsCOMPtr<nsISupports> iSupports;
nsCOMPtr<nsINetDataCacheRecord> record;
rv = aCache->NewCacheEntryIterator(getter_AddRefs(iterator));
if (!NS_SUCCEEDED(rv)) return rv;
while (1) {
PRBool notDone;
rv = iterator->HasMoreElements(&notDone);
if (NS_FAILED(rv)) return rv;
if (!notDone)
break;
rv = iterator->GetNext(getter_AddRefs(iSupports));
if (!NS_SUCCEEDED(rv)) return rv;
record = do_QueryInterface(iSupports);
rv = AssociateCacheEntryWithRecord(record, aCache, 0);
if (!NS_SUCCEEDED(rv)) return rv;
}
return NS_OK;
}
// Get current time and convert to seconds since the epoch
static PRUint32
now32()
{
double nowFP;
PRInt64 now64 = PR_Now();
LL_L2D(nowFP, now64);
PRUint32 now = (PRUint32)(nowFP * 1e-6);
return now;
}
void
nsCachedNetData::NoteDownloadTime(PRTime start, PRTime end)
{
double startFP, endFP, rate, duration;
LL_L2D(startFP, start);
LL_L2D(endFP, end);
duration = endFP - startFP;
// Sanity-check
if (!duration)
return;
// Compute download rate in kB/s
rate = mLogicalLength / (duration * (1e-6 /1024.0));
// Exponentially smooth download rate
const double alpha = 0.5;
mDownloadRate = (float)(mDownloadRate * alpha + rate * (1.0 - alpha));
}
// 1 hour
#define MIN_HALFLIFE (60 * 60)
// 1 week
#define TYPICAL_HALFLIFE (7 * 24 * 60 * 60)
/**
* Estimate the profit that would be lost if the given cache entry was evicted
* from the cache. Profit is defined as the future expected download delay per
* byte of cached content. The profit computation is made based on projected
* frequency of access, prior download performance and a heuristic staleness
* criteria. The technique used is a variation of that described in the
* following paper:
*
* "A Case for Delay-Conscious Caching of Web Documents"
* http://www.bell-labs.com/user/rvingral/www97.html
*
* Briefly, expected profit is:
*
* (projected frequency of access) * (download time per byte) * (probability freshness)
*/
void
nsCachedNetData::ComputeProfit(PRUint32 aNow)
{
PRUint32 K, now;
if (aNow)
now = aNow;
else
now = now32();
K = PR_MIN(MAX_K, mNumAccesses);
if (!K) {
mProfit = 0;
return;
}
// Compute time, in seconds, since k'th most recent access
double timeSinceKthAccess = now - mAccessTime[K - 1];
if (timeSinceKthAccess <= 0.0) // Sanity check
timeSinceKthAccess = 1.0;
// Estimate frequency of future document access based on past
// access frequency
double frequencyAccess = K / timeSinceKthAccess;
// If we don't have much historical data on access frequency
// use a heuristic based on document size as an estimate
if (mLogicalLength) {
if (K == 1) {
frequencyAccess /= pow(mLogicalLength, CACHE_CONST_B);
} else if (K == 2) {
frequencyAccess /= pow(mLogicalLength, CACHE_CONST_B / 2);
}
}
// Estimate likelihood that data in cache is fresh, i.e.
// that it corresponds to the document on the server
double probabilityFreshness;
PRInt32 halfLife, age, docTime;
bool potentiallyStale;
docTime = GetFlag(LAST_MODIFIED_KNOWN) ? mLastModifiedTime : mLastUpdateTime;
age = now - docTime;
probabilityFreshness = 1.0; // Optimistic
if (GetFlag(EXPIRATION_KNOWN)) {
potentiallyStale = now > mExpirationTime;
halfLife = mExpirationTime - mLastModifiedTime;
} else if (GetFlag(STALE_TIME_KNOWN)) {
potentiallyStale = true;
halfLife = mStaleTime - docTime;
} else {
potentiallyStale = true;
halfLife = TYPICAL_HALFLIFE;
}
if (potentiallyStale) {
if (halfLife < MIN_HALFLIFE)
halfLife = MIN_HALFLIFE;
probabilityFreshness = pow(0.5, (double)age / (double)halfLife);
}
mProfit = (float)(frequencyAccess * probabilityFreshness * mDownloadRate);
}
// Number of entries to grow mRankedEntries array when it's full
#define STATS_GROWTH_INCREMENT 256
// Sorting predicate for NS_Quicksort
int
nsCachedNetData::Compare(const void *a, const void *b, void *unused)
{
nsCachedNetData* entryA = (nsCachedNetData*)a;
nsCachedNetData* entryB = (nsCachedNetData*)b;
// Percolate deleted or empty entries to the end of the mRankedEntries
// array, so that they can be recycled.
if (!entryA || entryA->GetFlag(RECYCLED)) {
if (!entryB || entryB->GetFlag(RECYCLED))
return 0;
else
return +1;
}
if (!entryB || entryB->GetFlag(RECYCLED))
return -1;
// Evicted entries (those with no content data) and active entries (those
// currently being updated) are collected towards the end of the sorted
// array just prior to the deleted cache entries, since evicted entries
// can't be re-evicted.
if (entryA->GetFlag(UPDATE_IN_PROGRESS)) {
if (entryB->GetFlag(UPDATE_IN_PROGRESS))
return 0;
else
return +1;
}
if (entryB->GetFlag(UPDATE_IN_PROGRESS))
return -1;
PRUint16 Ka = PR_MIN(MAX_K, entryA->mNumAccesses);
PRUint16 Kb = PR_MIN(MAX_K, entryB->mNumAccesses);
// Order cache entries by the number of times they've been accessed
if (Ka < Kb)
return -1;
if (Ka > Kb)
return +1;
/*
* Among records that have been accessed an equal number of times, order
* them by profit.
*/
if (entryA->mProfit > entryB->mProfit)
return +1;
if (entryA->mProfit < entryB->mProfit)
return -1;
return 0;
}
/**
* Rank cache entries in terms of their elegibility for eviction.
*/
nsresult
nsReplacementPolicy::RankRecords()
{
PRUint32 i, now;
// Add all cache records if this is the first ranking
if (!mLastRankTime) {
nsresult rv;
CacheInfo *cacheInfo;
cacheInfo = mCaches;
while (cacheInfo) {
rv = AddAllRecordsInCache(cacheInfo->mCache);
if (NS_FAILED(rv)) return rv;
cacheInfo = cacheInfo->mNext;
}
}
// Get current time and convert to seconds since the epoch
now = now32();
// Recompute profit for every known cache record, except deleted ones
for (i = 0; i < mNumEntries; i++) {
nsCachedNetData* entry = mRankedEntries[i];
if (entry && !entry->GetFlag(nsCachedNetData::RECYCLED))
entry->ComputeProfit(now);
}
NS_QuickSort(mRankedEntries, mNumEntries, sizeof *mRankedEntries,
nsCachedNetData::Compare, 0);
mNumEntries -= mRecordsRemovedSinceLastRanking;
mRecordsRemovedSinceLastRanking = 0;
mLastRankTime = now;
return NS_OK;
}
// A heuristic policy to avoid the cost of re-ranking cache records by
// profitability every single time space must be made available in the cache.
void
nsReplacementPolicy::MaybeRerankRecords()
{
// Rank at most once per minute
PRUint32 now = now32();
if ((now - mLastRankTime) >= 60)
RankRecords();
}
void
nsReplacementPolicy::CompactRankedEntriesArray()
{
if (mRecordsRemovedSinceLastRanking || !mLastRankTime)
RankRecords();
}
nsresult
nsReplacementPolicy::CheckForTooManyCacheEntries()
{
if (mCapacityRankedEntriesArray == mMaxEntries) {
return DeleteOneEntry(0);
} else {
nsresult rv;
CacheInfo *cacheInfo;
cacheInfo = mCaches;
while (cacheInfo) {
PRUint32 numEntries, maxEntries;
rv = cacheInfo->mCache->GetNumEntries(&numEntries);
if (NS_FAILED(rv)) return rv;
rv = cacheInfo->mCache->GetMaxEntries(&maxEntries);
if (NS_FAILED(rv)) return rv;
if (numEntries == maxEntries)
return DeleteOneEntry(cacheInfo->mCache);
cacheInfo = cacheInfo->mNext;
}
}
return NS_OK;
}
/**
* Create a new association between a low-level cache database record and a
* cache entry. Add the entry to the set of entries eligible for eviction from
* the cache. This would typically be done when the cache entry is created.
*/
nsresult
nsReplacementPolicy::AssociateCacheEntryWithRecord(nsINetDataCacheRecord *aRecord,
nsINetDataCache* aCache,
nsCachedNetData** aResult)
{
nsCachedNetData* cacheEntry;
nsresult rv;
// First, see if the record is already known to the replacement policy
PRInt32 recordID;
rv = aRecord->GetRecordID(&recordID);
if (NS_FAILED(rv)) return rv;
cacheEntry = FindCacheEntryByRecordID(recordID, aCache);
if (cacheEntry) {
if (aResult) {
if (cacheEntry->GetFlag(nsCachedNetData::DORMANT))
cacheEntry->Resurrect(aRecord);
NS_ADDREF(cacheEntry);
*aResult = cacheEntry;
}
return NS_OK;
}
// Compact the array of cache entry statistics, so that free entries appear
// at the end, for possible reuse.
if (mNumEntries && (mNumEntries == mCapacityRankedEntriesArray))
CompactRankedEntriesArray();
// If compaction doesn't yield available entries in the
// mRankedEntries array, then extend the array.
if (mNumEntries == mCapacityRankedEntriesArray) {
PRUint32 newCapacity;
rv = CheckForTooManyCacheEntries();
if (NS_FAILED(rv)) return rv;
newCapacity = mCapacityRankedEntriesArray + STATS_GROWTH_INCREMENT;
if (newCapacity > mMaxEntries)
newCapacity = mMaxEntries;
nsCachedNetData** newRankedEntriesArray;
PRUint32 numBytes = sizeof(nsCachedNetData*) * newCapacity;
newRankedEntriesArray =
(nsCachedNetData**)nsAllocator::Realloc(mRankedEntries, numBytes);
if (!newRankedEntriesArray)
return NS_ERROR_OUT_OF_MEMORY;
mRankedEntries = newRankedEntriesArray;
mCapacityRankedEntriesArray = newCapacity;
PRUint32 i;
for (i = mNumEntries; i < newCapacity; i++)
mRankedEntries[i] = 0;
}
// Recycle the record after the last in-use record in the array
nsCachedNetData *entry = mRankedEntries[mNumEntries];
NS_ASSERTION(!entry || !entry->GetFlag(nsCachedNetData::RECYCLED),
"Only deleted cache entries should appear at end of array");
if (!entry) {
entry = new(mArena) nsCachedNetData;
if (!entry)
return NS_ERROR_OUT_OF_MEMORY;
mRankedEntries[mNumEntries] = entry;
} else {
// Clear out recycled data structure
nsCRT::zero(entry, sizeof(*entry));
}
entry->Init(aRecord, aCache);
AddCacheEntry(entry, recordID);
// Add one reference to the cache entry from the cache manager
NS_ADDREF(entry);
if (aResult) {
// And one reference from our caller
NS_ADDREF(entry);
*aResult = entry;
}
mNumEntries++;
return NS_OK;
}
nsresult
nsReplacementPolicy::GetCachedNetData(const char* cacheKey, PRUint32 cacheKeyLength,
nsINetDataCache* aCache,
nsCachedNetData** aResult)
{
nsresult rv;
nsCOMPtr<nsINetDataCacheRecord> record;
rv = aCache->GetCachedNetData(cacheKey, cacheKeyLength,
getter_AddRefs(record));
if (NS_FAILED(rv)) return rv;
return AssociateCacheEntryWithRecord(record, aCache, aResult);
}
/**
* Delete the least desirable record from the cache database. This is used
* when the addition of another record would exceed either the cache manager or
* the cache's maximum permitted number of records.
*/
nsresult
nsReplacementPolicy::DeleteOneEntry(nsINetDataCache *aCache)
{
PRUint32 i;
nsresult rv;
nsCachedNetData *entry;
i = 0;
while (1) {
MaybeRerankRecords();
for (; i < mNumEntries; i++) {
entry = mRankedEntries[i];
if (!entry || entry->GetFlag(nsCachedNetData::RECYCLED))
continue;
if (!aCache || (entry->mCache == aCache))
break;
}
// Report error if no record found to delete
if (i == mNumEntries)
return NS_ERROR_FAILURE;
rv = entry->Delete();
if (NS_SUCCEEDED(rv)) {
rv = DeleteCacheEntry(entry);
return rv;
}
}
}
nsresult
nsReplacementPolicy::GetStorageInUse(PRUint32* aStorageInUse)
{
nsresult rv;
CacheInfo *cacheInfo;
*aStorageInUse = 0;
cacheInfo = mCaches;
while (cacheInfo) {
PRUint32 cacheStorage;
rv = cacheInfo->mCache->GetStorageInUse(&cacheStorage);
if (NS_FAILED(rv)) return rv;
*aStorageInUse += cacheStorage;
cacheInfo = cacheInfo->mNext;
}
return NS_OK;
}
/**
* Delete the least desirable records from the cache until the occupancy of the
* cache has been reduced by the given number of KB. This is used when the
* addition of more cache data would exceed the cache's capacity.
*/
nsresult
nsReplacementPolicy::Evict(PRUint32 aTargetOccupancy)
{
PRUint32 i;
nsCachedNetData *entry;
nsresult rv;
PRUint32 occupancy;
PRInt32 truncatedLength;
nsCOMPtr<nsINetDataCacheRecord> record;
MaybeRerankRecords();
for (i = 0; i < mNumEntries; i++) {
rv = GetStorageInUse(&occupancy);
if (!NS_SUCCEEDED(rv)) return rv;
if (occupancy <= aTargetOccupancy)
return NS_OK;
entry = mRankedEntries[i];
// Skip deleted/empty cache entries and ones that have already been evicted
if (!entry || entry->GetFlag(nsCachedNetData::UNEVICTABLE))
continue;
if (entry->GetFlag(nsCachedNetData::ALLOW_PARTIAL)) {
rv = entry->GetRecord(getter_AddRefs(record));
if (NS_FAILED(rv))
continue;
PRUint32 contentLength;
rv = record->GetStoredContentLength(&contentLength);
if (NS_FAILED(rv))
continue;
// Additional cache storage required, in KB
PRUint32 storageToReclaim = (occupancy - aTargetOccupancy) << 10;
truncatedLength = (PRInt32)(contentLength - storageToReclaim);
if (truncatedLength < 0)
truncatedLength = 0;
} else {
truncatedLength = 0;
}
rv = entry->Evict(truncatedLength);
}
return NS_ERROR_FAILURE;
}

View File

@@ -1,136 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
/**
* This class manages one or more caches that share a storage resource, e.g. a
* file cache and a flat-database cache might each occupy space on the disk and
* they would share a single instance of nsReplacementPolicy. The replacement
* policy heuristically chooses which cache entries to evict when storage is
* required to accommodate incoming cache data.
*/
#ifndef _nsReplacementPolicy_h_
#define _nsReplacementPolicy_h_
#include "nscore.h"
#include "nsISupportsUtils.h"
#include "nsINetDataCache.h"
#include "nsICachedNetData.h"
#include "nsIArena.h"
#include "nsCOMPtr.h"
#include "nsHashtable.h"
class nsCachedNetData;
struct PL_HashTable;
/**
* This private class is responsible for implementing the network data cache's
* replacement policy, i.e. it decides which cache data should be evicted to
* make room for new incoming data.
*/
class nsReplacementPolicy {
public:
nsReplacementPolicy();
~nsReplacementPolicy();
protected:
nsresult Init(PRUint32 aMaxCacheEntries);
nsresult AddCache(nsINetDataCache *aCache);
nsresult GetCachedNetData(const char* cacheKey, PRUint32 cacheKeyLength,
nsINetDataCache* aCache,
nsCachedNetData** aResult);
nsresult GetStorageInUse(PRUint32* aNumKBytes);
friend class nsCacheManager;
private:
nsresult RankRecords();
void MaybeRerankRecords();
void CompactRankedEntriesArray();
nsresult DeleteOneEntry(nsINetDataCache* aCache);
nsresult Evict(PRUint32 aTargetOccupancy);
nsCachedNetData* FindCacheEntryByRecordID(PRInt32 aRecordID, nsINetDataCache *aCache);
void AddCacheEntry(nsCachedNetData* aCacheEntry, PRInt32 aRecordID);
nsresult DeleteCacheEntry(nsCachedNetData* aCacheEntry);
PRUint32 HashRecordID(PRInt32 aRecordID);
nsresult AssociateCacheEntryWithRecord(nsINetDataCacheRecord *aRecord,
nsINetDataCache* aCache,
nsCachedNetData** aResult);
nsresult AddAllRecordsInCache(nsINetDataCache *aCache);
nsresult CheckForTooManyCacheEntries();
class CacheInfo;
private:
// Growable array of pointers to individual cache entries; It is sorted by
// profitability, such that low-numbered array indices refer to cache
// entries that are the least profitable to retain. New cache entries are
// added to the end of the array. Deleted cache entries are specially
// marked and percolate to the end of the array for recycling whenever
// mRankedEntries is sorted. Evicted cache entries (those with no
// associated content data) are retained for the purpose of improving the
// replacement policy efficacy, and are percolated towards the end of the
// array, just prior to the deleted cache entries.
//
// The array is not in sorted order 100% of the time; For efficiency
// reasons, sorting is only done when heuristically deemed necessary.
nsCachedNetData** mRankedEntries;
// Hash table buckets to map Record ID to cache entry. We use this instead
// of a PL_HashTable to reduce storage requirements
nsCachedNetData** mMapRecordIdToEntry;
// Length of mMapRecordIdToEntry array
PRUint32 mHashArrayLength;
// Linked list of caches that share this replacement policy
CacheInfo* mCaches;
// Allocation area for cache entry (nsCachedNetData) instances
nsCOMPtr<nsIArena> mArena;
// Bookkeeping
PRUint32 mRecordsRemovedSinceLastRanking;
// Maximum permitted length of mRankedEntries array
PRUint32 mMaxEntries;
// Number of occupied slots in mRankedEntries array
PRUint32 mNumEntries;
// Capacity of mRankedEntries array
PRUint32 mCapacityRankedEntriesArray;
// Time at which cache entries were last ranked by profitability
PRUint32 mLastRankTime;
};
#endif // _nsReplacementPolicy_h_

View File

@@ -1,39 +0,0 @@
#
# The contents of this file are subject to the Netscape Public
# License Version 1.1 (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.org code.
#
# 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.
#
# Contributor(s):
#
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
EXPORTS = \
nsICacheManager.h \
nsICacheObject.h \
nsICachePref.h \
nsICacheModule.h \
$(NULL)
EXPORTS := $(addprefix $(srcdir)/, $(EXPORTS))
include $(topsrcdir)/config/rules.mk

View File

@@ -1,41 +0,0 @@
#!gmake
#
# The contents of this file are subject to the Netscape Public
# License Version 1.1 (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.org code.
#
# 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.
#
# Contributor(s):
MODULE = nkcache
DEPTH = ..\..\..
include <$(DEPTH)/config/config.mak>
EXPORTS = \
$(NULL)
XPIDLSRCS = \
.\nsICachedNetData.idl \
.\nsINetDataCacheManager.idl \
.\nsINetDataCache.idl \
.\nsINetDataCacheRecord.idl \
.\nsINetDataDiskCache.idl \
.\nsIStreamAsFile.idl \
$(NULL)
include <$(DEPTH)/config/rules.mak>

View File

@@ -1,229 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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.org code.
*
* 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.
*
* Contributor(s):
*/
#include "nsrootidl.idl"
#include "nsISupports.idl"
interface nsIFileSpec;
interface nsIURI;
interface nsIObserver;
interface nsIChannel;
interface nsINetDataCache;
interface nsINetDataCacheRecord;
interface nsILoadGroup;
interface nsIStreamListener;
/**
* The nsICachedNetData interface represents a single entry in a database that
* caches data retrieved from the network. This interface is implemented by the
* cache manager on top of the low-level nsINetDataCacheRecord and
* nsINetDataCache interfaces that are implemented by the database.
*
* Each cache record may contain both content and metadata. The content may
* be, for example, GIF image data or HTML, and it is accessed through
* nsIChannel's streaming API. The opaque metadata, which may contain HTTP
* headers among other things, is stored as a byte array. Each entry in the
* cache is indexed by two different keys: a record id number and a key created
* by combining the URI with a "secondary key", e.g. HTTP post data.
*
* @See nsINetDataCacheRecord
* @See nsINetDataCache
* @See nsINetDataDiskCache
* @See nsINetDataCacheManager
*/
[scriptable, uuid(6aeb2a40-6d43-11d3-90c8-000064657374)]
interface nsICachedNetData : nsISupports
{
/**
* String form of the URI provided as an argument to the call to
* nsINetDataCacheManager::GetCachedNetData() that created this record.
*/
readonly attribute string uriSpec;
/**
* Getter for the opaque secondary database key provided as an argument to
* the call to nsINetDataCacheManager::GetCachedNetData() that created this
* record.
*/
void getSecondaryKey(out unsigned long length,
[retval, size_is(length)] out string secondaryKey);
/**
* This flag may be set by a protocol handler to indicate that it supports
* partial fetching of data. In that case, the cache manager is permitted
* to truncate the entry's content to accommodate incoming data for other
* cache entries rather than deleting it wholesale.
*/
attribute boolean allowPartial;
/**
* This flag indicates that the write stream supplying content data for the
* cache did not complete normally and, therefore, the content may be
* truncated.
*/
readonly attribute boolean partialFlag;
/**
* This flag can be set and cleared by a protocol handler as a form of
* self-notification, so as to avoid race conditions in which a protocol
* handler issues two identical network requests to fill the same cache
* entry. The cache manager itself largely ignores this flag.
*/
attribute boolean updateInProgress;
/**
* inUse is set if any existing channels are associated with this cache
* entry or if the updateInProgess flag is set. This can be used to
* prevent writing to a cache entry by a protocol handler if it's being
* read or written elsewhere.
*/
readonly attribute boolean inUse;
/**
* Date/time that the document was last stored on the origin server, as
* supplied by the protocol handler. This value is used as input to the
* cache replacement policy, i.e. it is not used for validation. If the
* protocol can't supply a last-modified time, this attribute should remain
* unset. When unset, the value of this attribute is zero.
*
* FIXME: Should use nsIDateTime interface, once it's created
* instead of PRTime, for improved scriptability ?
*/
attribute PRTime lastModifiedTime;
/**
* Supplied by the protocol handler, the expirationTime attribute specifies
* the time until which the document is guaranteed fresh, i.e. the document
* does not have to be validated with the server and, therefore, any data
* in cache is definitely usable. The value of this attribute serves as a
* hint to the cache replacement policy. Only one of either staleTime or
* expirationTime may be set for a single cache record. When unset, the
* value of this attribute is zero.
*/
attribute PRTime expirationTime;
/**
* Date/time supplied by the protocol handler, at which point the content
* is *likely* to be stale, i.e. the data in the cache may be out-of-date
* with respect to the data on the server. This heuristic date does not
* necessarily correspond to the HTTP Expires header, as it does not
* determine when cached network data must be validated with the origin
* server, but only serves as a hint to the cache replacement policy. Only
* one of either staleTime or expirationTime may be set for a single cache
* record. When unset, the value of this attribute is zero.
*/
attribute PRTime staleTime;
/**
* Date/time of last access of the data in this cache record, as determined
* by the cache manager.
*/
readonly attribute PRTime lastAccessTime;
/**
* Number of times this record has been accessed since it was first stored.
*/
readonly attribute PRUint16 numberAccesses;
/**
* Accessor methods for opaque meta-data which can be read and updated
* independently of the content data.
*
* The aTag argument can be used to accommodate multiple clients of the
* cache API, each of which wants to store its own private meta-data into
* the cache. For example, there could be a "headers" tag that the HTTP
* protocol handler uses to store http response headers and a "image size"
* tag used to store the image dimensions of a GIF file. The aData
* argument refers to an opaque blob of arbitrary bytes.
*
* IMPORTANT: If aData does not contain byte-oriented data, i.e. it's not a
* string, the contents of aData must be byte-swapped by the,
* caller, so as to make the cache files endian-independent.
*/
void getAnnotation(in string aTag,
out PRUint32 aLength, [size_is(aLength), retval] out string aData);
void setAnnotation(in string aTag,
in PRUint32 aLength, [size_is(aLength)] in string aData);
/**
* As a getter, return the number of content bytes stored in the cache,
* i.e. via the nsIChannel streaming APIs. This may be less than the
* complete content length if a partial cache fill occurred. The cached
* content can be truncated by setting the value of this attribute. The
* value of the attribute represents a logical, not a physical, length. If
* compression has been used, the content may consume less storage than
* indicated by this attribute.
*
* When this attribute is set to zero the associated cache disk file, if
* any, should be deleted.
*/
attribute PRUint32 storedContentLength;
/**
* Notify any observers associated with this cache entry of the deletion
* request. If all observers drop their reference to the cache entry,
* proceed to delete the underlying cache database record and associated
* content storage.
*/
void delete();
/**
* Flush any changes in this entry's data to the cache database. This
* method will automatically be called when the last reference to the cache
* is dropped, but it can also be called explicitly for a synchronous
* effect.
*/
void commit();
/**
* Parent container cache for this entry.
*/
readonly attribute nsINetDataCache cache;
/**
* Create a channel for reading or writing a stream of content into the
* entry. It is expected that many of the nsIChannel methods return
* NS_NOT_IMPLEMENTED, including:
*
* + GetURI()
* + GetContentType()
* + GetContentLength()
*
* Though nsIChannel provides for both async and synchronous I/O APIs, both
* may not be implemented. Only AsyncRead() and OpenOutputStream() is
* required. The aProxyChannel argument allows another channel to be
* specified as the proffered argument to nsIStreamListener methods rather
* than the cache's own channel.
*/
nsIChannel newChannel(in nsILoadGroup aLoadGroup,
in nsIChannel aProxyChannel);
/**
* This method can be used by a caching protocol handler to store data in
* the cache by forking an asynchronous read stream so that it is
* simultaneously sent to a requester and written into the cache. This
* method implicitly sets the updateInProgress flag, if it has not already
* been set.
*/
nsIStreamListener interceptAsyncRead(in nsIStreamListener aOriginalListener,
in PRUint32 aStartOffset);
};

View File

@@ -1,143 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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.org code.
*
* 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.
*
* Contributor(s):
*/
#include "nsISupports.idl"
interface nsIURI;
interface nsINetDataCacheRecord;
interface nsISimpleEnumerator;
interface nsIFileSpec;
/**
* The nsINetDataCache defines the low-level API for a network-data
* cache, used to cache the responses to network retrieval commands.
* This interface, along with nsINetDataCacheRecord, is implemented by
* the memory cache, the file cache and, optionally, by some extension
* caches. This interface is essentially a pseudo-private API for the
* cache manager. Other clients should never use this interface.
*
* Each cache entry may contain both content, e.g. GIF image data, and
* associated metadata, e.g. HTTP headers. Each entry is indexed by two
* different keys: a record id number and a key created by combining the URI
* with a "secondary key", e.g. HTTP post data.
*
* The nsINetDataCache interface is agnostic as to where the data is
* stored and whether the storage is volatile or persistent. The
* memory cache, any disk caches and any extension caches must all
* implement this interface.
*
*/
[scriptable, uuid(ccfc58c0-6dde-11d3-90c8-000064657374)]
interface nsINetDataCache : nsISupports
{
/**
* Human-readable description of the cache module, e.g. "Disk Cache"
*/
readonly attribute wstring description;
/**
* Returns true if cached data is available for the given opaque key,
* even if only partial data is stored.
*/
boolean contains([size_is(length)] in string key, in PRUint32 length);
/**
* Fetch the cache entry record for the given opaque key. If one does not
* exist, create a new, empty record.
*/
nsINetDataCacheRecord getCachedNetData([size_is(length)] in string key,
in PRUint32 length);
/**
* Fetch the cache entry record for the given URI using the record ID as a key.
*/
nsINetDataCacheRecord getCachedNetDataByID(in PRInt32 RecordID);
/**
* False indicates that this cache is entirely bypassed.
*/
attribute boolean enabled;
/**
* Constants for flags attribute, below
*/
// Used for extension caches, e.g. a CD-ROM cache
const long READ_ONLY = 1 << 0;
// One of these bits must be set
const long MEMORY_CACHE = 1 << 1;
const long FLAT_FILE_CACHE = 1 << 2;
const long FILE_PER_URL_CACHE = 1 << 3;
/**
* See constants defined above.
*/
readonly attribute PRUint32 flags;
/**
* Total number of URI entries stored in the cache.
*/
readonly attribute PRUint32 numEntries;
/**
* Maximum number of URI entries that may be stored in the cache.
*/
readonly attribute PRUint32 maxEntries;
/**
* Enumerate the URI entries stored in the cache.
*/
nsISimpleEnumerator newCacheEntryIterator();
/**
* Contains a reference to the next cache in search order. For the memory
* cache, this attribute always references the disk cache. For the disk
* cache, it contains a reference to the first extension cache.
*/
attribute nsINetDataCache nextCache;
/**
* An estimate of the amount of storage occupied by the cache, in kB.
* Actual use may be slightly higher than reported due to cache overhead
* and heap fragmentation (in the memory cache) or block quantization (in
* the disk cache).
*/
readonly attribute PRUint32 storageInUse;
/**
* Remove all entries from a writable cache. This could be used, for
* example, after a guest ends a browser session. This is equivalent to
* setting the cache's Capacity to zero, except that all cache entries,
* even those in active use, will be deleted. Also, any global cache
* database files will be deleted.
*/
void removeAll();
};
%{ C++
// ProgID prefix for Components that implement this interface
#define NS_NETWORK_CACHE_PROGID "component://netscape/network/cache"
#define NS_NETWORK_MEMORY_CACHE_PROGID NS_NETWORK_CACHE_PROGID "?name=memory-cache"
#define NS_NETWORK_FLAT_CACHE_PROGID NS_NETWORK_CACHE_PROGID "?name=flat-cache"
#define NS_NETWORK_FILE_CACHE_PROGID NS_NETWORK_CACHE_PROGID "?name=file-cache"
%}

View File

@@ -1,163 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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.org code.
*
* 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.
*
* Contributor(s):
*/
#include "nsISupports.idl"
interface nsISimpleEnumerator;
interface nsICachedNetData;
interface nsINetDataCache;
interface nsINetDataDiskCache;
interface nsIURI;
interface nsIFileSpec;
/**
* The network-response cache manager is partly responsible for the caching of
* content and associated metadata that has been retrieved via the network.
* (The remaining responsibility for caching lies with individual network
* protocol handlers.)
*
* The cache manager supervises the actions of individual cache components,
* such as the memory cache, the disk cache and any extension caches, e.g. a
* read-only CD-ROM cache.
*
* @See nsINetDataCache
* @See nsICachedNetData
*/
[scriptable, uuid(71c8ab00-6d5c-11d3-90c8-000064657374)]
interface nsINetDataCacheManager : nsISupports
{
/**
* Flag for the GetCachedNetData() method: If set, the memory cache is
* neither searched nor will any data be stored into it. This might be
* appropriate, for example, with images, because they have their own
* cache for storing *decoded* images.
*/
const unsigned long BYPASS_MEMORY_CACHE = 1 << 0;
/**
* Flag for the GetCachedNetData() method: If set, the disk cache
* is neither searched nor will any be data stored into it.
* However, read-only extension caches may be searched. This
* might be used to avoid leaving persistent records of secure
* data.
*/
const unsigned long BYPASS_PERSISTENT_CACHE = 1 << 1;
/**
* Flag for the GetCachedNetData() method: If set, any stream
* content is stored in the cache as a single disk file. Content
* will not be cached in the memory cache nor is it cached in a
* flat-file cache database. This is used to implement the jar
* protocol handler and to provide the stream-as-file semantics
* required by the classic bowser plugin API.
*/
const unsigned long CACHE_AS_FILE = 1 << 2;
/**
* Fetch the cache entry record for the given URI. If one does not exist,
* create a new, empty record. The normal search order for caches is:
* + Memory cache
* + Disk cache
* + File cache (stream-as-file cache)
* + All extension caches
*
* When writing, data is typically stored in both the memory cache and the
* disk cache. Both the search order and this write policy can be modified by
* setting one or more of the flag argument bits, as defined above.
*
* The optionally-NULL secondaryKey argument can be used, e.g. for form
* post data or for HTTP headers in the case of HTTP.
*/
nsICachedNetData getCachedNetData(in string uri,
[size_is(secondaryKeyLength)] in string secondaryKey,
in PRUint32 secondaryKeyLength,
in PRUint32 flags);
/**
* Returns true if cached content is available for the given URI, even if
* only partial data is stored. The flags argument behaves the same as for
* the GetCachedNetData() method, above.
*/
boolean contains(in string uri,
[size_is(secondaryKeyLength)] in string secondaryKey,
in PRUint32 secondaryKeyLength,
in PRUint32 flags);
/**
* Total number of unexpired URI entries stored in all caches. This number
* does not take into account duplicate URIs, e.g. because the memory cache
* and the disk cache might each contain an entry for the same URI.
*/
readonly attribute PRUint32 numEntries;
/**
* Enumerate the unexpired URI entries stored in all caches. Some URIs may
* be enumerated more than once, e.g. because the the memory cache and the
* disk cache might each contain an entry for the same URI.
*/
nsISimpleEnumerator newCacheEntryIterator();
/*
* Enumerate all the loaded nsINetDataCache-implementing cache modules.
* The first module enumerated will be the memory cache, the second will be
* the disk cache, then the file cache, followed by all the extension
* caches, in search order.
*/
nsISimpleEnumerator newCacheModuleIterator();
/**
* Remove all entries from all writable caches. This could be used, for
* example, after a guest ends a browser session. This is equivalent to
* setting the DiskCacheCapacity to zero, except that all cache entries,
* even those in active use, will be deleted. Also, any global cache
* database files will be deleted.
*/
void RemoveAll();
/**
* The disk cache is made up of the file cache (for stream-as-file
* requests) and a (possibly independent) persistent cache that handles all
* other cache requests. This attribute sets/gets the combined capacity of
* these caches, measured in KBytes. Setting the capacity lower than the
* current amount of space currently in use may cause cache entries to be
* evicted from the cache to accomodate the requested capacity.
*/
attribute PRUint32 diskCacheCapacity;
/**
* This attribute sets/gets the capacity of the memory cache, measured in
* KBytes. Setting the capacity lower than the current amount of space
* currently in use may cause cache entries to be evicted from the cache to
* accomodate the requested capacity.
*/
attribute PRUint32 memCacheCapacity;
/**
* This attribute must be set before attempting to store into the disk cache.
*/
attribute nsIFileSpec diskCacheFolder;
};
%{ C++
// ProgID prefix for Components that implement this interface
#define NS_NETWORK_CACHE_MANAGER_PROGID NS_NETWORK_CACHE_PROGID "?name=manager"
%}

View File

@@ -1,125 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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.org code.
*
* 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.
*
* Contributor(s):
*/
#include "nsISupports.idl"
#include "nsrootidl.idl"
interface nsIFileSpec;
interface nsIChannel;
interface nsINetDataCache;
/**
* The nsINetDataCacheRecord represents a single entry in a database that
* caches data retrieved from the network. On top of this low-level interface
* to the raw record data, the cache manager implements a higher-level record
* interface, nsICachedNetData. Each instance of nsINetDataCacheRecord is
* (internally) associated with a parent database, an instance of the
* nsINetDataCache interface. This interface is essentially a pseudo-private
* API for the cache manager. Other clients should never use this interface.
*
* Each cache record may contain both content and metadata. The content may
* be, for example, GIF image data or HTML, and it is accessed through
* nsIChannel's streaming API. The opaque metadata, which may contain HTTP
* headers among other things, is accessed as a contiguous byte array. Each
* entry in the cache is indexed by two different keys: a unique record id
* number, generated by the cache, and an opaque string. The latter contains
* the URI and other secondary key information, e.g. HTTP form post key/value
* pairs.
*
* The nsINetDataCacheRecord interface is agnostic as to where the data is
* stored and whether the storage is volatile or persistent. The memory cache,
* the disk cache, a flat-file cache and any read-only extension caches must
* all implement this interface.
*
* @See nsICachedNetData
* @See nsINetDataCache
* @See nsINetDataDiskCache
* @See nsINetDataCacheManager
*/
interface nsILoadGroup;
[scriptable, uuid(fdcdd6a0-7461-11d3-90ca-0040056a906e)]
interface nsINetDataCacheRecord : nsISupports
{
/**
* As far as the nsINetDataCacheRecord implementation is concerned, the
* cache entry database key is an opaque blob, but it's intended to contain
* both the URI and any secondary keys, such as HTTP post data.
*/
void getKey(out unsigned long length, [size_is(length), retval] out string key);
/**
* A persistent record number assigned by the cache which must be unique
* among all entries stored within the same cache. The record ID serves as
* an alternate key to the cache record. Providing that they satisfy the
* afforementioned uniqueness requirement, record IDs can be assigned any
* value by the database except that they may never be zero.
*/
readonly attribute PRInt32 recordID;
/**
* Opaque data which can be updated for each cache entry independently of
* the content data. This data is a combination of protocol-independent
* data provided by the cache manager and protocol-specific meta-data,
* e.g. HTTP headers.
*/
void getMetaData(out PRUint32 length, [size_is(length), retval] out string metaData);
void setMetaData(in PRUint32 length, [size_is(length)] in string data);
/**
* Number of content bytes stored in the cache, i.e. via the nsIChannel
* streaming APIs. This may be less than the complete content length if a
* partial cache fill occurred. Additionally, the cached content can be
* truncated by reducing the value of this attribute. When this attribute
* is set to zero the associated cache disk file, if any, should be
* deleted.
*/
attribute PRUint32 storedContentLength;
/**
* Delete this cache entry and its associated content.
*/
void delete();
/**
* Create a channel for reading or writing a stream of content into the
* entry. However, many of the nsIChannel methods may return
* NS_NOT_IMPLEMENTED, including:
*
* + GetURI()
* + GetContentType()
* + GetContentLength()
*/
nsIChannel newChannel(in nsILoadGroup loadGroup);
/**
* If a cache is implemented such that it stores each URI's content in an
* individual disk file, this method will identify the file corresponding
* to this record. This may be used to implement the "stream-as-file"
* semantics required by some plugins and by the 'jar:' protocol handler.
* However, not all cache implementations are *required* to store the data
* from each URI in an individual file, so it is acceptable for an
* implementation of this method to signal NS_NOT_IMPLEMENTED.
*/
readonly attribute nsIFileSpec filename;
};

View File

@@ -1,42 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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.org code.
*
* 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.
*
* Contributor(s):
*/
#include "nsINetDataCache.idl"
interface nsIFileSpec;
/**
/**
* A network-data disk cache is used to persistently cache the responses to
* network retrieval commands. Each cache entry may contain both content,
* e.g. GIF image data, and associated metadata, e.g. HTTP headers.
*/
[scriptable, uuid(6408e390-6f13-11d3-90c8-000064657374)]
interface nsINetDataDiskCache : nsINetDataCache
{
/**
* This attribute must be set before calling any other methods of this
* interface.
*/
attribute nsIFileSpec diskCacheFolder;
};

View File

@@ -1,106 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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) 1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#include "nsrootidl.idl"
#include "nsISupports.idl"
interface nsIFileSpec;
interface nsIStreamAsFileObserver;
/**
* In addition to enhancing effective network response time via caching, the
* cache manager serves a second purpose by providing the stream-as-file
* service required by traditional browser plugins and the jar: protocol
* handler. The interface below provides a means for a client to determine the
* filename associated with a stream and to detect modification/deletion of
* that file.
*/
[scriptable, uuid(0eedbbf0-92d9-11d3-90d3-0040056a906e)]
interface nsIStreamAsFile : nsISupports
{
/**
* Filename containing stream-as-file
*/
readonly attribute nsIFileSpec fileSpec;
/**
* Add an observer for this cache record. When the cache wants to delete
* or truncate a record, so as to make space for another cache entry's
* content data, it will call <code>aObserver</code>'s Observe() method,
* passing the nsIStreamAsFile instance as the <code>aSubject</code>
* argument and an appropriate message. If the observer does not wish to
* inhibit deletion/truncation, it should Release() any references it has to the
* cache record.
*
* @See nsIStreamAsFileObserver
*/
void addObserver(in nsIStreamAsFileObserver aObserver);
/**
* Delete an observer that was added by the AddObserver() method.
*/
void removeObserver(in nsIStreamAsFileObserver aObserver);
};
/**
* This interface can be implemented by a client to receive notifications of
* either modification or deletion of a file created by the cache manager using
* the stream-as-file semantics.
*/
[scriptable, uuid(a26e27c0-92da-11d3-90d3-0040056a906e)]
interface nsIStreamAsFileObserver : nsISupports
{
/**
* Flag bits for argument to Observe() method.
*/
const long NOTIFY_AVAILABLE = 1 << 0; // Stream-as-file now available for reading
const long NOTIFY_ERROR = 1 << 1; // Error while loading stream / creating file
const long REQUEST_DELETION = 1 << 2; // Cache manager wishes to delete/truncate file
const long INVALIDATE = 1 << 3; // File is out-of-date
// Convenience value
const long MAKE_UNAVAILABLE = REQUEST_DELETION | INVALIDATE;
/**
* Receive either a notification or a request concerning a file that has
* been opened using stream-as-file. The aMessage and aError arguments
* have varying values depending on the nature of the notification.
* aMessage is set to NOTIFY_AVAILABLE when a complete stream has been read
* and stored on disk in a file. At that point, and no sooner, may the
* filename attribute of the associated nsIStreamAsFile be accessed via the
* associated nsIStreamAsFile interface. If the aMessage argument is
* NOTIFY_ERROR, the aError argument contains the relevant error code. If
* the aMessage argument is either REQUEST_DELETION or REQUEST_TRUNCATION,
* the callee should immediately Release() all references to the
* nsIStreamAsFile (and any references to its associated nsICachedNetData
* instances), unless it wishes to inhibit the requested file modification.
* If the aMessage argument is INVALIDATE, the cache manager is replacing
* the file with a more recent version. If a client wants to continue
* using the (now out-of-date) file, it must delete it when it has finished,
* as the cache manager will effectively relinquished ownership of the
* file.
*/
void ObserveStreamAsFile(in nsIStreamAsFile aStreamAsFile,
in PRUint32 aMessage,
in nsresult aError);
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,216 @@
# Backwards.pm
# Copyright (C) 1999 Uri Guttman. All rights reserved.
# mail bugs, comments and feedback to uri@sysarch.com
use strict ;
package Backwards ;
use Symbol ;
use Fcntl ;
use Carp ;
use integer ;
#my $max_read_size = 3 ;
my $max_read_size = 1 << 13 ;
# support tied handles. the tied calls map directly to the object methods
*TIEHANDLE = \&new ;
*READLINE = \&readline ;
# create a new Backwards object
sub new {
my( $class, $filename ) = @_ ;
my( $handle, $seek_pos, $read_size, $self ) ;
# get a new handle symbol and the real file handle
$handle = gensym() ;
# open the file for reading
unless( sysopen( $handle, $filename, O_RDONLY ) ) {
carp "Can't open $filename $!" ;
return ;
}
# seek to the end of the file
seek( $handle, 0, 2 ) ;
$seek_pos = tell( $handle ) ;
# get the size of the first block to read,
# either a trailing partial one (the % size) or full sized one (max read size)
$read_size = $seek_pos % $max_read_size || $max_read_size ;
# create the hash for the object, bless and return it
$self = {
'file_name' => $filename,
'handle' => $handle,
'read_size' => $read_size,
'seek_pos' => $seek_pos,
'lines' => [],
} ;
return( bless( $self, $class ) ) ;
}
sub readline {
my( $self, $line_ref ) = @_ ;
my( $handle, $lines_ref, $seek_pos, $read_cnt, $read_buf,
$file_size, $read_size, $text ) ;
# get the buffer of lines
$lines_ref = $self->{'lines'} ;
while( 1 ) {
# see if there is more than 1 line in the buffer
if ( @{$lines_ref} > 1 ) {
# we have a complete line so return it
return( pop @{$lines_ref} ) ;
}
# we don't have a complete, so have to read blocks until we do
$seek_pos = $self->{'seek_pos'} ;
# see if we are at the beginning of the file
if ( $seek_pos == 0 ) {
# the last read never made more lines, so return the last line in the buffer
# if no lines left then undef will be returned
return( pop @{$lines_ref} ) ;
}
#print "c size $read_size\n" ;
# we have to read more text so get the handle and the current read size
$handle = $self->{'handle'} ;
$read_size = $self->{'read_size'} ;
# after the first read, always read the maximum size
$self->{'read_size'} = $max_read_size ;
# seek to the beginning of this block and save the new seek position
$seek_pos -= $read_size ;
$self->{'seek_pos'} = $seek_pos ;
seek( $handle, $seek_pos, 0 ) ;
#print "seek $seek_pos\n" ;
# read in the next (previous) block of text
$read_cnt = sysread( $handle, $read_buf, $read_size ) ;
#print "Read <$read_buf>\n" ;
# if ( $read_cnt != $read_size ) {
# print "bad read cnt $read_cnt != size $read_size\n" ;
# return( undef ) ;
# }
# prepend the read buffer to the leftover (possibly partial) line
$text = $read_buf . ( pop @{$lines_ref} || '' ) ;
# split the buffer into a list of lines
# this may want to be $/ but reading files backwards assumes plain text and
# newline separators
@{$lines_ref} = $text =~ m[(^.*\n|^.+)]mg ;
#print "Lines \n=>", join( "<=\n=>", @{$lines_ref} ), "<=\n" ;
}
}
__END__
=head1 NAME
Backwards.pm -- Read a file backwards by lines.
=head1 SYNOPSIS
use Backwards ;
# Object interface
$bw = Backwards->new( 'log_file' ) ;
while( $log_line = $bw->readline ) {
print $log_line ;
}
# Tied Handle Interface
tie *BW, 'log_file' ;
while( <BW> ) {
print ;
}
=head1 DESCRIPTION
This module reads a file backwards line by line. It is simple to use,
memory efficient and fast. It supports both an object and a tied handle
interface.
It is intended for processing log and other similar text files which
typically have new entries appended. It uses newline as the separator
and not $/ since it is only meant to be used for text.
It works by reading large (8kb) blocks of text from the end of the file,
splits them on newlines and stores the other lines until the buffer runs
out. Then it seeks to the previous block and splits it. When it reaches
the beginning of the file, it stops reading more blocks. All boundary
conditions are handled correctly. If there is a trailing partial line
(no newline) it will be the first line returned. Lines larger than the
read buffer size are ok.
=head2 Object Interface
There are only 2 methods in Backwards' object interface, new and
readline.
=head2 new
New takes just a filename for an argument and it either returns the
object on a successful open on that file or undef.
=head2 readline
Readline takes no arguments and it returns the previous line in the file
or undef when there are no more lines in the file.
=head2 Tied Handle Interface
The only tied handle calls supported are TIEHANDLE and READLINE and they
are typeglobbed to new and readline respectively. All other tied handle
operations will generate an unknown method error. Do not seek, write or
do any other operation other than <> on the handle.

View File

@@ -0,0 +1 @@
<font size=+2>Click on the <b>filename and line number</b> in the log and the source will appear in this window.</font>

View File

@@ -0,0 +1,77 @@
#!gmake
#
# 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 the Tinderbox build tool.
#
# 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.
# This Makefile helps you install Tinderbox. Define PERL and MYSQLTCL to
# the full pathnames of where you have these utilities. Define PREFIX
# to where you will install the running Tinderbox. Then "make install" should
# copy things for you.
#BONSAI = /home/httpd/html/bonsai
#CVSROOT = /cvsroot
#GZIP = /usr/bin/gzip
#PERL = /usr/bin/perl
#PREFIX = /home/httpd/html/tinderbox
#UUDECODE = /usr/bin/uudecode
FILES = addimage.cgi \
addnote.cgi \
admintree.cgi \
buildwho.pl \
clean.pl \
copydata.pl \
doadmin.cgi \
ep_mac.pl \
ep_unix.pl \
ep_windows.pl \
fixupimages.pl \
globals.pl \
handlemail.pl \
imagelog.pl \
processbuild.pl \
showbuilds.cgi \
showimages.cgi \
showlog.cgi \
Empty.html \
faq.html \
index.html \
examples/mozilla-unix.pl \
examples/mozilla-windows.pl
PICS = \
1afi003r.gif \
reledanim.gif \
star.gif
install:
mkdir -p $(PREFIX) && \
for I in $(FILES); do \
echo Installing $$I && \
sed -e s#/usr/bonsaitools/bin/perl#$(PERL)#g \
-e s#/tools/ns/bin/perl5#$(PERL)#g \
-e s#/m/src#$(CVSROOT)#g \
-e s#/usr/local/bin/gzip#$(GZIP)#g \
-e s#/tools/ns/bin/uudecode#$(UUDECODE)#g \
-e s#/d/webdocs/projects/bonsai#$(BONSAI)#g \
$$I > $(PREFIX)/$$I && \
chmod 755 $(PREFIX)/$$I; done && \
for I in $(PICS); do \
echo Installing $$I && \
cp -f $$I $(PREFIX) && \
chmod 755 $(PREFIX)/$$I; done

View File

@@ -0,0 +1,287 @@
This is Tinderbox. See <http://www.mozilla.org/tinderbox.html>.
==========
DISCLAIMER
==========
This is not very well packaged code. It's not packaged at all. Don't
come here expecting something you plop in a directory, twiddle a few
things, and you're off and using it. Much work has to be done to get
there. We'd like to get there, but it wasn't clear when that would be,
and so we decided to let people see it first.
Don't believe for a minute that you can use this stuff without first
understanding most of the code.
============
DEPENDENCIES
============
To use tinderbox, you must first have bonsai up and running.
See <http://www.mozilla.org/bonsai.html>.
Be warned now that bonsai is not easily installed.
====================================
What's What in the Tinderbox sources:
====================================
This is a rough first pass at cataloging and documenting the Tinderbox
sources. Many hands have been in this code over the years, and it has
accreted wildly. There is probably quite a lot of dead code in here.
PROGRAMS
========
handlemail.pl This is the mail deliverty agent (MDA) for tinderbox,
the local message transfer agent (MTA) calls this
program to deliver the tinderbox mail into the system.
It is a wrapper for processbuild.pl.
processbuild.pl Update the "$tbx{tree}/build.dat" database as new
mail comes in then run "./buildwho.pl $tree" and
"./showbuilds.cgi" (to build static versions of
tinderbox data.
buildwho.pl Update the who.dat file with the list of authors who
checked in in the last two days. This is run from
processbuild.pl, always, and from showbuild.cgi when
'rebuildguilty' is clicked
Conceptually, the three programs, handlemail.pl, processbuild.pl, and
buildwho.pl, make up the MDA for tinderbox.
showbuilds.cgi Create the Tinderbox web page.
showlog.cgi Show a build log (brief and full) update the brief_log if
necessary. Requires the ep_$form{errorparser}.pl error
parser.
ep_unix.pl Knows how to parse Unix build error logs. Used by
processbuild.pl There needs to be one ep_* file for
each distinct parsing algorithm used, typically this
is per platform. This file defines the regular
expressions which locate error lines: has_error()
has_warning() and the function has_errorline() which
parses the line into the global variables:
$error_file, $error_file_ref, $error_line, $error_guess,
admintree.cgi Displays the admin cgi which depends on:
$message_of_day, $ignore_builds
doadmin.cgi Actually do the work to admin a tinderbox tree
(change message of the day, turn off displays for a
channel)
clean.pl Run `find . -name \"*.gz\" -mtime +7 -print ` and
unlink those files. Does not appear to be run from
other tools. It is a good candidate for a cron job.
OPTIONS to showbuilds.cgi
=========================
Options to showbuilds are specified in the URL and are undocumented
elsewhere.
If called with no 'tree' option display the possible build trees to
pick from. The 'tree' picks the build to display. An additional tree
can be specified with 'tree2'.
Interesting visual params are:
current state monitoring mode:
express=1
or
panel=1
text mode state monitoring:
quickparse=1
These modes do not show on my browser:
flash=1
rdf=1
static=1
These are self explanatory:
nocrap=1;
hours=n;
EMAIL FORMAT
============
Each tinderbox client mails status updates to the tinderbox server.
These mails contain special tinderbox data lines describing the
progress of the build it may also contain the log of the build process
and could contain a uuencoded binary.
The email to the tinderbox server looks like:
tinderbox: tree: Mozilla
tinderbox: builddate: 900002087
tinderbox: status: building
tinderbox: build: IRIX 6.3 Depend
tinderbox: errorparser: unix
If binaryname is set then the mail message is run through uudecode to
create a file called "$binaryname" on the tinderbox server.
# NOT USED tinderbox: buildfamily: unix
DATA STRUCTURES IN showbuilds.cgi
=================================
load_buildlog() creates build_list a list of hash refernces of this
type
$buildrec = {
mailtime => $mailtime,
buildtime => $buildtime,
buildname => ($tree2 ne '' ? $t->{name} . ' ' : '' ) . $buildname,
errorparser => $errorparser,
buildstatus => $buildstatus,
logfile => $logfile,
binaryname => $binaryname,
td => $t
};
the $buildrec->{rowspan} variable holds the number of rows that the build
should occupy on the table and is not stored in the build.dat file.
These are other add ons to the data structure
$buildrec->{hasnote}=1;
$buildrec->{noteid} = (0+@note_array);
commonly buildrec's are accessed through:
$build_table->[$build_time][$build_name] = $buildrec;
The list of users who updated this build is stored as:
$who_list->[$checkin_time]->{$author} = 1;
There are numerous duplicate data structures which hold partial
information:
hashes which hold all indices:
$build_name_index->{$br->{buildname}} = 1;
$build_time_index->{$br->{buildtime}} = 1;
other access into $build_table:
$build_name_names->[$i] = $n;
$build_time_times->[$i] = $n;
loadquickparseinfo creates these references
$build->{$buildname} = $buildstatus;
$times->{$buildname} = $buildtime;
DATA FILES
==========
These files are used to store data structures. They are a persistent
store of data between executions of the same program and they pass the
data between cooperating program. This data is often databases with
rows separated by "\n" and columns by '|'.
$tree/${logfile}
$tree/${logfile}.brief.html
$tree/ignorebuilds.pl
$tree/mod.pl
$tree/notes.txt
$tree/treedata.pl
$tree/who.dat
$treename/build.dat
The logfile that the tinderbox client sent stored in gziped format.
The filename is ${tree}/$builddate.$$.gz so its quite random and does
not depend on the clients$build string and multiple logs are kept for
each build, one for each mail message sent.
The brief.html file is a cache of the error log summary for this log
file and is recreated when the logfile gets updated.
ignorebuilds.pl is a file which specifies builds that should not be
performed. It is valid perl code which sets the hash reference
$ignore_builds, each key is a tree name.
mod.pl stores the tree specific message of the day. This is not to be
confused with mod perl the CGI library. Its contents looks like:
$message_of_day = 'message';
notes.txt store the database of notes:
$buildtime|$buildname||$author|$time_now|$note
treedata.pl stores the cvs information pertaining to this tree and
looks like this:
$cvs_module='$modulename';
$cvs_branch='$branchname';
$cvs_root='$repository';
who.dat file has lines like:
$checkin_time|$author
This gets stored in the data structure, where checkin_time
gets fudged up so that is is a time already in the
build_table:
$who_list->[$checkin_time]->{$author} = 1;
build.dat stores the build results table. It is a flat file
representation of $build_table
build.dat is a database file each row is a build and has pipe
separated columns:
1) the time stamp of the tinderbox server
2) time stamp of the build machine
3) the official build name (should include build machine name)
( note: that 2 & 3 together uniquely identify the build
and all relevant build data)
4) the architecture dependent error parser to use on the log files
5) status of the build (success|busted|building|testfailed)
6) The log file for this build (if completed)
7) the name of the binary (if any) that came from the build
Other Files
====================
1afi003r.gif The "flames" animation used by showbuilds.cgi
Empty.html Document used for an empty frame by ???
addimage.cgi The form that lets you add a new image to the list of
images that Tinderbox picks from randomly.
addnote.cgi Add a note to a build log.
admintree.cgi Lets you perform various admin tasks on a Tinderbox tree.
This just prompts for a password and posts to doadmin.cgi.
clean.pl ???
copydata.pl ???
doadmin.cgi Actually do the work to admin a tinderbox tree
ep_mac.pl Knows how to parse Mac build error logs. Used by ???
ep_unix.pl Knows how to parse Unix build error logs. Used by ???
ep_windows.pl Knows how to parse Windows build error logs. Used by ???
faq.html Wildly out of date.
fixupimages.pl ???
globals.pl ???
imagelog.pl ???
index.html ???
reledanim.gif ???
showimages.cgi Show all the images in the Tinderbox list. Password-protected.
star.gif The "star" image used to annotate builds by ???

View File

@@ -0,0 +1,404 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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 the Tinderbox build tool.
#
# 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.
use Socket;
use lib "../bonsai";
require 'header.pl';
print "Content-type: text/html\n\n";
EmitHtmlTitleAndHeader("tinderbox: add images", "add images");
$| = 1;
require "globals.pl";
require "imagelog.pl";
&split_cgi_args;
sub Error {
my ($msg) = @_;
print "<BR><BR><BR>";
print "<UL><FONT SIZE='+1'><B>Something went wrong:</B><P>";
print "<UL>";
print $msg;
print "</UL>";
print "<P>";
print "Hit <B>\`Back'</B> and try again.";
print "</UL>";
exit 1;
}
if( $url = $form{"url"} ){
$quote = $form{"quote"};
$quote =~ s/[\r\n]/ /g;
$url =~ s/[\r\n]/ /g;
$width = "";
$height = "";
# I think we don't want to allow this --jwz
# $width = $form{"width"};
# $height = $form{"height"};
if ($width eq "" || $height eq "") {
$size = &URLsize($url);
if ($size =~ /WIDTH=([0-9]*)/) {
$width = $1;
}
if ($size =~ /HEIGHT=([0-9]*)/) {
$height = $1;
}
if ($width eq "" || $height eq "") {
Error "Couldn't get image size for \"$url\".\n";
}
}
print "
<P><center><img border=2 src='$url' width=$width height=$height><br>
<i>$quote</i><br><br>
";
if( $form{"submit"} ne "Yes" ){
my $u2 = $url;
my $q2 = $quote;
$u2 =~ s@&@&amp;@g; $u2 =~ s@<@&lt;@g; $u2 =~ s@\"@&quot;@g;
$q2 =~ s@&@&amp;@g; $q2 =~ s@<@&lt;@g; $q2 =~ s@\"@&quot;@g;
print "
<form action='addimage.cgi' METHOD='get'>
<input type=hidden name=url value=\"$u2\">
<input type=hidden name=quote value=\"$q2\">
<HR>
<TABLE>
<TR>
<TH ALIGN=RIGHT NOWRAP>Image URL:</TH>
<TD><TT><B>$u2</B></TT></TD>
</TR><TR>
<TH ALIGN=RIGHT>Caption:</TH>
<TD><TT><B>$q2</B></TT></TD>
</TR>
<TR>
<TD></TD>
<TD>
<FONT SIZE=+2><B>
Does that look right?
<SPACER SIZE=10>
<INPUT Type='submit' name='submit' value='Yes'>
</B><BR>(If not, hit \`Back' and fix it.)
</FONT>
</TD>
</TABLE>
</form>
";
}
else {
&add_imagelog( $url, $quote, $width, $height );
print "<br><br>
<font size=+2>Has been added</font><br><br>
<a href=showbuilds.cgi>Return to Log</a>";
}
}
else {
print "
<h2>Add an image and a funny caption.</h2>
<ul>
<p>This is about fun, and making your daily excursion to
<A HREF=http://www.mozilla.org/tinderbox.html>Tinderbox</A> a
novel experience. Engineers spend a lot of time here; it might as well
have some entertainment value.
<p>Please play nice. We don't have the time or inclination to look at
everything you people submit, but if we get nastygrams or legalgrams
and have to take something down, we will curse your IP address, and you
might even make it so the whole thing goes away forever. Please don't
make us go there. You might also avoid links to big images or slow
servers.
<p><ul><B>Thank you for playing nice.</B></UL>
<p>If you really find an image offensive, please
<A HREF=mailto:terry\@netscape.com?Subject=offensive%20tinderbox%20image>tell us</A>
nicely before someone causes a stink. Be sure to include the URL of
the image. Remember, we don't screen these submissions and may not
have even seen it.
<p><ul><B>P.S. Please, no more pictures of Bill Gates.</B></UL>
</ul>
<p><form action='addimage.cgi' METHOD='get'>
<TABLE>
<TR>
<TH ALIGN=RIGHT NOWRAP>Image URL:</TH>
<TD><INPUT NAME='url' SIZE=60></TD>
</TR><TR>
<TH ALIGN=RIGHT>Caption:</TH>
<TD><INPUT NAME='quote' SIZE=60></TD>
</TR><TR>
<TD></TD>
<TD><B>
<INPUT Type='submit' name='submit' value='Test'>
<SPACER SIZE=25>
<INPUT Type='reset' name='reset' value='Reset'>
</B></TD>
</TR>
</TABLE>
</form>
<br><br>
";
}
sub split_cgi_args {
local($i,$var,$value, $s);
$s = $ENV{"QUERY_STRING"};
@args= split(/\&/, $s );
for $i (@args) {
($var, $value) = split(/=/, $i);
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$form{$var} = $value;
}
}
#sub imgsize {
# local($file)= @_;
#
# #first try to open the file
# if( !open(STREAM, "<$file") ){
# Error "Can't open IMG $file";
## $size="";
# } else {
# if ($file =~ /.jpg/i || $file =~ /.jpeg/i) {
# $size = &jpegsize(STREAM);
# } elsif($file =~ /.gif/i) {
# $size = &gifsize(STREAM);
# } elsif($file =~ /.xbm/i) {
# $size = &xbmsize(STREAM);
# } else {
# return "";
# }
# $_ = $size;
# if( /\s*width\s*=\s*([0-9]*)\s*/i ){
# ($newwidth)= /\s*width\s*=\s*(\d*)\s*/i;
# }
# if( /\s*height\s*=\s*([0-9]*)\s*/i ){
# ($newheight)=/\s*height\s*=\s*(\d*)\s*/i;
# }
# close(STREAM);
# }
# return $size;
#}
###########################################################################
# Subroutine gets the size of the specified GIF
###########################################################################
# bug: it thinks that
# http://cvs1.mozilla.org/webtools/tinderbox/data/knotts.gif
# is 640x400, but it's really 200x245.
# giftrans says of that image:
#
# Header: "GIF87a"
# Logical Screen Descriptor:
# Logical Screen Width: 640 pixels
# Logical Screen Height: 480 pixels
# Image Descriptor:
# Image Width: 200 pixels
# Image Height: 245 pixels
sub gifsize {
local($GIF) = @_;
read($GIF, $type, 6);
if(!($type =~ /GIF8[7,9]a/) ||
!(read($GIF, $s, 4) == 4) ){
Error "Invalid or Corrupted GIF";
$size="";
} else {
($a,$b,$c,$d)=unpack("C"x4,$s);
$size=join ("", 'WIDTH=', $b<<8|$a, ' HEIGHT=', $d<<8|$c);
}
return $size;
}
sub xbmsize {
local($XBM) = @_;
local($input)="";
$input .= <$XBM>;
$input .= <$XBM>;
$_ = $input;
if( /#define\s*\S*\s*\d*\s*\n#define\s*\S*\s*\d*\s*\n/i ){
($a,$b)=/#define\s*\S*\s*(\d*)\s*\n#define\s*\S*\s*(\d*)\s*\n/i;
$size=join ("", 'WIDTH=', $a, ' HEIGHT=', $b );
} else {
Error "Doesn't look like an XBM file";
}
return $size;
}
# jpegsize : gets the width and height (in pixels) of a jpeg file
# Andrew Tong, werdna@ugcs.caltech.edu February 14, 1995
# modified slightly by alex@ed.ac.uk
sub jpegsize {
local($JPEG) = @_;
local($done)=0;
$size="";
read($JPEG, $c1, 1); read($JPEG, $c2, 1);
if( !((ord($c1) == 0xFF) && (ord($c2) == 0xD8))){
my $s = sprintf "This is not a JPEG! (Codes %02X %02X)\n", ord($c1), ord($c2);
Error $s;
$done=1;
}
while (ord($ch) != 0xDA && !$done) {
# Find next marker (JPEG markers begin with 0xFF)
# This can hang the program!!
while (ord($ch) != 0xFF) { read($JPEG, $ch, 1); }
# JPEG markers can be padded with unlimited 0xFF's
while (ord($ch) == 0xFF) { read($JPEG, $ch, 1); }
# Now, $ch contains the value of the marker.
$marker=ord($ch);
if (($marker >= 0xC0) && ($marker <= 0xCF) &&
($marker != 0xC4) && ($marker != 0xCC)) { # it's a SOFn marker
read ($JPEG, $junk, 3); read($JPEG, $s, 4);
($a,$b,$c,$d)=unpack("C"x4,$s);
$size=join("", 'HEIGHT=',$a<<8|$b,' WIDTH=',$c<<8|$d );
$done=1;
} else {
# We **MUST** skip variables, since FF's within variable
# names are NOT valid JPEG markers
read ($JPEG, $s, 2);
($c1, $c2) = unpack("C"x2,$s);
$length = $c1<<8|$c2;
if( ($length < 2) ){
Error "Bad JPEG file: erroneous marker length";
$done=1;
} else {
read($JPEG, $junk, $length-2);
}
}
}
return $size;
}
###########################################################################
# Subroutine grabs a gif from another server and gets its size
###########################################################################
sub URLsize {
my ($fullurl) = @_;
$_ = $fullurl;
if ( ! m@^http://@ ) {
Error "HTTP URLs only, please: \"$_\" is no good.";
}
my($dummy, $dummy, $serverstring, $url) = split(/\//, $fullurl, 4);
my($them,$port) = split(/:/, $serverstring);
my $port = 80 unless $port;
my $size="";
$_ = $them;
if ( m@^[^.]*$@ ) {
Error "Fully-qualified host names only, please: \"$_\" is no good.";
}
$_=$url;
my ($remote, $iaddr, $paddr, $proto, $line);
$remote = $them;
if ($port =~ /\D/) { $port = getservbyname($port, 'tcp') }
die "No port" unless $port;
$iaddr = inet_aton($remote) || die "no host: $remote";
$paddr = sockaddr_in($port, $iaddr);
$proto = getprotobyname('tcp');
socket(S, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
connect(S, $paddr) || die "connect: $!";
select(S); $| = 1; select(STDOUT);
print S "GET /$url HTTP/1.0\r\n";
print S "Host: $them\r\n";
print S "User-Agent: Tinderbox/0.0\r\n";
print S "\r\n";
$_ = <S>;
if (! m@^HTTP/[0-9.]+ 200@ ) {
Error "$them responded:<BR> $_";
}
my $ctype = "";
while (<S>) {
# print "read: $_<br>\n";
if ( m@^Content-Type:[ \t]*([^ \t\r\n]+)@io ) {
$ctype = $1;
}
last if (/^[\r\n]/);
}
$_ = $ctype;
if ( $_ eq "" ) {
Error "Server returned no content-type for \"$fullurl\"?";
} elsif ( m@image/jpeg@i || m@image/pjpeg@i ) {
$size = &jpegsize(S);
} elsif ( m@image/gif@i ) {
$size = &gifsize(S);
} elsif ( m@image/xbm@i || m@image/x-xbm@i || m@image/x-xbitmap@i ) {
$size = &xbmsize(S);
} else {
Error "Not a GIF, JPEG, or XBM: that was of type \"$ctype\".";
}
$_ = $size;
if( /\s*width\s*=\s*([0-9]*)\s*/i ){
($newwidth)= /\s*width\s*=\s*(\d*)\s*/i;
}
if( /\s*height\s*=\s*([0-9]*)\s*/i ){
($newheight)=/\s*height\s*=\s*(\d*)\s*/i;
}
if ( $newwidth eq "" || $newheight eq "" ) {
return "";
} else {
if ( $newwidth <= 5 || $newheight <= 5 ) {
Error "${newwidth}x${newheight} seems small, don't you think?";
} elsif ( $newwidth >= 400 || $newheight >= 400 ) {
Error "${newwidth}x${newheight} is too big; please" .
" keep it under 400x400."
}
return $size;
}
}
sub dokill {
kill 9,$child if $child;
}

View File

@@ -0,0 +1,206 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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 the Tinderbox build tool.
#
# 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.
use lib "../bonsai";
use Fcntl;
require "globals.pl";
require 'lloydcgi.pl';
if (defined($args = $form{log})) {
($tree, $logfile) = split /\//, $args;
my $br = find_build_record($tree, $logfile);
$errorparser = $br->{errorparser};
$buildname = $br->{buildname};
$buildtime = $br->{buildtime};
} else {
$tree = $form{'tree'};
$logfile = $form{'logfile'};
$errorparser = $form{'errorparser'};
$buildname = $form{'buildname'};
$buildtime = $form{'buildtime'};
}
$enc_buildname = &url_encode($buildname);
$note = $form{'note'};
$who = $form{'who'};
$now = time;
$now_str = &print_time($now);
$|=1;
if( -r "$tree/ignorebuilds.pl" ){
require "$tree/ignorebuilds.pl";
}
print "Content-Type:text/html\n";
if ($ENV{"REQUEST_METHOD"} eq 'POST' && defined($form{'note'})) {
# Expire the cookie 5 months from now
print "Set-Cookie: email=$form{who}; expires="
. toGMTString(time + 86400 * 152) . "; path=/\n";
}
print "\n<HTML>\n";
if( $url = $form{"note"} ){
$note =~ s/\&/&amp;/gi;
$note =~ s/\</&lt;/gi;
$note =~ s/\>/&gt;/gi;
$enc_note = url_encode( $note );
open( NOTES,">>$tree/notes.txt");
flock(NOTES, LOCK_EX);
print NOTES "$buildtime|$buildname|$who|$now|$enc_note\n";
&LoadBuildTable;
foreach $element (keys %form) {
if(exists ${$build_name_index}{$element}) {
print NOTES "${$build_name_index}{$element}|$element|$who|$now|$enc_note\n";
} #EndIf
} #Endforeach
close(NOTES);
print "<H1>The following comment has been added to the log</h1>\n";
#print "$buildname \n $buildtime \n $errorparser \n $logfile \n $tree \n $enc_buildname \n";
print "<pre>\n[<b>$who - $now_str</b>]\n$note\n</pre>";
print"
<p><a href=\"showlog.cgi?tree=$tree\&buildname=$enc_buildname\&buildtime=$buildtime\&logfile=$logfile\&errorparser=$errorparser\">
Go back to the Error Log</a>
<a href=\"showbuilds.cgi?tree=$tree\">
<br>Go back to the build Page</a>";
# Build tinderbox static pages
$ENV{QUERY_STRING}="tree=$tree&static=1";
$ENV{REQUEST_METHOD}="GET";
system './showbuilds.cgi >/dev/null';
} else {
&GetBuildNameIndex;
@names = sort (keys %$build_name_index);
if ($buildname eq '' || $buildtime == 0) {
print "<h1>Invalid parameters</h1>\n";
die "\n";
}
#print "$buildname \n $buildtime \n $errorparser \n $logfile \n $tree \n $enc_buildname \n";
$emailvalue = '';
$emailvalue = " value='$cookie_jar{email}'" if defined($cookie_jar{email});
print qq(
<head><title>Add a Comment to $buildname log</title></head>
<body BGCOLOR="#FFFFFF" TEXT="#000000"LINK="#0000EE" VLINK="#551A8B" ALINK="#FF0000">
<table><tr><td>
<b><font size="+2">Add a Log Comment</font></b>
</td></tr><tr><td>
<b><code>$buildname</code></b>
</td></tr></table>
<form action='addnote.cgi' METHOD='post'>
<INPUT Type='hidden' name='buildname' value='${buildname}'>
<INPUT Type='hidden' name='buildtime' value='${buildtime}'>
<INPUT Type='hidden' name='errorparser' value='$errorparser'>
<INPUT Type='hidden' name='logfile' value='$logfile'>
<INPUT Type='hidden' name='tree' value='$tree'>
<table border=0 cellpadding=4 cellspacing=1>
<tr valign=top>
<td align=right>
<NOWRAP>Email address:</NOWRAP>
</td><td>
<INPUT Type='input' name='who' size=32$emailvalue><BR>
</td>
</tr><tr valign=top>
<td align=right>
Comment:
</td><td>
<TEXTAREA NAME=note ROWS=10 COLS=30 WRAP=HARD></textarea>
</td>
</tr>
</table>
<br><b><font size="+2">Addition Builds</font></b><br>
(Comment will be added to the most recent cycle.)<br>
);
for $other_build (@names){
if( $other_build ne "" ){
if (not exists ${$ignore_builds}{$other_build}) {
if( $other_build ne $buildname ){
print "<INPUT TYPE=checkbox NAME=\"$other_build\">";
print "$other_build<BR>\n";
}
} #EndIf
}
} #Endfor
print "<INPUT Type='submit' name='submit' value='Add Comment'><BR>
</form>\n</body>\n</html>";
}
sub GetBuildNameIndex {
local($mailtime, $buildtime, $buildname, $errorparser, $buildstatus, $logfile, $binaryname);
open(BUILDLOG, "$tree/build.dat") or die "Couldn't open build.dat: $!\n";
while(<BUILDLOG>) {
chomp;
($mailtime, $buildtime, $buildname, $errorparser, $buildstatus, $logfile, $binaryname) =
split( /\|/ );
$build_name_index->{$buildname} = 0;
} #EndWhile
close(BUILDLOG);
}
sub LoadBuildTable {
local($mailtime, $buildtime, $buildname, $errorparser, $buildstatus, $logfile, $binaryname);
open(BUILDLOG, "$tree/build.dat") or die "Couldn't open build.dat: $!\n";
while(<BUILDLOG>) {
chomp;
($mailtime, $buildtime, $buildname, $errorparser, $buildstatus, $logfile, $binaryname) =
split( /\|/ );
if ($buildtime > $build_name_index->{$buildname} ) {
$build_name_index->{$buildname} = $buildtime;
}
} #EndWhile
close(BUILDLOG);
}

View File

@@ -0,0 +1,131 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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 the Tinderbox build tool.
#
# 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.
use lib "../bonsai";
require 'lloydcgi.pl';
require 'globals.pl';
require 'header.pl';
$|=1;
print "Content-type: text/html\n\n<HTML>\n";
EmitHtmlHeader("administer tinderbox", "tree: $tree");
$form{noignore} = 1; # Force us to load all build info, not
# paying any attention to ignore_builds stuff.
$maxdate = time();
$mindate = $maxdate - 24*60*60;
&load_data;
if (defined($tree)) {
if( -r "$tree/mod.pl" ){
require "$tree/mod.pl";
}
else {
$message_of_day = "";
}
print "
<FORM method=post action=doadmin.cgi>
<B>Password:</B> <INPUT NAME=password TYPE=password>
<INPUT TYPE=HIDDEN NAME=tree VALUE=$tree>
<INPUT TYPE=HIDDEN NAME=command VALUE=set_message>
<br><b>Message of the Day
<br><TEXTAREA NAME=message ROWS=10 COLS=70 WRAP=SOFT>$message_of_day
</TEXTAREA>
<br><INPUT TYPE=SUBMIT VALUE='Set Message of the Day'>
</FORM>
<hr>
";
print "
<FORM method=post action=doadmin.cgi>
<B>Password:</B> <INPUT NAME=password TYPE=password>
<INPUT TYPE=HIDDEN NAME=tree VALUE=$tree>
<INPUT TYPE=HIDDEN NAME=command VALUE=trim_logs>
<br><b>Trim Logs to <INPUT NAME=days size=5 VALUE='7'> days.</b> (Tinderbox
shows 2 days history by default. You can see more by clicking show all).
<br><INPUT TYPE=SUBMIT VALUE='Trim Logs'>
</FORM>
<FORM method=post action=doadmin.cgi>
<hr>
" ;
}
print "
<FORM method=post action=doadmin.cgi>
<B>Password:</B> <INPUT NAME=password TYPE=password> <BR>
<INPUT TYPE=HIDDEN NAME=tree VALUE=$tree>
<INPUT TYPE=HIDDEN NAME=command VALUE=create_tree>
<TABLE>
<TR>
<TD><B>tinderbox tree name:</B></TD>
<TD><INPUT NAME=treename VALUE=''></TD>
</TR><TR>
<TD><B>cvs repository:</B></TD>
<TD><INPUT NAME=repository VALUE=''></TD>
</TR><TR>
<TD><B>cvs module name:</B></TD>
<TD><INPUT NAME=modulename VALUE=''></TD>
</TR><TR>
<TD><B>cvs branch:</B></TD>
<TD><INPUT NAME=branchname VALUE='HEAD'></TD>
</TR>
</TABLE>
<INPUT TYPE=SUBMIT VALUE='Create a new Tinderbox page'>
</FORM>
<FORM method=post action=doadmin.cgi>
<hr>
";
if (defined($tree)) {
print "
<B><font size=+1>If builds are behaving badly you can turn them off.</font></b><br> Uncheck
the build that is misbehaving and click the button. You can still see all the
builds even if some are disabled by adding the parameter <b><tt>&noignore=1</tt></b> to
the tinderbox URL.<br>
<B>Password:</B> <INPUT NAME=password TYPE=password> <BR>
<INPUT TYPE=HIDDEN NAME=tree VALUE=$tree>
<INPUT TYPE=HIDDEN NAME=command VALUE=disable_builds>
";
@names = sort (@$build_name_names) ;
for $i (@names){
if( $i ne "" ){
$checked = ($ignore_builds->{$i} != 0 ? "": "CHECKED" );
print "<INPUT TYPE=checkbox NAME='build_$i' $checked >";
print "$i<br>\n";
}
}
print "
<INPUT TYPE=SUBMIT VALUE='Show only checked builds'>
</FORM>
<hr>
";
}

View File

@@ -0,0 +1,97 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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 the Tinderbox build tool.
#
# 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.
use lib "../bonsai";
require 'globals.pl';
$F_DEBUG=1;
$days = 2;
if ($ARGV[0] eq "-days") {
shift @ARGV;
$days = shift @ARGV;
}
$tree = $ARGV[0];
open(SEMFILE, ">>$tree/buildwho.sem") || die "Couldn't open semaphore file!";
if (!flock(SEMFILE, 2 + 4)) { # 2 means "lock"; 4 means "fail immediately if
# lock already taken".
print "buildwho.pl: Another process is currently building the database.\n";
exit(0);
}
require "$tree/treedata.pl";
if( $cvs_root eq '' ){
$CVS_ROOT = '/m/src';
}
else {
$CVS_ROOT = $cvs_root;
}
$CVS_REPOS_SUFIX = $CVS_ROOT;
$CVS_REPOS_SUFIX =~ s/\//_/g;
$CHECKIN_DATA_FILE = "/d/webdocs/projects/bonsai/data/checkinlog${CVS_REPOS_SUFIX}";
$CHECKIN_INDEX_FILE = "/d/webdocs/projects/bonsai/data/index${CVS_REPOS_SUFIX}";
require 'cvsquery.pl';
print "cvsroot='$CVS_ROOT'\n";
&build_who;
flock(SEMFILE, 8); # '8' is magic 'unlock' const.
close SEMFILE;
sub build_who {
open(BUILDLOG, "<$tree/build.dat" );
$line = <BUILDLOG>;
close(BUILDLOG);
#($j,$query_date_min) = split(/\|/, $line);
$query_date_min = time - (60 * 60 * 24 * $days);
if( $F_DEBUG ){
print "Minimum date: $query_date_min\n";
}
$query_module=$cvs_module;
$query_branch=$cvs_branch;
$result = &query_checkins;
$last_who='';
$last_date=0;
open(WHOLOG, ">$tree/who.dat" );
for $ci (@$result) {
if( $ci->[$CI_DATE] != $last_date || $ci->[$CI_WHO] != $last_who ){
print WHOLOG "$ci->[$CI_DATE]|$ci->[$CI_WHO]\n";
}
$last_who=$ci->[$CI_WHO];
$last_date=$ci->[$CI_DATE];
}
close( WHOLOG );
}

View File

@@ -0,0 +1,124 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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 the Tinderbox build tool.
#
# 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.
use lib "../bonsai";
require 'lloydcgi.pl';
require 'globals.pl';
require 'header.pl';
use Date::Parse;
use Date::Format;
my $TIMEFORMAT = "%D %T";
$|=1;
print "Content-type: text/html\n\n<HTML>\n";
# &load_data;
EmitHtmlHeader("Statistics");
my $tree = $form{'tree'};
my $start = $form{'start'};
my $end = $form{'end'};
sub str2timeAndCheck {
my ($str) = (@_);
my $result = str2time($str);
if (defined $result && $result > 7000000) {
return $result;
}
print "<p><font color=red>Can't parse as a date: $str</font><p>\n";
return 0;
}
if (defined $tree && defined $start && defined $end) {
my $first = str2timeAndCheck($start);
my $last = str2timeAndCheck($end);
if ($first > 0 && $last > 0) {
if (open(IN, "<$tree/build.dat")) {
print "<hr><center><h1>Bustage stats for $tree</H1><H3>from " .
time2str($TIMEFORMAT, $first) . " to " .
time2str($TIMEFORMAT, $last) . "</H3></center>\n";
my %stats;
while (<IN>) {
chomp;
my ($mailtime, $buildtime, $buildname, $errorparser,
$buildstatus, $logfile, $binaryname) =
split( /\|/ );
if ($buildtime >= $first && $buildtime <= $last) {
if (!defined $stats{$buildname}) {
$stats{$buildname} = 0;
}
if ($buildstatus eq "busted") {
$stats{$buildname}++;
}
}
}
print "<table>\n";
print "<tr><th>Build name</th><th>Number of bustages</th></tr>\n";
foreach my $key (sort (keys %stats)) {
print "<tr><td>$key</td><td>$stats{$key}</td></tr>\n";
}
print "</table>\n";
} else {
print "<p><font color=red>There does not appear to be a tree " .
"named '$tree'.</font><p>";
}
}
print "<hr>\n";
}
if (!defined $tree) {
$tree = "";
}
if (!defined $start) {
$start = time2str($TIMEFORMAT, time() - 7*24*60*60); # One week ago.
}
if (!defined $end) {
$end = time2str($TIMEFORMAT, time()); # #now
}
print qq|
<form>
<table>
<tr>
<th align=right>Tree:</th>
<td><input name=tree size=30 value="$tree"></td>
</tr>
<tr>
<th align=right>Start time:</th>
<td><input name=start size=30 value="$start"></td>
</tr>
<tr>
<th align=right>End time:</th>
<td><input name=end size=30 value="$end"></td>
</tr>
</table>
<INPUT TYPE=\"submit\" VALUE=\"Generate stats \">
</form>
|;

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

View File

@@ -0,0 +1,42 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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 the Tinderbox build tool.
#
# 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.
# Figure out which directory tinderbox is in by looking at argv[0]
$tinderboxdir = $0;
$tinderboxdir =~ s:/[^/]*$::; # Remove last word, and slash before it.
if ($tinderboxdir eq "") {
$tinderboxdir = ".";
}
#print "tinderbox = $tinderboxdir\n";
chdir $tinderboxdir || die "Couldn't chdir to $tinderboxdir";
#print "cd ok\n";
open FL, "find . -name \"*.gz\" -mtime +7 -print |";
#print "find ok\n";
while( <FL> ){
chop();
#print "unlink $_\n";
unlink $_;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 B

View File

@@ -0,0 +1,91 @@
#!/tools/ns/bin/perl5
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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 the Tinderbox build tool.
#
# 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.
#
# to run this script execute 'nohup copydata.pl &' from the tinderbox directory
#
$start_dir = `pwd`;
chop($start_dir);
$scp_cmd = "scp -o 'User snapshot' -o'Port 22' -o 'UserKnownHostsFile /u/shaver/snapshot/known_hosts' -o 'IdentityFile /u/shaver/snapshot/identity'";
$last_time = 0;
$min_cycle_time = 3 * 60; # 3 minutes
print "starting dir is :$start_dir\n";
while( 1 ){
chdir("$start_dir");
if( time - $last_time < $min_cycle_time ){
$sleep_time = $min_cycle_time - (time - $last_time);
print "\n\nSleeping $sleep_time seconds ...\n";
sleep( $sleep_time );
}
&copy_data("Mozilla");
&copy_data("raptor");
chdir( "$start_dir");
print "$scp_cmd * cvs1.mozilla.org:/e/webtools/tinderbox\n";
system "$scp_cmd * cvs1.mozilla.org:/e/webtools/tinderbox";
chdir( "$start_dir/../bonsai");
print "$scp_cmd * cvs1.mozilla.org:/e/webtools/bonsai\n";
system "$scp_cmd * cvs1.mozilla.org:/e/webtools/bonsai";
$last_time = time;
}
1;
sub copy_data {
local($data_dir) = @_;
local($zips,$qry);
chdir $data_dir || die "couldn't chdir to $data_dir";
system "echo hello >lastup.new";
if( -r 'lastup' ) {
$qry = '-newer lastup.old';
rename 'lastup', 'lastup.old'
}
rename 'lastup.new', 'lastup';
open( FINDER, "find . $qry -name \"*.gz\" -print|" );
while(<FINDER>){
print;
chop;
$zips .= "$_ ";
}
close( FINDER );
unlink 'lastup.old';
print "$scp_cmd *.txt $zips *.dat cvs1.mozilla.org:/e/webtools/tinderbox/$data_dir\n";
system "$scp_cmd *.txt $zips *.dat cvs1.mozilla.org:/e/webtools/tinderbox/$data_dir";
chdir $start_dir || die "couldn't chdir to $start_dir";
}

View File

@@ -0,0 +1,217 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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 the Tinderbox build tool.
#
# 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.
use lib "../bonsai";
require 'lloydcgi.pl';
require 'globals.pl';
umask O666;
$|=1;
check_password();
print "Content-type: text/html\n\n<HTML>\n";
$command = $form{'command'};
$tree= $form{'tree'};
if( $command eq 'create_tree' ){
&create_tree;
}
elsif( $command eq 'remove_build' ){
&remove_build;
}
elsif( $command eq 'trim_logs' ){
&trim_logs;
}
elsif( $command eq 'set_message' ){
&set_message;
}
elsif( $command eq 'disable_builds' ){
&disable_builds;
} else {
print "Unknown command: \"$command\".";
}
sub trim_logs {
$days = $form{'days'};
$tree = $form{'tree'};
print "<h2>Trimming Log files for $tree...</h2>\n<p>";
$min_date = time - (60*60*24 * $days);
#
# Nuke the old log files
#
$i = 0;
opendir( D, 'DogbertTip' );
while( $fn = readdir( D ) ){
if( $fn =~ /\.gz$/ ){
($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,
$ctime,$blksize,$blocks) = stat("$tree/$fn");
if( $mtime && ($mtime < $min_date) ){
print "$fn\n";
$tblocks += $blocks;
unlink( "$tree/$fn" );
$i++;
}
}
}
closedir( D );
$k = $tblocks*512/1024;
print "<br><b>$i Logfiles ( $k K bytes ) removed</b><br>\n";
#
# Trim build.dat
#
$builds_removed = 0;
open(BD, "<$tree/build.dat");
open(NBD, ">$tree/build.dat.new");
while( <BD> ){
($mailtime,$buildtime,$buildname) = split( /\|/ );
if( $buildtime >= $min_date ){
print NBD $_;
}
else {
$builds_removed++;
}
}
close( BD );
close( NBD );
rename( "$tree/build.dat", "$tree/build.dat.old" );
rename( "$tree/build.dat.new", "$tree/build.dat" );
print "<h2>$builds_removed Builds removed from build.dat</h2>\n";
}
sub create_tree {
$treename = $form{'treename'};
my $repository = $form{'repository'};
$modulename = $form{'modulename'};
$branchname = $form{'branchname'};
if( -r $treename ){
chmod 0777, $treename;
}
else {
mkdir( $treename, 0777 ) || die "<h1> Cannot mkdir $treename</h1>";
}
open( F, ">$treename/treedata.pl" );
print F "\$cvs_module='$modulename';\n";
print F "\$cvs_branch='$branchname';\n";
if ($repository ne "") {
print F "\$cvs_root='$repository';\n";
}
close( F );
open( F, ">$treename/build.dat" );
close( F );
open( F, ">$treename/who.dat" );
close( F );
open( F, ">$treename/notes.txt" );
close( F );
chmod 0777, "$treename/build.dat", "$treename/who.dat", "$treename/notes.txt",
"$treename/treedata.pl";
print "<h2><a href=showbuilds.cgi?tree=$treename>Tree created or modified</a></h2>\n";
}
sub remove_build {
$build_name = $form{'build'};
#
# Trim build.dat
#
$builds_removed = 0;
open(BD, "<$tree/build.dat");
open(NBD, ">$tree/build.dat.new");
while( <BD> ){
($mailtime,$buildtime,$bname) = split( /\|/ );
if( $bname ne $build_name ){
print NBD $_;
}
else {
$builds_removed++;
}
}
close( BD );
close( NBD );
chmod( 0777, "$tree/build.dat.new");
rename( "$tree/build.dat", "$tree/build.dat.old" );
rename( "$tree/build.dat.new", "$tree/build.dat" );
print "<h2><a href=showbuilds.cgi?tree=$tree>
$builds_removed Builds removed from build.dat</a></h2>\n";
}
sub disable_builds {
my $i,%buildnames;
$build_name = $form{'build'};
#
# Trim build.dat
#
open(BD, "<$tree/build.dat");
while( <BD> ){
($mailtime,$buildtime,$bname) = split( /\|/ );
$buildnames{$bname} = 0;
}
close( BD );
for $i (keys %form) {
if ($i =~ /^build_/ ){
$i =~ s/^build_//;
$buildnames{$i} = 1;
}
}
open(IGNORE, ">$tree/ignorebuilds.pl");
print IGNORE '$ignore_builds = {' . "\n";
for $i ( sort keys %buildnames ){
if( $buildnames{$i} == 0 ){
print IGNORE "\t\t'$i' => 1,\n";
}
}
print IGNORE "\t};\n";
chmod( 0777, "$tree/ignorebuilds.pl");
print "<h2><a href=showbuilds.cgi?tree=$treename>Build state Changed</a></h2>\n";
}
sub set_message {
$m = $form{'message'};
$m =~ s/\'/\\'/g;
open(MOD, ">$tree/mod.pl");
print MOD "\$message_of_day = \'$m\'\;\n1;";
close(MOD);
chmod( 0777, "$tree/mod.pl");
print "<h2><a href=showbuilds.cgi?tree=$tree>
Message Changed</a></h2>\n";
}

View File

@@ -0,0 +1,45 @@
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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 the Tinderbox build tool.
#
# 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.
1;
#
# Scan a line and see if it has an error
#
sub has_error {
$line =~ /fatal error/ # link error
|| $line =~ /Error / # C error
|| $line =~ /\[checkout aborted\]/ #cvs error
|| $line =~ /Couldn\'t find project file / # CW project error
;
}
sub has_warning {
$line =~ /^[A-Za-z0-9_]+\.[A-Za-z0-9]+\ line [0-9]+/ ;
}
sub has_errorline {
local( $line ) = @_;
if( $line =~ /^(([A-Za-z0-9_]+\.[A-Za-z0-9]+) line ([0-9]+))/ ){
$error_file = $1;
$error_file_ref = $2;
$error_line = $3;
$error_guess = 1;
return 1;
}
return 0;
}

View File

@@ -0,0 +1,59 @@
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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 the Tinderbox build tool.
#
# 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.
1;
#
# Scan a line and see if it has an error
#
sub has_error {
$line =~ /fatal error/ # link error
|| $line =~ /^C / # cvs merge conflict
|| $line =~ / Error: / # C error
|| $line =~ / error\([0-9]*\)\:/ # C error
|| ($line =~ /gmake/ && $line =~ / Error /)
|| $line =~ /\[checkout aborted\]/ #cvs error
|| $line =~ /\: cannot find module/ #cvs error
;
}
sub has_warning {
$line =~ /^[A-Za-z0-9_]+\.[A-Za-z0-9]+\:[0-9]+\:/
|| $line =~ /^\"[A-Za-z0-9_]+\.[A-Za-z0-9]+\"\, line [0-9]+\:/
;
}
sub has_errorline {
local( $line ) = @_;
if( $line =~ /^(([A-Za-z0-9_]+\.[A-Za-z0-9]+)\:([0-9]+)\:)/ ){
$error_file = $1;
$error_file_ref = $2;
$error_line = $3;
$error_guess = 1;
return 1;
}
if ( $line =~ /^(\"([A-Za-z0-9_]+\.[A-Za-z0-9]+)\"\, line ([0-9]+)\:)/ ){
$error_file = $1;
$error_file_ref = $2;
$error_line = $3;
$error_guess = 1;
return 1;
}
return 0;
}

View File

@@ -0,0 +1,73 @@
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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 the Tinderbox build tool.
#
# 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.
1;
#
# Scan a line and see if it has an error
#
sub has_error {
$line =~ /fatal error/ # link error
|| $line =~ / error / # C error
|| $line =~ /^C / # cvs merge conflict
|| $line =~ /error C/ # C error
|| $line =~ /Creating new precompiled header/ # Wastes time.
|| $line =~ /error:/ # java error
|| $line =~ /jmake.MakerFailedException:/ # java error
|| $line =~ /Unknown host / # cvs error
|| $line =~ /: build failed\;/ # nmake error
|| ($line =~ /gmake/ && $line =~ / Error /)
|| $line =~ /\[checkout aborted\]/ #cvs error
|| $line =~ /\: cannot find module/ #cvs error
;
}
sub has_warning {
$line =~ /: warning/ # link error
|| $line =~ / error / # C error
;
}
sub has_errorline {
local( $line ) = @_;
$error_file = ''; #'NS\CMD\WINFE\CXICON.cpp';
$error_line = 0;
if( $line =~ m@(ns([\\/][a-z0-9\._]+)*)@i ){
$error_file = $1;
$error_file_ref = lc $error_file;
$error_file_ref =~ s@\\@/@g;
$line =~ m/\(([0-9]+)\)/;
$error_line = $1;
return 1;
}
if( $line =~ m@(^([A-Za-z0-9_]+\.[A-Za-z])+\(([0-9]+)\))@ ){
$error_file = $1;
$error_file_ref = lc $2;
$error_line = $3;
$error_guess=1;
$error_file_ref =~ s@\\@/@g;
return 1;
}
return 0;
}

View File

@@ -0,0 +1,44 @@
This directory contains example Tinderbox client scripts. These scripts are
for illustration/documentation purposes only and are not maintained
regularly. Current scripts to build mozilla will live in an another spot
in the mozilla tree.
Three examples have been provided:
mozilla-windows.pl: perl script that drives mozilla tinderbox builds for Win32
mozilla-unix.pl : perl script that drives mozilla tinderbox builds for UNIX
build-moz-smoke.pl: perl script that drives mozilla tinderbox builds for UNIX,
and subsequently runs the executable returning a green tree only if
it does not crash.
These scripts show the basic elements of a Tinderbox client script. These
elements are:
1) Sending a start e-mail to the Tinderbox server, in the form of a formatted
mail message. Example:
tinderbox: tree: Mozilla
tinderbox: builddate: 900002087
tinderbox: status: building
tinderbox: build: IRIX 6.3 Depend
tinderbox: errorparser: unix
tinderbox: buildfamily: unix
2) Obtain a source tree by performing a cvs checkout.
3) Perform the build, saving the output to a log file.
4) Determine if the build was successful or failed. This could be done either
by checking for the presence of a binary, or being using error codes returned
from the compile command.
5) Send a completion message to Tinderbox, identifying build success or
failure. Example:
tinderbox: tree: Mozilla
tinderbox: builddate: 900002087
tinderbox: status: success
tinderbox: build: IRIX 6.3 Depend
tinderbox: errorparser: unix
tinderbox: buildfamily: unix

View File

@@ -0,0 +1,594 @@
#!/usr/bin/perl
require 5.000;
use Sys::Hostname;
use POSIX "sys_wait_h";
use Cwd;
$Version = "1.000";
sub InitVars {
# PLEASE FILL THIS IN WITH YOUR PROPER EMAIL ADDRESS
$BuildAdministrator = "$ENV{'USER'}\@$ENV{'HOST'}";
#Default values of cmdline opts
$BuildDepend = 1; #depend or clobber
$ReportStatus = 1; # Send results to server or not
$BuildOnce = 0; # Build once, don't send results to server
$BuildClassic = 0; # Build classic source
#relative path to binary
$BinaryName{'x'} = 'mozilla-export';
$BinaryName{'qt'} = 'qtmozilla-export';
$BinaryName{'gnome'} = 'gnuzilla-export';
$BinaryName{'webshell'} = '/webshell/tests/viewer/viewer';
# Set these to what makes sense for your system
$cpus = 1;
$Make = 'gmake'; # Must be gnu make
$mail = '/bin/mail';
$Autoconf = 'autoconf -l build/autoconf';
$CVS = 'cvs -z3';
$CVSCO = 'co -P';
# Set these proper values for your tinderbox server
$Tinderbox_server = 'tinderbox-daemon\@cvs-mirror.mozilla.org';
#$Tinderbox_server = 'external-tinderbox-incoming\@tinderbox.seawood.org';
# These shouldn't really need to be changed
$BuildSleep = 10; # Minimum wait period from start of build to start
# of next build in minutes
$BuildTree = 'SeaMonkey';
$BuildTag = '';
$BuildName = '';
$TopLevel = '.';
$Topsrcdir = 'mozilla';
$BuildObjName = '';
$BuildConfigDir = 'mozilla/config';
$ClobberStr = 'realclean';
$ConfigureEnvArgs = 'CFLAGS=-pipe CXXFLAGS=-pipe';
#$ConfigureEnvArgs = '';
$ConfigureArgs = "--with-nspr=/usr/local/nspr --cache-file=/dev/null --with-static-gtk --enable-editor";
$ConfigGuess = './build/autoconf/config.guess';
$Logfile = '${BuildDir}.log';
} #EndSub-InitVars
sub ConditionalArgs {
if ( $BuildClassic ) {
$FE = 'x';
$ConfigureArgs .= " --enable-fe=$FE";
# $BuildTree = 'raptor';
$BuildModule = 'Raptor';
$BuildTag = ''
if ($BuildTag eq '');
$TopLevel = "mozilla-classic";
} else {
# $BuildTree = 'raptor';
# $Toolkit = 'gtk';
$FE = 'webshell';
# $BuildModule = 'Raptor';
$BuildModule = 'SeaMonkeyAll';
# $ConfigureArgs .= " --enable-toolkit=$Toolkit";
}
$CVSCO .= " -r $BuildTag" if ( $BuildTag ne '');
}
sub SetupEnv {
umask(0);
$ENV{"CVSROOT"} = ':pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot';
$ENV{"LD_LIBRARY_PATH"} = '/usr/local/nspr:/builds/tinderbox/SeaMonkey/Linux_2.0.34_clobber/mozilla/obj-i586-pc-linux-gnu/dist/bin:/usr/lib/png:/usr/local/lib';
$ENV{"DISPLAY"} = 'crucible.mcom.com:0.0';
} #EndSub-SetupEnv
sub SetupPath {
my($Path);
$Path = $ENV{PATH};
print "Path before: $Path\n";
if ( $OS eq 'SunOS' ) {
$ENV{'PATH'} = '/usr/ccs/bin:' . $ENV{'PATH'};
}
$Path = $ENV{PATH};
print "Path After: $Path\n";
} #EndSub-SetupPath
##########################################################################
# NO USER CONFIGURABLE PIECES BEYOND THIS POINT #
##########################################################################
sub GetSystemInfo {
$OS = `uname -s`;
$OSVer = `uname -r`;
chop($OS, $OSVer);
if ( $OS eq 'AIX' ) {
$OSVer = `uname -v`;
chop($OSVer);
$OSVer = $OSVer . "." . `uname -r`;
chop($OSVer);
}
if ( $OS eq 'IRIX64' ) {
$OS = 'IRIX';
}
if ( $OS eq 'SCO_SV' ) {
$OS = 'SCOOS';
$OSVer = '5.0';
}
my $host, $myhost = hostname;
chomp($myhost);
($host, $junk) = split(/\./, $myhost);
$BuildName = "";
if ( "$host" ne "" ) {
$BuildName = $host . ' ';
}
$BuildName .= $OS . ' ' . $OSVer . ' ' . ($BuildDepend?'Depend':'Clobber');
$DirName = $OS . '_' . $OSVer . '_' . ($BuildDepend?'depend':'clobber');
$RealOS = $OS;
$RealOSVer = $OSVer;
if ( $OS eq 'HP-UX' ) {
$RealOSVer = substr($OSVer,0,4);
}
if ( $OS eq 'Linux' ) {
$RealOSVer = substr($OSVer,0,3);
}
if ($BuildClassic) {
$logfile = "${DirName}-classic.log";
} else {
$logfile = "${DirName}.log";
}
} #EndSub-GetSystemInfo
sub BuildIt {
my ($fe, @felist, $EarlyExit, $LastTime, $StartTimeStr);
mkdir("$DirName", 0777);
chdir("$DirName") || die "Couldn't enter $DirName";
$StartDir = getcwd();
$LastTime = 0;
print "Starting dir is : $StartDir\n";
$EarlyExit = 0;
while ( ! $EarlyExit ) {
chdir("$StartDir");
if ( time - $LastTime < (60 * $BuildSleep) ) {
$SleepTime = (60 * $BuildSleep) - (time - $LastTime);
print "\n\nSleeping $SleepTime seconds ...\n";
sleep($SleepTime);
}
$LastTime = time;
$StartTime = time - 60 * 10;
$StartTimeStr = &CVSTime($StartTime);
&StartBuild if ($ReportStatus);
$CurrentDir = getcwd();
if ( $CurrentDir ne $StartDir ) {
print "startdir: $StartDir, curdir $CurrentDir\n";
die "curdir != startdir";
}
$BuildDir = $CurrentDir;
unlink( "$logfile" );
print "opening $logfile\n";
open( LOG, ">$logfile" ) || print "can't open $?\n";
print LOG "current dir is -- $hostname:$CurrentDir\n";
print LOG "Build Administrator is $BuildAdministrator\n";
&PrintEnv;
$BuildStatus = 0;
mkdir($TopLevel, 0777);
chdir($TopLevel) || die "chdir($TopLevel): $!\n";
if ( $BuildClassic ) {
print"$CVS $CVSCO $BuildModule\n";
print LOG "$CVS $CVSCO $BuildModule\n";
open (PULL, "$CVS $CVSCO $BuildModule 2>&1 |") || die "open: $!\n";
} else {
# print "$CVS $CVSCO mozilla/nglayout.mk\n";
# print LOG "$CVS $CVSCO mozilla/nglayout.mk\n";
# open (PULL, "$CVS $CVSCO mozilla/nglayout.mk 2>&1 |") || die "open: $!\n";
print "$CVS $CVSCO mozilla/client.mk\n";
print LOG "$CVS $CVSCO mozilla/client.mk\n";
open (PULL, "$CVS $CVSCO mozilla/client.mk 2>&1 |") || die "open: $!\n";
}
while (<PULL>) {
print $_;
print LOG $_;
}
close(PULL);
# Move to topsrcdir
#chdir($Topsrcdir) || die "chdir($Topsrcdir): $!\n";
# Do a separate checkout with toplevel makefile
if (! $BuildClassic) {
# print LOG "$Make -f nglayout.mk pull_all CVSCO='$CVS $CVSCO'|\n";
# open (PULLALL, "$Make -f nglayout.mk pull_all CVSCO='$CVS $CVSCO' |\n");
print LOG "$Make -f mozilla/client.mk checkout CVSCO='$CVS $CVSCO'|\n";
open (PULLALL, "$Make -f mozilla/client.mk checkout CVSCO='$CVS $CVSCO' |\n");
while (<PULLALL>) {
print LOG $_;
print $_;
}
close(PULLALL);
}
chdir($Topsrcdir) || die "chdir($Topsrcdir): $!\n";
print LOG "$Autoconf\n";
open (AUTOCONF, "$Autoconf 2>&1 | ") || die "$Autoconf: $!\n";
while (<AUTOCONF>) {
print LOG $_;
print $_;
}
close(AUTOCONF);
print LOG "$ConfigGuess\n";
$BuildObjName = "obj-";
open (GETOBJ, "$ConfigGuess 2>&1 |") || die "$ConfigGuess: $!\n";
while (<GETOBJ>) {
print $_;
print LOG $_;
chomp($BuildObjName .= $_);
}
close (GETOBJ);
mkdir($BuildObjName, 0777);
chdir($BuildObjName) || die "chdir($BuildObjName): $!\n";
print LOG "$ConfigureEnvArgs ../configure $ConfigureArgs\n";
open (CONFIGURE, "$ConfigureEnvArgs ../configure $ConfigureArgs 2>&1 |") || die "../configure: $!\n";
while (<CONFIGURE>) {
print $_;
print LOG $_;
}
close(CONFIGURE);
# if we are building depend, rebuild dependencies
if ($BuildDepend) {
print LOG "$Make MAKE='$Make -j $cpus' depend 2>&1 |\n";
open ( MAKEDEPEND, "$Make MAKE='$Make -j $cpus' depend 2>&1 |\n");
while ( <MAKEDEPEND> ) {
print $_;
print LOG $_;
}
close (MAKEDEPEND);
system("rm -rf dist");
} else {
# Building clobber
print LOG "$Make MAKE='$Make -j $cpus' $ClobberStr 2>&1 |\n";
open( MAKECLOBBER, "$Make MAKE='$Make -j $cpus' $ClobberStr 2>&1 |");
while ( <MAKECLOBBER> ) {
print $_;
print LOG $_;
}
close( MAKECLOBBER );
}
@felist = split(/,/, $FE);
foreach $fe ( @felist ) {
if (&BinaryExists($fe)) {
print LOG "deleting existing binary\n";
&DeleteBinary($fe);
}
}
if ($BuildClassic) {
# Build the BE only
print LOG "$Make MAKE='$Make -j $cpus' MOZ_FE= 2>&1 |\n";
open( BEBUILD, "$Make MAKE='$Make -j $cpus' MOZ_FE= 2>&1 |");
while ( <BEBUILD> ) {
print $_;
print LOG $_;
}
close( BEBUILD );
foreach $fe ( @felist ) {
# Now build each front end
print LOG "$Make MAKE='$Make -j $cpus' -C cmd/${fe}fe 2>&1 |\n";
open(FEBUILD, "$Make MAKE='$Make -j $cpus' -C cmd/${fe}fe 2>&1 |\n");
while (<FEBUILD>) {
print $_;
print LOG $_;
}
close(FEBUILD);
}
} else {
print LOG "$Make MAKE='$Make -j $cpus' 2>&1 |\n";
open(BUILD, "$Make MAKE='$Make -j $cpus' 2>&1 |\n");
while (<BUILD>) {
print $_;
print LOG $_;
}
close(BUILD);
}
foreach $fe (@felist) {
if (&BinaryExists($fe)) {
print LOG "export binary exists, build successful. Testing...\n";
#return 0 if no problem, else 333 for a runtime error
$BuildStatus = &RunSmokeTest($fe);
}
else {
print LOG "export binary missing, build FAILED\n";
$BuildStatus = 666;
}
if ( $BuildStatus == 0 ) {
$BuildStatusStr = 'success';
}
elsif ( $BuildStatus == 333 ) {
$BuildStatusStr = 'testfailed';
}
else {
$BuildStatusStr = 'busted';
}
# replaced by above lines
# $BuildStatusStr = ( $BuildStatus ? 'busted' : 'success' );
print LOG "tinderbox: tree: $BuildTree\n";
print LOG "tinderbox: builddate: $StartTime\n";
print LOG "tinderbox: status: $BuildStatusStr\n";
print LOG "tinderbox: build: $BuildName $fe\n";
print LOG "tinderbox: errorparser: unix\n";
print LOG "tinderbox: buildfamily: unix\n";
print LOG "tinderbox: END\n";
}
close(LOG);
chdir("$StartDir");
# this fun line added on 2/5/98. do not remove. Translated to english,
# that's "take any line longer than 1000 characters, and split it into less
# than 1000 char lines. If any of the resulting lines is
# a dot on a line by itself, replace that with a blank line."
# This is to prevent cases where a <cr>.<cr> occurs in the log file. Sendmail
# interprets that as the end of the mail, and truncates the log before
# it gets to Tinderbox. (terry weismann, chris yeh)
#
# This was replaced by a perl 'port' of the above, writen by
# preed@netscape.com; good things: no need for system() call, and now it's
# all in perl, so we don't have to do OS checking like before.
open(LOG, "$logfile") || die "Couldn't open logfile: $!\n";
open(OUTLOG, ">${logfile}.last") || die "Couldn't open logfile: $!\n";
while (<LOG>) {
$q = 0;
for (;;) {
$val = $q * 1000;
$Output = substr($_, $val, 1000);
last if $Output eq undef;
$Output =~ s/^\.$//g;
$Output =~ s/\n//g;
print OUTLOG "$Output\n";
$q++;
} #EndFor
} #EndWhile
close(LOG);
close(OUTLOG);
system( "$mail $Tinderbox_server < ${logfile}.last" )
if ($ReportStatus );
unlink("$logfile");
# if this is a test run, set early_exit to 0.
#This mean one loop of execution
$EarlyExit++ if ($BuildOnce);
}
} #EndSub-BuildIt
sub CVSTime {
my($StartTimeArg) = @_;
my($RetTime, $StartTimeArg, $sec, $minute, $hour, $mday, $mon, $year);
($sec,$minute,$hour,$mday,$mon,$year) = localtime($StartTimeArg);
$mon++; # month is 0 based.
sprintf("%02d/%02d/%02d %02d:%02d:00", $mon,$mday,$year,$hour,$minute );
}
sub StartBuild {
my($fe, @felist);
@felist = split(/,/, $FE);
# die "SERVER: " . $Tinderbox_server . "\n";
open( LOG, "|$mail $Tinderbox_server" );
foreach $fe ( @felist ) {
print LOG "\n";
print LOG "tinderbox: tree: $BuildTree\n";
print LOG "tinderbox: builddate: $StartTime\n";
print LOG "tinderbox: status: building\n";
print LOG "tinderbox: build: $BuildName $fe\n";
print LOG "tinderbox: errorparser: unix\n";
print LOG "tinderbox: buildfamily: unix\n";
print LOG "tinderbox: END\n";
print LOG "\n";
}
close( LOG );
}
# check for the existence of the binary
sub BinaryExists {
my($fe) = @_;
my($Binname);
$fe = 'x' if (!defined($fe));
if ($BuildClassic) {
$BinName = $BuildDir . '/' . $TopLevel . '/' . $Topsrcdir . '/'. $BuildObjName . "/cmd/${fe}fe/" . $BinaryName{"$fe"};
} else {
$BinName = $BuildDir . '/' . $TopLevel . '/' . $Topsrcdir . '/' . $BuildObjName . $BinaryName{"$fe"};
}
print LOG $BinName . "\n";
if ((-e $BinName) && (-x $BinName) && (-s $BinName)) {
1;
}
else {
0;
}
}
sub DeleteBinary {
my($fe) = @_;
my($BinName);
$fe = 'x' if (!defined($fe));
if ($BuildClassic) {
$BinName = $BuildDir . '/' . $TopLevel . '/' . $Topsrcdir . '/' . $BuildObjName . "/cmd/${fe}fe/" . $BinaryName{"$fe"};
} else {
$BinName = $BuildDir . '/' . $TopLevel . '/' . $Topsrcdir . '/' . $BuildObjName . $BinaryName{"$fe"};
}
print LOG "unlinking $BinName\n";
unlink ($BinName) || print LOG "unlinking $BinName failed\n";
}
sub ParseArgs {
my($i, $manArg);
if( @ARGV == 0 ) {
&PrintUsage;
}
$i = 0;
$manArg = 0;
while( $i < @ARGV ) {
if ($ARGV[$i] eq '--depend') {
$BuildDepend = 1;
$manArg++;
}
elsif ($ARGV[$i] eq '--clobber') {
$BuildDepend = 0;
$manArg++;
}
elsif ( $ARGV[$i] eq '--once' ) {
$BuildOnce = 1;
#$ReportStatus = 0;
}
elsif ($ARGV[$i] eq '--classic') {
$BuildClassic = 1;
}
elsif ($ARGV[$i] eq '--noreport') {
$ReportStatus = 0;
}
elsif ($ARGV[$i] eq '--version' || $ARGV[$i] eq '-v') {
die "$0: version $Version\n";
}
elsif ( $ARGV[$i] eq '-tag' ) {
$i++;
$BuildTag = $ARGV[$i];
if ( $BuildTag eq '' || $BuildTag eq '-t') {
&PrintUsage;
}
}
elsif ( $ARGV[$i] eq '-t' ) {
$i++;
$BuildTree = $ARGV[$i];
if ( $BuildTree eq '' ) {
&PrintUsage;
}
} else {
&PrintUsage;
}
$i++;
} #EndWhile
if ( $BuildTree =~ /^\s+$/i ) {
&PrintUsage;
}
if ($BuildDepend eq undef) {
&PrintUsage;
}
&PrintUsage if (! $manArg );
} #EndSub-ParseArgs
sub PrintUsage {
die "usage: $0 [--depend | --clobber] [-v | --version ] [--once --classic --noreport -tag TREETAG -t TREENAME ]\n";
}
sub PrintEnv {
my ($key);
foreach $key (keys %ENV) {
print LOG "$key = $ENV{$key}\n";
print "$key = $ENV{$key}\n";
}
} #EndSub-PrintEnv
sub RunSmokeTest {
my($fe) = @_;
my($Binary);
$fe = 'x' if (!defined($fe));
$Binary = $BuildDir . '/' . $TopLevel . '/' . $Topsrcdir . '/' . $BuildObjName . $BinaryName{"$fe"};
print LOG $BinName . "\n";
$waittime = 30;
$pid = fork;
exec $Binary if !$pid;
# parent - wait $waittime seconds then check on child
sleep $waittime;
$status = waitpid $pid, WNOHANG();
if ($status != 0) {
print LOG "$BinName has crashed or quit. Turn the tree orange now.\n";
return 333;
}
print LOG "Success! $BinName is still running. Killing..\n";
# try to kill 3 times, then try a kill -9
for ($i=0 ; $i<3 ; $i++) {
kill 'TERM',$pid,;
# give it 3 seconds to actually die
sleep 3;
$status = waitpid $pid, WNOHANG();
last if ($status != 0);
}
return 0;
} #EndSub-RunSmokeTest
# Main function
&InitVars;
&ParseArgs;
&ConditionalArgs;
&GetSystemInfo;
&SetupEnv;
&SetupPath;
&BuildIt;
1;

View File

@@ -0,0 +1,63 @@
#!/usr/bin/perl
sub InitVars {
#initialize variables
#$binary_name = '/netscape-export';
$BinaryName = '/mozilla-export';
$BuildDepend = 1; #depend or clobber
$BuildTree = '';
$BuildTag = '';
$BuildName = '';
#$BuildContinue = 0;
$TopLevel = 'mozilla/';
$BuildObjName = '';
$BuildSleep = 10;
$BuildUnixclasses = 0;
$EarlyExit = 1;
$BuildStartDir = 'ns/';
$BuildConfigDir = 'mozilla/config';
} #EndSub-InitVars
sub SetupEnv {
umask(0);
$ENV{'MOZILLA_CLIENT'} = 1;
$ENV{'NETSCAPE_HIERARCHY'} = 1;
$ENV{'BUILD_OFFICIAL'} = 1;
$ENV{'NSPR20'} = 1;
$ENV{'AWT_11'} = 1;
$ENV{'MOZ_SECURITY'} = 1;
$ENV{"CVSROOT"} = '/m/src';
$ENV{"MAKE"} = 'gmake -e';
$ENV{'MOZ_MEDIUM'} = 1;
$ENV{'NO_MDUPDATE'} = 1;
$ENV{'EDITOR'} = 1;
} #EndSub-SetupEnv
sub SetupPath {
my($Path);
$Path = $ENV{PATH};
print "Path before: $Path\n";
$ENV{'PATH'} = '/tools/ns/bin:/tools/contrib/bin:/usr/local/bin:/usr/sbin:/usr/bsd:/sbin:/usr/bin:/bin:/usr/bin/X11:/usr/etc:/usr/hosts:/usr/ucb:';
# This won't work on x86 or sunos4 systems....
if ( $OS eq 'SunOS' ) {
$ENV{'PATH'} = '/usr/ccs/bin:/tools/ns/soft/gcc-2.6.3/run/default/sparc_sun_solaris2.4/bin:' . $ENV{'PATH'};
$ENV{'NO_MDUPDATE'} = 1;
}
if ( $OS eq 'AIX' ) {
$ENV{'PATH'} = $ENV{'PATH'} . '/usr/lpp/xlC/bin:/usr/local-aix/bin:';
}
$Path = $ENV{PATH};
print "Path After: $Path\n";
} #EndSub-SetupPath
1;

View File

@@ -0,0 +1,548 @@
#!/usr/bin/perl
require 5.000;
use Sys::Hostname;
use Cwd;
$Version = "1.000";
sub InitVars {
# PLEASE FILL THIS IN WITH YOUR PROPER EMAIL ADDRESS
$BuildAdministrator = "$ENV{'USER'}\@$ENV{'HOST'}";
#Default values of cmdline opts
$BuildDepend = 1; #depend or clobber
$ReportStatus = 1; # Send results to server or not
$BuildOnce = 0; # Build once, don't send results to server
$BuildClassic = 0; # Build classic source
#relative path to binary
$BinaryName{'x'} = 'mozilla-export';
$BinaryName{'qt'} = 'qtmozilla-export';
$BinaryName{'gnome'} = 'gnuzilla-export';
$BinaryName{'webshell'} = '/webshell/tests/viewer/viewer';
$BinaryName{'xpfe'} = '/xpfe/xpviewer/src/xpviewer';
# Set these to what makes sense for your system
$cpus = 1;
$Make = 'gmake'; # Must be gnu make
$mail = '/bin/mail';
$Autoconf = 'autoconf -l build/autoconf';
$CVS = 'cvs -z3';
$CVSCO = 'co -P';
# Set these proper values for your tinderbox server
$Tinderbox_server = 'tinderbox-daemon\@warp.mcom.com';
#$Tinderbox_server = 'external-tinderbox-incoming\@tinderbox.seawood.org';
# These shouldn't really need to be changed
$BuildSleep = 10; # Minimum wait period from start of build to start
# of next build in minutes
$BuildTree = 'raptor';
$BuildTag = '';
$BuildName = '';
$TopLevel = '.';
$Topsrcdir = 'mozilla';
$BuildObjName = '';
$BuildConfigDir = 'mozilla/config';
$ClobberStr = 'realclean';
$ConfigureEnvArgs = 'CFLAGS=-pipe CXXFLAGS=-pipe';
#$ConfigureEnvArgs = '';
$ConfigureArgs = "--cache-file=/dev/null";
$ConfigGuess = './build/autoconf/config.guess';
$Logfile = '${BuildDir}.log';
} #EndSub-InitVars
sub ConditionalArgs {
if ( $BuildClassic ) {
$FE = 'x';
$ConfigureArgs .= " --enable-fe=$FE";
$BuildTree = 'raptor';
$BuildModule = 'Raptor';
$BuildTag = ''
if ($BuildTag eq '');
$TopLevel = "mozilla-classic";
} else {
$BuildTree = 'raptor';
# $Toolkit = 'gtk';
$FE = 'webshell,xpfe';
$BuildModule = 'Raptor';
# $ConfigureArgs .= " --enable-toolkit=$Toolkit";
}
$CVSCO .= " -r $BuildTag" if ( $BuildTag ne '');
}
sub SetupEnv {
umask(0);
$ENV{"CVSROOT"} = ':pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot';
} #EndSub-SetupEnv
sub SetupPath {
my($Path);
$Path = $ENV{PATH};
print "Path before: $Path\n";
if ( $OS eq 'SunOS' ) {
$ENV{'PATH'} = '/usr/ccs/bin:' . $ENV{'PATH'};
}
$Path = $ENV{PATH};
print "Path After: $Path\n";
} #EndSub-SetupPath
##########################################################################
# NO USER CONFIGURABLE PIECES BEYOND THIS POINT #
##########################################################################
sub GetSystemInfo {
$OS = `uname -s`;
$OSVer = `uname -r`;
chop($OS, $OSVer);
if ( $OS eq 'AIX' ) {
$OSVer = `uname -v`;
chop($OSVer);
$OSVer = $OSVer . "." . `uname -r`;
chop($OSVer);
}
if ( $OS eq 'IRIX64' ) {
$OS = 'IRIX';
}
if ( $OS eq 'SCO_SV' ) {
$OS = 'SCOOS';
$OSVer = '5.0';
}
my $host, $myhost = hostname;
chomp($myhost);
($host, $junk) = split(/\./, $myhost);
$BuildName = "";
if ( "$host" ne "" ) {
$BuildName = $host . ' ';
}
$BuildName .= $OS . ' ' . $OSVer . ' ' . ($BuildDepend?'Depend':'Clobber');
$DirName = $OS . '_' . $OSVer . '_' . ($BuildDepend?'depend':'clobber');
$RealOS = $OS;
$RealOSVer = $OSVer;
if ( $OS eq 'HP-UX' ) {
$RealOSVer = substr($OSVer,0,4);
}
if ( $OS eq 'Linux' ) {
$RealOSVer = substr($OSVer,0,3);
}
if ($BuildClassic) {
$logfile = "${DirName}-classic.log";
} else {
$logfile = "${DirName}.log";
}
} #EndSub-GetSystemInfo
sub BuildIt {
my ($fe, @felist, $EarlyExit, $LastTime, $StartTimeStr);
mkdir("$DirName", 0777);
chdir("$DirName") || die "Couldn't enter $DirName";
$StartDir = getcwd();
$LastTime = 0;
print "Starting dir is : $StartDir\n";
$EarlyExit = 0;
while ( ! $EarlyExit ) {
chdir("$StartDir");
if ( time - $LastTime < (60 * $BuildSleep) ) {
$SleepTime = (60 * $BuildSleep) - (time - $LastTime);
print "\n\nSleeping $SleepTime seconds ...\n";
sleep($SleepTime);
}
$LastTime = time;
$StartTime = time - 60 * 10;
$StartTimeStr = &CVSTime($StartTime);
&StartBuild if ($ReportStatus);
$CurrentDir = getcwd();
if ( $CurrentDir ne $StartDir ) {
print "startdir: $StartDir, curdir $CurrentDir\n";
die "curdir != startdir";
}
$BuildDir = $CurrentDir;
unlink( "$logfile" );
print "opening $logfile\n";
open( LOG, ">$logfile" ) || print "can't open $?\n";
print LOG "current dir is -- $hostname:$CurrentDir\n";
print LOG "Build Administrator is $BuildAdministrator\n";
&PrintEnv;
$BuildStatus = 0;
mkdir($TopLevel, 0777);
chdir($TopLevel) || die "chdir($TopLevel): $!\n";
if ( $BuildClassic ) {
print"$CVS $CVSCO $BuildModule\n";
print LOG "$CVS $CVSCO $BuildModule\n";
open (PULL, "$CVS $CVSCO $BuildModule 2>&1 |") || die "open: $!\n";
} else {
# print "$CVS $CVSCO mozilla/nglayout.mk\n";
# print LOG "$CVS $CVSCO mozilla/nglayout.mk\n";
# open (PULL, "$CVS $CVSCO mozilla/nglayout.mk 2>&1 |") || die "open: $!\n";
print "$CVS $CVSCO mozilla/client.mk\n";
print LOG "$CVS $CVSCO mozilla/client.mk\n";
open (PULL, "$CVS $CVSCO mozilla/client.mk 2>&1 |") || die "open: $!\n";
}
while (<PULL>) {
print $_;
print LOG $_;
}
close(PULL);
# Move to topsrcdir
#chdir($Topsrcdir) || die "chdir($Topsrcdir): $!\n";
# Do a separate checkout with toplevel makefile
if (! $BuildClassic) {
# print LOG "$Make -f nglayout.mk pull_all CVSCO='$CVS $CVSCO'|\n";
# open (PULLALL, "$Make -f nglayout.mk pull_all CVSCO='$CVS $CVSCO' |\n");
print LOG "$Make -f mozilla/client.mk checkout CVSCO='$CVS $CVSCO'|\n";
open (PULLALL, "$Make -f mozilla/client.mk checkout CVSCO='$CVS $CVSCO' |\n");
while (<PULLALL>) {
print LOG $_;
print $_;
}
close(PULLALL);
}
chdir($Topsrcdir) || die "chdir($Topsrcdir): $!\n";
print LOG "$Autoconf\n";
open (AUTOCONF, "$Autoconf 2>&1 | ") || die "$Autoconf: $!\n";
while (<AUTOCONF>) {
print LOG $_;
print $_;
}
close(AUTOCONF);
print LOG "$ConfigGuess\n";
$BuildObjName = "obj-";
open (GETOBJ, "$ConfigGuess 2>&1 |") || die "$ConfigGuess: $!\n";
while (<GETOBJ>) {
print $_;
print LOG $_;
chomp($BuildObjName .= $_);
}
close (GETOBJ);
mkdir($BuildObjName, 0777);
chdir($BuildObjName) || die "chdir($BuildObjName): $!\n";
print LOG "$ConfigureEnvArgs ../configure $ConfigureArgs\n";
open (CONFIGURE, "$ConfigureEnvArgs ../configure $ConfigureArgs 2>&1 |") || die "../configure: $!\n";
while (<CONFIGURE>) {
print $_;
print LOG $_;
}
close(CONFIGURE);
# if we are building depend, rebuild dependencies
if ($BuildDepend) {
print LOG "$Make MAKE='$Make -j $cpus' depend 2>&1 |\n";
open ( MAKEDEPEND, "$Make MAKE='$Make -j $cpus' depend 2>&1 |\n");
while ( <MAKEDEPEND> ) {
print $_;
print LOG $_;
}
close (MAKEDEPEND);
system("rm -rf dist");
} else {
# Building clobber
print LOG "$Make MAKE='$Make -j $cpus' $ClobberStr 2>&1 |\n";
open( MAKECLOBBER, "$Make MAKE='$Make -j $cpus' $ClobberStr 2>&1 |");
while ( <MAKECLOBBER> ) {
print $_;
print LOG $_;
}
close( MAKECLOBBER );
}
@felist = split(/,/, $FE);
foreach $fe ( @felist ) {
if (&BinaryExists($fe)) {
print LOG "deleting existing binary\n";
&DeleteBinary($fe);
}
}
if ($BuildClassic) {
# Build the BE only
print LOG "$Make MAKE='$Make -j $cpus' MOZ_FE= 2>&1 |\n";
open( BEBUILD, "$Make MAKE='$Make -j $cpus' MOZ_FE= 2>&1 |");
while ( <BEBUILD> ) {
print $_;
print LOG $_;
}
close( BEBUILD );
foreach $fe ( @felist ) {
# Now build each front end
print LOG "$Make MAKE='$Make -j $cpus' -C cmd/${fe}fe 2>&1 |\n";
open(FEBUILD, "$Make MAKE='$Make -j $cpus' -C cmd/${fe}fe 2>&1 |\n");
while (<FEBUILD>) {
print $_;
print LOG $_;
}
close(FEBUILD);
}
} else {
print LOG "$Make MAKE='$Make -j $cpus' 2>&1 |\n";
open(BUILD, "$Make MAKE='$Make -j $cpus' 2>&1 |\n");
while (<BUILD>) {
print $_;
print LOG $_;
}
close(BUILD);
}
foreach $fe (@felist) {
if (&BinaryExists($fe)) {
print LOG "export binary exists, build SUCCESSFUL!\n";
$BuildStatus = 0;
}
else {
print LOG "export binary missing, build FAILED\n";
$BuildStatus = 666;
}
print LOG "\nBuild Status = $BuildStatus\n";
$BuildStatusStr = ( $BuildStatus ? 'busted' : 'success' );
print LOG "tinderbox: tree: $BuildTree\n";
print LOG "tinderbox: builddate: $StartTime\n";
print LOG "tinderbox: status: $BuildStatusStr\n";
print LOG "tinderbox: build: $BuildName $fe\n";
print LOG "tinderbox: errorparser: unix\n";
print LOG "tinderbox: buildfamily: unix\n";
print LOG "tinderbox: END\n";
}
close(LOG);
chdir("$StartDir");
# this fun line added on 2/5/98. do not remove. Translated to english,
# that's "take any line longer than 1000 characters, and split it into less
# than 1000 char lines. If any of the resulting lines is
# a dot on a line by itself, replace that with a blank line."
# This is to prevent cases where a <cr>.<cr> occurs in the log file. Sendmail
# interprets that as the end of the mail, and truncates the log before
# it gets to Tinderbox. (terry weismann, chris yeh)
#
# This was replaced by a perl 'port' of the above, writen by
# preed@netscape.com; good things: no need for system() call, and now it's
# all in perl, so we don't have to do OS checking like before.
open(LOG, "$logfile") || die "Couldn't open logfile: $!\n";
open(OUTLOG, ">${logfile}.last") || die "Couldn't open logfile: $!\n";
while (<LOG>) {
$q = 0;
for (;;) {
$val = $q * 1000;
$Output = substr($_, $val, 1000);
last if $Output eq undef;
$Output =~ s/^\.$//g;
$Output =~ s/\n//g;
print OUTLOG "$Output\n";
$q++;
} #EndFor
} #EndWhile
close(LOG);
close(OUTLOG);
system( "$mail $Tinderbox_server < ${logfile}.last" )
if ($ReportStatus );
unlink("$logfile");
# if this is a test run, set early_exit to 0.
#This mean one loop of execution
$EarlyExit++ if ($BuildOnce);
}
} #EndSub-BuildIt
sub CVSTime {
my($StartTimeArg) = @_;
my($RetTime, $StartTimeArg, $sec, $minute, $hour, $mday, $mon, $year);
($sec,$minute,$hour,$mday,$mon,$year) = localtime($StartTimeArg);
$mon++; # month is 0 based.
sprintf("%02d/%02d/%02d %02d:%02d:00", $mon,$mday,$year,$hour,$minute );
}
sub StartBuild {
my($fe, @felist);
@felist = split(/,/, $FE);
# die "SERVER: " . $Tinderbox_server . "\n";
open( LOG, "|$mail $Tinderbox_server" );
foreach $fe ( @felist ) {
print LOG "\n";
print LOG "tinderbox: tree: $BuildTree\n";
print LOG "tinderbox: builddate: $StartTime\n";
print LOG "tinderbox: status: building\n";
print LOG "tinderbox: build: $BuildName $fe\n";
print LOG "tinderbox: errorparser: unix\n";
print LOG "tinderbox: buildfamily: unix\n";
print LOG "tinderbox: END\n";
print LOG "\n";
}
close( LOG );
}
# check for the existence of the binary
sub BinaryExists {
my($fe) = @_;
my($Binname);
$fe = 'x' if (!defined($fe));
if ($BuildClassic) {
$BinName = $BuildDir . '/' . $TopLevel . '/' . $Topsrcdir . '/'. $BuildObjName . "/cmd/${fe}fe/" . $BinaryName{"$fe"};
} else {
$BinName = $BuildDir . '/' . $TopLevel . '/' . $Topsrcdir . '/' . $BuildObjName . $BinaryName{"$fe"};
}
print LOG $BinName . "\n";
if ((-e $BinName) && (-x $BinName) && (-s $BinName)) {
1;
}
else {
0;
}
}
sub DeleteBinary {
my($fe) = @_;
my($BinName);
$fe = 'x' if (!defined($fe));
if ($BuildClassic) {
$BinName = $BuildDir . '/' . $TopLevel . '/' . $Topsrcdir . '/' . $BuildObjName . "/cmd/${fe}fe/" . $BinaryName{"$fe"};
} else {
$BinName = $BuildDir . '/' . $TopLevel . '/' . $Topsrcdir . '/' . $BuildObjName . $BinaryName{"$fe"};
}
print LOG "unlinking $BinName\n";
unlink ($BinName) || print LOG "unlinking $BinName failed\n";
}
sub ParseArgs {
my($i, $manArg);
if( @ARGV == 0 ) {
&PrintUsage;
}
$i = 0;
$manArg = 0;
while( $i < @ARGV ) {
if ($ARGV[$i] eq '--depend') {
$BuildDepend = 1;
$manArg++;
}
elsif ($ARGV[$i] eq '--clobber') {
$BuildDepend = 0;
$manArg++;
}
elsif ( $ARGV[$i] eq '--once' ) {
$BuildOnce = 1;
#$ReportStatus = 0;
}
elsif ($ARGV[$i] eq '--classic') {
$BuildClassic = 1;
}
elsif ($ARGV[$i] eq '--noreport') {
$ReportStatus = 0;
}
elsif ($ARGV[$i] eq '--version' || $ARGV[$i] eq '-v') {
die "$0: version $Version\n";
}
elsif ( $ARGV[$i] eq '-tag' ) {
$i++;
$BuildTag = $ARGV[$i];
if ( $BuildTag eq '' || $BuildTag eq '-t') {
&PrintUsage;
}
}
elsif ( $ARGV[$i] eq '-t' ) {
$i++;
$BuildTree = $ARGV[$i];
if ( $BuildTree eq '' ) {
&PrintUsage;
}
} else {
&PrintUsage;
}
$i++;
} #EndWhile
if ( $BuildTree =~ /^\s+$/i ) {
&PrintUsage;
}
if ($BuildDepend eq undef) {
&PrintUsage;
}
&PrintUsage if (! $manArg );
} #EndSub-ParseArgs
sub PrintUsage {
die "usage: $0 [--depend | --clobber] [-v | --version ] [--once --classic --noreport -tag TREETAG -t TREENAME ]\n";
}
sub PrintEnv {
my ($key);
foreach $key (keys %ENV) {
print LOG "$key = $ENV{$key}\n";
print "$key = $ENV{$key}\n";
}
} #EndSub-PrintEnv
# Main function
&InitVars;
&ParseArgs;
&ConditionalArgs;
&GetSystemInfo;
&SetupEnv;
&SetupPath;
&BuildIt;
1;

View File

@@ -0,0 +1,416 @@
#!c:/nstools/bin/perl5
use Cwd;
$build_depend=1; #depend or clobber
$build_tree = '';
$build_tag = '';
$build_name = '';
$build_continue = 0;
$build_sleep=10;
$no32 = 0;
$no16 = 0;
$original_path = $ENV{'PATH'};
$early_exit = 1;
$doawt11 = 0;
$do_clobber = '';
$client_param = 'pull_and_build_all';
&parse_args;
if( $build_test ){
$build_sleep=1;
}
$dirname = ($build_depend?'dep':'clob');
$logfile = "${dirname}.log";
if( $build_depend ){
$clobber_str = 'depend';
}
else {
$clobber_str = 'clobber_all';
}
mkdir("$dirname", 0777);
chdir("$dirname") || die "couldn't cd to $dirname";
$start_dir = cwd;
$last_time = 0;
print "starting dir is :$start_dir\n";
while( $early_exit ){
chdir("$start_dir");
if( time - $last_time < (60 * $build_sleep) ){
$sleep_time = (60 * $build_sleep) - (time - $last_time);
print "\n\nSleeping $sleep_time seconds ...\n";
sleep( $sleep_time );
}
$last_time = time;
$start_time = time-60*10;
$start_time_str = &cvs_time( $start_time );
# call setup_env here in the loop, to update MOZ_DATE with each pass.
# setup_env uses start_time_str for MOZ_DATE.
&setup_env;
$cur_dir = cwd;
if( $cur_dir ne $start_dir ){
print "startdir: $start_dir, curdir $cur_dir\n";
die "curdir != startdir";
}
# build 32-bit with AWT_11=1
&setup32("1");
if( !$no32 ){
if( !$noawt11 ){
&do_build(1,$do_clobber);
}
}
if ($build_test) {
$early_exit = 0; # stops this while loop after one pass.
}
if( !$no16 ){
# build 32-bit with AWT_11=0
# necessary before building 16-bit because 16-bit cannot use AWT 1.1 classes
&setup32("0");
if( !$no32 ){
if( !$noawt11 ){
&do_build(0,'');
} else {
&do_build(1,$do_clobber);
}
}
&setup16;
# strip_conf fails to strip any variables from the real environemnt
# &strip_config;
&do_build(0,'');
# &restore_config;
}
}
sub copy_win16_dist {
system 'xcopy w:\ y:\ns\dist /S /E /F';
print "COPYCOPYCOPY\n";
}
sub build_NSPR20_Win16 {
&start_build;
unlink( "${logname}.last" );
rename( "${logname}","${logname}.last");
print "opening ${logname}\n";
open( LOG, ">${logname}" ) || print "can't open $?\n";
print LOG "current dir is :$cur_dir\n";
&print_env;
chdir("$moz_src/ns/nspr20") || die "couldn't chdir to '$moz_src/ns/nspr20'";
print LOG "gmake |\n";
open( BUILDNSPR, "gmake 2>&1 |") || print "couldn't execute gmake\n";;
while( <BUILDNSPR> ) {
print $_;
print LOG $_;
}
close ( BUILDNSPR );
close( LOG );
}
sub setup32 {
local ($awt) = @_;
$ENV{"MOZ_BITS"} = '32';
$ENV{"AWT_11"} = $awt;
$doawt11 = $awt;
$ENV{"INCLUDE"} = "$msdev\\include;$msdev\\mfc\\include";
$ENV{"LIB"} = "$msdev\\lib;$msdev\\mfc\\lib";
$ENV{"PATH"} = $original_path . ";$msdev\\bin";
$ENV{"OS_TARGET"} = 'WIN95';
$moz_src = $ENV{'MOZ_SRC'} = $start_dir;
$build_name = 'Win32 ' . ($build_depend?'Depend':'Clobber');
$do_clobber = $clobber_str;
$logname = "win32.log";
}
sub setup16 {
$moz_src = $ENV{'MOZ_SRC'} = $start_dir;
$ENV{"MOZ_BITS"} = '16';
# perl 5 is fucked up. you MUST set AWT_11=0. deleting the environment
# variable doesn't work. it's removed from the environment entry, but
# is still defined as true for a build.
$ENV{"AWT_11"} = '0';
$moz_src = $ENV{'MOZ_SRC'} = "$src_16_drive";
$ENV{"OS_TARGET"} = 'WIN16';
$msvc_inc = "$moz_src\\ns\\msvc15\\include;$moz_src\\ns\\msvc15\\mfc\\include";
$msvc_lib = "$msvc\\lib;$msvc\\mfc\\lib";
$msvcpath = "$msvc\\bin;c:\\nstools\\bin;c:\\WINNT40;c:\\WINNT40\\system32;c:\\utils";
$ENV{"MSVC_INC"} = $msvc_inc;
$ENV{"MSVC_LIB"} = $msvc_lib;
$ENV{"MSVCPATH"} = $msvcpath;
$ENV{"INCLUDE"} = $msvc_inc;
$ENV{"LIB"} = $msvc_lib;
$ENV{"PATH"} = $msvcpath;
$watcom = $ENV{"WATCOM"} = "C:\\WATCOM";
$ENV{"EDPATH"} = "$watcom\\EDDAT";
$ENV{"WATC_INC"} = "$watcom\\h;$watcom\\h\win;$msvc_inc";
$ENV{"WATC_LIB"} = $msvc_lib;
$ENV{"WATCPATH"} = "$watcom\\BINNT;$watcom\\BINW;c:\\nstools\\bin";
$build_name = 'Win16 ' . ($build_depend?'Depend':'Clobber');
$do_clobber = $clobber_str;
$logname = "win16.log";
system "subst l: /d";
system "subst r: /d";
system "subst $src_16_drive /d";
system "subst $src_16_drive $start_dir";
system "subst r: $src_16_drive\\ns\\netsite\\ldap\\libraries\\msdos\\winsock";
system "subst l: $src_16_drive\\ns\\netsite";
}
sub do_build {
local ($pull, $do_clobber) = @_;
&start_build;
print "opening ${logname}\n";
open( LOG, ">${logname}" ) || print "can't open $?\n";
print LOG "current dir is :$cur_dir\n";
&print_env;
$build_status = 0;
if( $pull ){
if ( $build_tag eq '' ){
print LOG "cvs co -D\"$start_time_str\" mozilla/client.mak 2>&1 |\n";
open( PULL, "cvs co -D\"$start_time_str\" mozilla/client.mak 2>&1 |") || print "couldn't execute cvs\n";;
} else{
print LOG "cvs co -r $build_tag mozilla/client.mak 2>&1 |\n";
open( PULL, "cvs co -r $build_tag mozilla/client.mak 2>&1 |") || print "couldn't execute cvs\n";;
}
# tee the output
while( <PULL> ){
print $_;
print LOG $_;
}
close( PULL );
$build_status = $?;
}
chdir("$moz_src/mozilla") || die "couldn't chdir to '$moz_src/mozilla'";
if( $do_clobber ne '' ){
print LOG "nmake -f client.mak $do_clobber |\n";
print "nmake -f client.mak $do_clobber |\n";
open( PULL, "nmake -f client.mak $do_clobber 2>&1 |") || print "couldn't execute nmake\n";;
# tee the output
while( <PULL> ){
print $_;
print LOG $_;
}
close( PULL );
}
if (!$pull) {
$client_param = 'build_all';
}
else {
$client_param = 'pull_and_build_all';
}
if (!$doawt11) {
$client_param = 'build_dist';
}
print LOG "nmake -f client.mak $client_param 2>&1 |\n";
open( BUILD, "nmake -f client.mak $client_param 2>&1 |");
# tee the output
while( <BUILD> ){
print $_;
print LOG $_;
}
close( BUILD );
$build_status |= $?;
$build_status_str = ( $build_status ? 'busted' : 'success' );
print LOG "tinderbox: tree: $build_tree\n";
print LOG "tinderbox: builddate: $start_time\n";
print LOG "tinderbox: status: $build_status_str\n";
print LOG "tinderbox: build: $build_name\n";
print LOG "tinderbox: errorparser: windows\n";
print LOG "tinderbox: buildfamily: windows\n";
close( LOG );
chdir("$start_dir");
system( "$nstools\\bin\\blat ${logname} -t tinderbox-daemon\@warp" );
}
sub cvs_time {
local( $ret_time );
($sec,$minute,$hour,$mday,$mon,$year) = localtime( $_[0] );
$mon++; # month is 0 based.
sprintf("%02d/%02d/%02d %02d:%02d:00",
$mon,$mday,$year,$hour,$minute );
}
sub start_build {
open( LOG, ">>logfile" );
print LOG "\n";
print LOG "tinderbox: tree: $build_tree\n";
print LOG "tinderbox: builddate: $start_time\n";
print LOG "tinderbox: status: building\n";
print LOG "tinderbox: build: $build_name\n";
print LOG "tinderbox: errorparser: windows\n";
print LOG "tinderbox: buildfamily: windows\n";
print LOG "\n";
close( LOG );
system("$nstools\\bin\\blat logfile -t tinderbox-daemon\@warp" );
}
sub parse_args {
local($i);
if( @ARGV == 0 ){
&usage;
}
$i = 0;
while( $i < @ARGV ){
if( $ARGV[$i] eq '--depend' ){
$build_depend = 1;
}
elsif ( $ARGV[$i] eq '--clobber' ){
$build_depend = 0;
}
elsif ( $ARGV[$i] eq '--continue' ){
$build_continue = 1;
}
elsif ( $ARGV[$i] eq '--noawt11' ){
$noawt11 = 1;
}
elsif ( $ARGV[$i] eq '--no32' ){
$no32 = 1;
}
elsif ( $ARGV[$i] eq '--no16' ){
$no16 = 1;
}
elsif ( $ARGV[$i] eq '--test' ){
$build_test = 1;
}
elsif ( $ARGV[$i] eq '-tag' ){
$i++;
$build_tag = $ARGV[$i];
if( $build_tag eq '' || $build_tag eq '-t'){
&usage;
}
}
elsif ( $ARGV[$i] eq '-t' ){
$i++;
$build_tree = $ARGV[$i];
if( $build_tree eq '' ){
&usage;
}
}
$i++;
}
if( $build_tree eq '' ){
&usage;
}
}
sub usage {
die "usage: buildit.pl [--depend | --clobber] [--no16] [--continue] [--test] [-tag TAGNAME] -t TREENAME\n";
}
sub setup_env {
local($p);
$ENV{"MOZ_DEBUG"} = '1';
$ENV{"MOZ_GOLD"} = '1';
$ENV{"NO_SECURITY"} = '1';
$ENV{"MOZ_MEDIUM"} = '1';
$ENV{"MOZ_CAFE"} = '1';
$ENV{"NSPR20"} = '1';
$ENV{"VERBOSE"} = '1';
$nstools = $ENV{"MOZ_TOOLS"};
if( $nstools eq '' ){
die "error: environment variable MOZ_TOOLS not set\n";
}
$msdev = $ENV{"MOZ_MSDEV"} = 'c:\msdev';
if( $msdev eq '' ){
die "error: environment variable MOZ_MSDEV not set\n";
}
$msvc = $ENV{"MOZ_MSVC"} = 'c:\msvc';
if( $msvc eq '' ){
die "error: environment variable MOZ_VC not set\n";
}
if ( $build_tag ne '' ) {
$ENV{"MOZ_BRANCH"} = $build_tag;
}
$moz_src = $ENV{"MOZ_SRC"} = $start_dir;
$ENV{"MOZ_DATE"} = $start_time_str;
$src_16_drive = 'y:';
}
sub print_env {
local( $k, $v);
print LOG "\nEnvironment\n";
print "\nEnvironment\n";
for $k (sort keys %ENV){
$v = $ENV{$k};
print LOG " $k=$v\n";
print " $k=$v\n";
}
print LOG "\n";
print "\n";
system 'set';
}
sub strip_config {
$save_compname=$ENV{"COMPUTERNAME"};
$save_userdomain=$ENV{"USERDOMAIN"};
$save_username=$ENV{"USERNAME"};
$save_userprofile=$ENV{"USERPROFILE"};
# most of these deletes have no effect.
delete($ENV{"COMPUTERNAME"});
delete($ENV{"USERDOMAIN"});
delete($ENV{"USERNAME"});
delete($ENV{"USERPROFILE"});
# delete($ENV{"WATCOM"});
}
sub restore_config {
$ENV{"COMPUTERNAME"}=$save_compname;
$ENV{"USERDOMAIN"}=$save_userdomain;
$ENV{"USERNAME"}=$save_username;
$ENV{"MSDevDir"}=$save_userprofile;
&print_env;
}

View File

@@ -0,0 +1,31 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<META NAME="GENERATOR" CONTENT="Mozilla/4.0b2 (WinNT; I) [Netscape]">
</HEAD>
<BODY>
<H1>
FAQ on Tinderbox&nbsp;</H1>
<B><FONT SIZE=+2>Q. What is Tinderbox.</FONT></B>
<BR><FONT SIZE=+2>A. Your very own automated build page.&nbsp; It shows you how builds
are going on various platforms. fs</FONT>
<BR><FONT SIZE=+2></FONT>
<BR><B><FONT SIZE=+2>Q. I just checked in some code.&nbsp; How can I tell when
I'm OK.</FONT></B>
<BR><FONT SIZE=+2>A. You name will appear in the <I>guilty </I>column.&nbsp;
When there are successful (<FONT COLOR="#00FF00">green</FONT>) builds in
all the columns in a row <B>above</B> your name, you know you are ok.</FONT>
<BR>
<BR><B><FONT SIZE=+2>Q. The tree is broken, how do I find out what is busted
(or who busted it).</FONT></B>
<BR><FONT SIZE=+2>A. Clicking 'L' in the first red box (first build to break)
above a green will show you a build log for the broken build.&nbsp; You
can also click 'C' in this box and see what code was checked in.</FONT>
<BR>
<BR><B><FONT SIZE=+2>More Questions? Mail me <A HREF="mailto:ltabb@netscape.com">ltabb@netscape.com</A></FONT></B>
<BR>
</BODY>
</HTML>

View File

@@ -0,0 +1,217 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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 the Tinderbox build tool.
#
# 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.
use Socket;
require 'globals.pl';
require 'imagelog.pl';
# Port an old-style imagelog thing to a newstyle one
open( IMAGELOG, "<$data_dir/imagelog.txt" ) || die "can't open file";
open (OUT, ">$data_dir/newimagelog.txt") || die "can't open output file";
select(OUT); $| = 1; select(STDOUT);
while( <IMAGELOG> ){
chop;
($url,$quote) = split(/\`/);
print "$url\n";
$size = &URLsize($url);
$width = "";
$height = "";
if ($size =~ /WIDTH=([0-9]*)/) {
$width = $1;
}
if ($size =~ /HEIGHT=([0-9]*)/) {
$height = $1;
}
if ($width eq "" || $height eq "") {
print "Couldn't get image size; skipping.\n";
} else {
print OUT "$url`$width`$height`$quote\n";
}
}
sub imgsize {
local($file)= @_;
#first try to open the file
if( !open(STREAM, "<$file") ){
print "Can't open IMG $file";
$size="";
} else {
if ($file =~ /.jpg/i || $file =~ /.jpeg/i) {
$size = &jpegsize(STREAM);
} elsif($file =~ /.gif/i) {
$size = &gifsize(STREAM);
} elsif($file =~ /.xbm/i) {
$size = &xbmsize(STREAM);
} else {
return "";
}
$_ = $size;
if( /\s*width\s*=\s*([0-9]*)\s*/i ){
($newwidth)= /\s*width\s*=\s*(\d*)\s*/i;
}
if( /\s*height\s*=\s*([0-9]*)\s*/i ){
($newheight)=/\s*height\s*=\s*(\d*)\s*/i;
}
close(STREAM);
}
return $size;
}
###########################################################################
# Subroutine gets the size of the specified GIF
###########################################################################
sub gifsize {
local($GIF) = @_;
read($GIF, $type, 6);
if(!($type =~ /GIF8[7,9]a/) ||
!(read($GIF, $s, 4) == 4) ){
print "Invalid or Corrupted GIF";
$size="";
} else {
($a,$b,$c,$d)=unpack("C"x4,$s);
$size=join ("", 'WIDTH=', $b<<8|$a, ' HEIGHT=', $d<<8|$c);
}
return $size;
}
sub xbmsize {
local($XBM) = @_;
local($input)="";
$input .= <$XBM>;
$input .= <$XBM>;
$_ = $input;
if( /#define\s*\S*\s*\d*\s*\n#define\s*\S*\s*\d*\s*\n/i ){
($a,$b)=/#define\s*\S*\s*(\d*)\s*\n#define\s*\S*\s*(\d*)\s*\n/i;
$size=join ("", 'WIDTH=', $a, ' HEIGHT=', $b );
} else {
print "Hmmm... Doesn't look like an XBM file";
}
return $size;
}
# jpegsize : gets the width and height (in pixels) of a jpeg file
# Andrew Tong, werdna@ugcs.caltech.edu February 14, 1995
# modified slightly by alex@ed.ac.uk
sub jpegsize {
local($JPEG) = @_;
local($done)=0;
$size="";
read($JPEG, $c1, 1); read($JPEG, $c2, 1);
if( !((ord($c1) == 0xFF) && (ord($c2) == 0xD8))){
printf "This is not a JPEG! (Codes %02X %02X)\n", ord($c1), ord($c2);
$done=1;
}
while (ord($ch) != 0xDA && !$done) {
# Find next marker (JPEG markers begin with 0xFF)
# This can hang the program!!
while (ord($ch) != 0xFF) { read($JPEG, $ch, 1); }
# JPEG markers can be padded with unlimited 0xFF's
while (ord($ch) == 0xFF) { read($JPEG, $ch, 1); }
# Now, $ch contains the value of the marker.
$marker=ord($ch);
if (($marker >= 0xC0) && ($marker <= 0xCF) &&
($marker != 0xC4) && ($marker != 0xCC)) { # it's a SOFn marker
read ($JPEG, $junk, 3); read($JPEG, $s, 4);
($a,$b,$c,$d)=unpack("C"x4,$s);
$size=join("", 'HEIGHT=',$a<<8|$b,' WIDTH=',$c<<8|$d );
$done=1;
} else {
# We **MUST** skip variables, since FF's within variable
# names are NOT valid JPEG markers
read ($JPEG, $s, 2);
($c1, $c2) = unpack("C"x2,$s);
$length = $c1<<8|$c2;
if( ($length < 2) ){
print "Erroneous JPEG marker length";
$done=1;
} else {
read($JPEG, $junk, $length-2);
}
}
}
return $size;
}
###########################################################################
# Subroutine grabs a gif from another server and gets its size
###########################################################################
sub URLsize {
my ($fullurl) = @_;
my($dummy, $dummy, $serverstring, $url) = split(/\//, $fullurl, 4);
my($them,$port) = split(/:/, $serverstring);
my $port = 80 unless $port;
$them = 'localhost' unless $them;
my $size="";
$_=$url;
if( /gif/i || /jpeg/i || /jpg/i || /xbm/i ) {
my ($remote, $iaddr, $paddr, $proto, $line);
$remote = $them;
if ($port =~ /\D/) { $port = getservbyname($port, 'tcp') }
die "No port" unless $port;
$iaddr = inet_aton($remote) || die "no host: $remote";
$paddr = sockaddr_in($port, $iaddr);
$proto = getprotobyname('tcp');
socket(S, PF_INET, SOCK_STREAM, $proto) || return "socket: $!";
connect(S, $paddr) || return "connect: $!";
select(S); $| = 1; select(STDOUT);
print S "GET /$url\n";
if ($url =~ /.jpg/i || $url =~ /.jpeg/i) {
$size = &jpegsize(S);
} elsif($url =~ /.gif/i) {
$size = &gifsize(S);
} elsif($url =~ /.xbm/i) {
$size = &xbmsize(S);
} else {
return "";
}
$_ = $size;
if( /\s*width\s*=\s*([0-9]*)\s*/i ){
($newwidth)= /\s*width\s*=\s*(\d*)\s*/i;
}
if( /\s*height\s*=\s*([0-9]*)\s*/i ){
($newheight)=/\s*height\s*=\s*(\d*)\s*/i;
}
} else {
$size="";
}
return $size;
}
sub dokill {
kill 9,$child if $child;
}

View File

@@ -0,0 +1,556 @@
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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 the Tinderbox build tool.
#
# 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.
#
# Global variabls and functions for tinderbox
#
#
# Global variables
#
$td1 = {};
$td2 = {};
$build_list = []; # array of all build records
$build_name_index = {};
$ignore_builds = {};
$build_name_names = [];
$name_count = 0;
$build_time_index = {};
$build_time_times = [];
$time_count = 0;
$mindate_time_count = 0; # time_count that corresponds to the mindate
$build_table = [];
$who_list = [];
$who_list2 = [];
@note_array = ();
#$body_tag = "<BODY TEXT=#000000 BGCOLOR=#8080C0 LINK=#FFFFFF VLINK=#800080 ALINK=#FFFF00>";
#$body_tag = "<BODY TEXT=#000000 BGCOLOR=#FFFFC0 LINK=#0000FF VLINK=#800080 ALINK=#FF00FF>";
if( $ENV{'USERNAME'} eq 'ltabb' ){
$gzip = 'gzip';
}
else {
$gzip = '/usr/local/bin/gzip';
}
$data_dir='data';
$lock_count = 0;
1;
sub lock{
#if( $lock_count == 0 ){
# print "locking $tree/LOCKFILE.lck\n";
# open( LOCKFILE_LOCK, ">$tree/LOCKFILE.lck" );
# flock( LOCKFILE_LOCK, 2 );
#}
#$lock_count++;
}
sub unlock{
#$lock_count--;
#if( $lock_count == 0 ){
# flock( LOCKFILE_LOCK, 8 );
# close( LOCKFILE_LOCK );
#}
}
sub print_time {
my ($t) = @_;
my ($minute,$hour,$mday,$mon);
(undef,$minute,$hour,$mday,$mon,undef) = localtime($t);
sprintf("%02d/%02d&nbsp;%02d:%02d",$mon+1,$mday,$hour,$minute);
}
sub url_encode {
my ($s) = @_;
$s =~ s/\%/\%25/g;
$s =~ s/\=/\%3d/g;
$s =~ s/\?/\%3f/g;
$s =~ s/ /\%20/g;
$s =~ s/\n/\%0a/g;
$s =~ s/\r//g;
$s =~ s/\"/\%22/g;
$s =~ s/\'/\%27/g;
$s =~ s/\|/\%7c/g;
$s =~ s/\&/\%26/g;
return $s;
}
sub url_decode {
my ($value) = @_;
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
return $value;
}
sub value_encode {
my ($s) = @_;
$s =~ s@&@&amp;@g;
$s =~ s@<@&lt;@g;
$s =~ s@>@&gt;@g;
$s =~ s@\"@&quot;@g;
return $s;
}
sub load_data {
$tree2 = $form{'tree2'};
if( $tree2 ne '' ){
require "$tree2/treedata.pl";
if( -r "$tree2/ignorebuilds.pl" ){
require "$tree2/ignorebuilds.pl";
}
$td2 = {};
$td2->{name} = $tree2;
$td2->{cvs_module} = $cvs_module;
$td2->{cvs_branch} = $cvs_branch;
$td2->{num} = 1;
$td2->{ignore_builds} = $ignore_builds;
if( $cvs_root eq '' ){
$cvs_root = '/m/src';
}
$td2->{cvs_root} = $cvs_root;
$tree = $form{'tree'};
require "$tree/treedata.pl";
if( $cvs_root eq '' ){
$cvs_root = '/m/src';
}
}
$tree = $form{'tree'};
return unless $tree;
#die "the 'tree' parameter must be provided\n" unless $tree;
if ( -r "$tree/treedata.pl" ) {
require "$tree/treedata.pl";
}
$ignore_builds = {};
if( -r "$tree/ignorebuilds.pl" ){
require "$tree/ignorebuilds.pl";
}
$td1 = {};
$td1->{name} = $tree;
$td1->{num} = 0;
$td1->{cvs_module} = $cvs_module;
$td1->{cvs_branch} = $cvs_branch;
$td1->{ignore_builds} = $ignore_builds;
if( $cvs_root eq '' ){
$cvs_root = '/m/src';
}
$td1->{cvs_root} = $cvs_root;
&lock;
&load_buildlog;
&unlock;
&get_build_name_index;
&get_build_time_index;
&load_who($who_list, $td1);
if( $tree2 ne '' ){
&load_who($who_list2, $td2);
}
&make_build_table;
}
sub load_buildlog {
my $mailtime, $buildtime, $buildname, $errorparser;
my $buildstatus, $logfile,$binaryname;
my $buildrec, @treelist, $t;
if (not defined $maxdate) {
$maxdate = time();
}
if (not defined $mindate) {
$mindate = $maxdate - 24*60*60;
}
if ($tree2 ne '') {
@treelist = ($td1, $td2);
}
else {
@treelist = ($td1);
}
for $t (@treelist) {
use Backwards;
my ($bw) = Backwards->new("$t->{name}/build.dat") or die;
my $tooearly = 0;
while( $_ = $bw->readline ) {
chomp;
($mailtime, $buildtime, $buildname,
$errorparser, $buildstatus, $logfile, $binaryname) = split /\|/;
# Ignore stuff in the future.
next if $buildtime > $maxdate;
# Ignore stuff in the past (but get a 2 hours of extra data)
if ($buildtime < $mindate - 2*60*60) {
# Occasionally, a build might show up with a bogus time. So,
# we won't judge ourselves as having hit the end until we
# hit a full 20 lines in a row that are too early.
last if $tooearly++ > 20;
next;
}
$tooearly = 0;
$buildrec = {
mailtime => $mailtime,
buildtime => $buildtime,
buildname => ($tree2 ne '' ? $t->{name} . ' ' : '' ) . $buildname,
errorparser => $errorparser,
buildstatus => $buildstatus,
logfile => $logfile,
binaryname => $binaryname,
td => $t
};
if ($form{noignore} or not $t->{ignore_builds}->{$buildname}) {
push @{$build_list}, $buildrec;
}
}
}
}
sub loadquickparseinfo {
my ($tree, $build, $times) = (@_);
do "$tree/ignorebuilds.pl";
use Backwards;
my ($bw) = Backwards->new("$form{tree}/build.dat") or die;
my $latest_time = 0;
my $tooearly = 0;
while( $_ = $bw->readline ) {
chop;
my ($buildtime, $buildname, $buildstatus) = (split /\|/)[1,2,4];
if ($buildstatus =~ /^success|busted|testfailed$/) {
# Ignore stuff in the future.
next if $buildtime > $maxdate;
$latest_time = $buildtime if $buildtime > $latest_time;
# Ignore stuff more than 12 hours old
if ($buildtime < $latest_time - 12*60*60) {
# Hack: A build may give a bogus time. To compensate, we will
# not stop until we hit 20 consecutive lines that are too early.
last if $tooearly++ > 20;
next;
}
$tooearly = 0;
next if exists $ignore_builds->{$buildname};
next if exists $build->{$buildname}
and $times->{$buildname} >= $buildtime;
$build->{$buildname} = $buildstatus;
$times->{$buildname} = $buildtime;
}
}
}
sub load_who {
my ($who_list, $td) = @_;
my $d, $w, $i, $bfound;
open(WHOLOG, "<$td->{name}/who.dat");
while (<WHOLOG>) {
$i = $time_count;
chop;
($d,$w) = split /\|/;
$bfound = 0;
while ($i > 0 and not $bfound) {
if ($d <= $build_time_times->[$i]) {
$who_list->[$i+1]->{$w} = 1;
$bfound = 1;
}
else {
$i--;
}
}
}
# Ignore the last one
#
if ($time_count > 0) {
$who_list->[$time_count] = {};
}
}
sub get_build_name_index {
my $i,$br;
# Get all the unique build names.
#
foreach $br (@{$build_list}) {
$build_name_index->{$br->{buildname}} = 1;
}
$i = 1;
foreach $n (sort keys (%{$build_name_index})) {
$build_name_names->[$i] = $n;
$i++;
}
$name_count = @{$build_name_names}-1;
# Update the map so it points to the right index
#
for ($i=1; $i < $name_count+1; $i++) {
$build_name_index->{$build_name_names->[$i]} = $i;
}
}
sub get_build_time_index {
my $i,$br;
# Get all the unique build names.
#
foreach $br (@{$build_list}) {
$build_time_index->{$br->{buildtime}} = 1;
}
$i = 1;
foreach $n (sort {$b <=> $a} keys (%{$build_time_index})) {
$build_time_times->[$i] = $n;
$mindate_time_count = $i if $n >= $mindate;
$i++;
}
$time_count = @{$build_time_times}-1;
# Update the map so it points to the right index
#
for ($i=1; $i < $time_count+1; $i++) {
$build_time_index->{$build_time_times->[$i]} = $i;
}
#for $i (@{$build_time_times}) {
# print $i . "\n";
#}
#while( ($k,$v) = each(%{$build_time_index})) {
# print "$k=$v\n";
#}
}
sub make_build_table {
my $i,$ti,$bi,$ti1,$br;
# Create the build table
#
for ($i=1; $i <= $time_count; $i++){
$build_table->[$i] = [];
}
# Populate the build table with build data
#
foreach $br (reverse @{$build_list}) {
$ti = $build_time_index->{$br->{buildtime}};
$bi = $build_name_index->{$br->{buildname}};
$build_table->[$ti][$bi] = $br;
}
&load_notes;
for ($bi = $name_count; $bi > 0; $bi--) {
for ($ti = $time_count; $ti > 0; $ti--) {
if (defined($br = $build_table->[$ti][$bi])
and not defined($br->{rowspan})) {
# If the cell immediately after us is defined, then we
# can have a previousbuildtime.
if (defined($br1 = $build_table->[$ti+1][$bi])) {
$br->{previousbuildtime} = $br1->{buildtime};
}
$ti1 = $ti-1;
while ($ti1 > 0 and not defined($build_table->[$ti1][$bi])) {
$build_table->[$ti1][$bi] = -1;
$ti1--;
}
$br->{rowspan} = $ti - $ti1;
if ($br->{rowspan} != 1) {
$build_table->[$ti1+1][$bi] = $br;
$build_table->[$ti][$bi] = -1;
}
}
}
}
}
sub load_notes {
if ($tree2 ne '') {
@treelist = ($td1, $td2);
}
else {
@treelist = ($td1);
}
foreach $t (@treelist) {
open(NOTES,"<$t->{name}/notes.txt")
or print "<h2>warning: Couldn't open $t->{name}/notes.txt </h2>\n";
while (<NOTES>) {
chop;
($nbuildtime,$nbuildname,$nwho,$nnow,$nenc_note) = split /\|/;
$nbuildname = "$t->{name} $nbuildname" if $tree2 ne '';
$ti = $build_time_index->{$nbuildtime};
$bi = $build_name_index->{$nbuildname};
#print "[ti = $ti][bi=$bi][buildname='$nbuildname' $_<br>";
if ($ti != 0 and $bi != 0) {
$build_table->[$ti][$bi]->{hasnote} = 1;
if (not defined($build_table->[$ti][$bi]->{noteid})) {
$build_table->[$ti][$bi]->{noteid} = (0+@note_array);
}
$noteid = $build_table->[$ti][$bi]->{noteid};
$now_str = &print_time($nnow);
$note = &url_decode($nenc_note);
$note_array[$noteid] = "<pre>\n[<b><a href=mailto:$nwho>"
."$nwho</a> - $now_str</b>]\n$note\n</pre>"
.$note_array[$noteid];
}
}
close(NOTES);
}
}
sub last_success_time {
my ($row) = @_;
for (my $tt=1; $tt <= $time_count; $tt++) {
my $br = $build_table->[$tt][$row];
next unless defined $br;
next unless $br->{buildstatus} eq 'success';
return $build_time_times->[$tt + $br->{rowspan} ];
}
return 0;
}
sub last_status {
my ($row) = @_;
for (my $tt=1; $tt <= $time_count; $tt++) {
my $br = $build_table->[$tt][$row];
next unless defined $br;
next unless $br->{buildstatus} =~ /^(success|busted|testfailed)$/;
return $br->{buildstatus};
}
return 'building';
}
sub check_password {
if ($form{password} eq '') {
if (defined $cookie_jar{tinderbox_password}) {
$form{password} = $cookie_jar{tinderbox_password};
}
}
my $correct = '';
if (open(REAL, '<data/passwd')) {
$correct = <REAL>;
close REAL;
$correct =~ s/\s+$//; # Strip trailing whitespace.
}
$form{password} =~ s/\s+$//; # Strip trailing whitespace.
if ($form{password} ne '') {
open(TRAPDOOR, "../bonsai/data/trapdoor $form{'password'} |")
or die "Can't run trapdoor func!";
my $encoded = <TRAPDOOR>;
close TRAPDOOR;
$encoded =~ s/\s+$//; # Strip trailing whitespace.
if ($encoded eq $correct) {
if ($form{rememberpassword} ne '') {
print "Set-Cookie: tinderbox_password=$form{'password'} ;"
." path=/ ; expires = Sun, 1-Mar-2020 00:00:00 GMT\n";
}
return;
}
}
require 'header.pl';
print "Content-type: text/html\n";
print "Set-Cookie: tinderbox_password= ; path=/ ; "
." expires = Sun, 1-Mar-2020 00:00:00 GMT\n";
print "\n";
EmitHtmlHeader("What's the magic word?",
"You need to know the magic word to use this page.");
if ($form{password} ne '') {
print "<B>Invalid password; try again.<BR></B>";
}
print q(
<FORM method=post>
<B>Password:</B>
<INPUT NAME=password TYPE=password><BR>
<INPUT NAME=rememberpassword TYPE=checkbox>
If correct, remember password as a cookie<BR>
);
while (my ($key,$value) = each %form) {
next if $key eq "password" or $key eq "rememberpassword";
my $enc = value_encode($value);
print "<INPUT TYPE=HIDDEN NAME=$key VALUE='$enc'>\n";
}
print "<INPUT TYPE=SUBMIT value=Submit></FORM>\n";
exit;
}
sub find_build_record {
my ($tree, $logfile) = @_;
my $log_entry = `grep $logfile $tree/build.dat`;
chomp($log_entry);
my ($mailtime, $buildtime, $buildname, $errorparser,
$buildstatus, $logfile, $binaryname) = split /\|/, $log_entry;
$buildrec = {
mailtime => $mailtime,
buildtime => $buildtime,
buildname => $buildname,
errorparser => $errorparser,
buildstatus => $buildstatus,
logfile => $logfile,
binaryname => $binaryname,
td => undef
};
return $buildrec;
}

View File

@@ -0,0 +1,49 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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 the Tinderbox build tool.
#
# 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.
# Figure out which directory tinderbox is in by looking at argv[0]. Unless
# there is a command line argument; if there is, just use that.
$tinderboxdir = $0;
$tinderboxdir =~ s:/[^/]*$::; # Remove last word, and slash before it.
if ($tinderboxdir eq "") {
$tinderboxdir = ".";
}
if (@ARGV > 0) {
$tinderboxdir = $ARGV[0];
}
print "tinderbox = $tinderboxdir\n";
chdir $tinderboxdir || die "Couldn't chdir to $tinderboxdir";
open(DF, ">data/tbx.$$") || die "could not open data/tbx.$$";
while(<STDIN>){
print DF $_;
}
close(DF);
$err = system("./processbuild.pl data/tbx.$$");
if( $err ) {
die "processbuild.pl returned an error\n";
}

View File

@@ -0,0 +1,43 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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 the Tinderbox build tool.
#
# 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.
1;
sub add_imagelog {
local($url,$quote,$width,$height) = @_;
open( IMAGELOG, ">>$data_dir/imagelog.txt" ) || die "Oops; can't open imagelog.txt";
print IMAGELOG "$url`$width`$height`$quote\n";
close( IMAGELOG );
}
sub get_image{
local(@log,@ret,$i);
open( IMAGELOG, "<$data_dir/imagelog.txt" );
@log = <IMAGELOG>;
# return a random line
srand;
@ret = split(/\`/,$log[rand @log]);
close( IMAGELOG );
@ret;
}

View File

@@ -0,0 +1,12 @@
<TITLE>tinderbox</TITLE>
<META HTTP-EQUIV="Refresh" CONTENT="1; URL=showbuilds.cgi">
<BODY BGCOLOR="#FFFFFF" TEXT="#000000"
LINK="#0000EE" VLINK="#551A8B" ALINK="#FF0000">
<CENTER>
<TABLE BORDER=0 WIDTH="100%" HEIGHT="100%"><TR><TD ALIGN=CENTER VALIGN=CENTER>
<FONT SIZE="+2">
You're looking for
<A HREF="showbuilds.cgi">showbuilds.cgi</A>.
</FONT>
</TD></TR></TABLE>
</CENTER>

View File

@@ -0,0 +1,241 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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 the Tinderbox build tool.
#
# 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.
require 'globals.pl';
require 'timelocal.pl';
umask 0;
#$logfile = '';
%MAIL_HEADER = ();
$DONE = 0;
$building = 0;
$endsection = 0;
open( LOG, "<$ARGV[0]") || die "cant open $!";
&parse_mail_header;
while ($DONE == 0) {
%tbx = ();
&get_variables;
# run thru if EOF and we haven't hit our section end marker
if ( !$DONE || !$endsection) {
&check_required_vars;
$tree = $tbx{'tree'} if (!defined($tree));
$logfile = "$builddate.$$.gz" if (!defined($logfile));
$building++ if ($tbx{'status'} =~ m/building/);
&lock;
&write_build_data;
&unlock;
}
}
close(LOG);
&compress_log_file;
&unlink_log_file;
system "./buildwho.pl $tree";
# Build static pages for Sidebar flash and tinderbox panels.
$ENV{QUERY_STRING}="tree=$tree&static=1";
system './showbuilds.cgi';
# end of main
######################################################################
# This routine will scan through log looking for 'tinderbox:' variables
#
sub get_variables{
#while( ($k,$v) = each( %MAIL_HEADER ) ){
# print "$k='$v'\n";
#}
&parse_log_variables;
#while( ($k,$v) = each( %tbx ) ){
# print "$k='$v'\n";
#}
}
sub parse_log_variables {
my ($line, $stop);
$stop = 0;
while($stop == 0){
$line = <LOG>;
$DONE++, return if !defined($line);
chomp($line);
if( $line =~ /^tinderbox\:/ ){
if( $line =~ /^tinderbox\:[ \t]*([^:]*)\:[ \t]*([^\n]*)/ ){
$tbx{$1} = $2;
} elsif ( $line =~ /^tinderbox: END/ ) {
$stop++, $endsection++;
}
}
}
}
sub parse_mail_header {
my $line;
my $name = '';
while($line = <LOG> ){
chomp($line);
if( $line eq '' ){
return;
}
if( $line =~ /([^ :]*)\:[ \t]+([^\n]*)/ ){
$name = $1;
$name =~ tr/A-Z/a-z/;
$MAIL_HEADER{$name} = $2;
#print "$name $2\n";
}
elsif( $name ne '' ){
$MAIL_HEADER{$name} .= $2;
}
}
}
sub check_required_vars {
$err_string = '';
if( $tbx{'tree'} eq ''){
$err_string .= "Variable 'tinderbox:tree' not set.\n";
}
elsif( ! -r $tbx{'tree'} ){
$err_string .= "Variable 'tinderbox:tree' not set to a valid tree.\n";
}
elsif(($MAIL_HEADER{'to'} =~ /external/i ||
$MAIL_HEADER{'cc'} =~ /external/i) &&
$tbx{'tree'} !~ /external/i) {
$err_string .= "Data from an external source didn't specify an 'external' tree.";
}
if( $tbx{'build'} eq ''){
$err_string .= "Variable 'tinderbox:build' not set.\n";
}
if( $tbx{'errorparser'} eq ''){
$err_string .= "Variable 'tinderbox:errorparser' not set.\n";
}
#
# Grab the date in the form of mm/dd/yy hh:mm:ss
#
# Or a GMT unix date
#
if( $tbx{'builddate'} eq ''){
$err_string .= "Variable 'tinderbox:builddate' not set.\n";
}
else {
if( $tbx{'builddate'} =~
/([0-9]*)\/([0-9]*)\/([0-9]*)[ \t]*([0-9]*)\:([0-9]*)\:([0-9]*)/ ){
$builddate = timelocal($6,$5,$4,$2,$1-1,$3);
}
elsif( $tbx{'builddate'} > 7000000 ){
$builddate = $tbx{'builddate'};
}
else {
$err_string .= "Variable 'tinderbox:builddate' not of the form MM/DD/YY HH:MM:SS or unix date\n";
}
}
#
# Build Status
#
if( $tbx{'status'} eq ''){
$err_string .= "Variable 'tinderbox:status' not set.\n";
}
elsif( ! $tbx{'status'} =~ /success|busted|building|testfailed/ ){
$err_string .= "Variable 'tinderbox:status' must be 'success', 'busted', 'testfailed', or 'building'\n";
}
#
# Report errors
#
if( $err_string ne '' ){
die $err_string;
}
}
sub write_build_data {
$t = time;
open( BUILDDATA, ">>$tbx{'tree'}/build.dat" )|| die "can't open $! for writing";
print BUILDDATA "$t|$builddate|$tbx{'build'}|$tbx{'errorparser'}|$tbx{'status'}|$logfile|$tbx{binaryname}\n";
close( BUILDDATA );
}
sub compress_log_file {
local( $done, $line);
return if ( $building );
open( LOG2, "<$ARGV[0]") || die "cant open $!";
#
# Skip past the the RFC822.HEADER
#
$done = 0;
while( !$done && ($line = <LOG2>) ){
chomp($line);
$done = ($line eq '');
}
open( ZIPLOG, "| $gzip -c > ${tree}/$logfile" ) || die "can't open $! for writing";
$inBinary = 0;
$hasBinary = ($tbx{'binaryname'} ne '');
while( $line = <LOG2> ){
if( !$inBinary ){
print ZIPLOG $line;
if( $hasBinary ){
$inBinary = ($line =~ /^begin [0-7][0-7][0-7] /);
}
}
else {
if( $line =~ /^end\n/ ){
$inBinary = 0;
}
}
}
close( ZIPLOG );
close( LOG2 );
#
# If a uuencoded binary is part of the build, unpack it.
#
if( $hasBinary ){
$bin_dir = "$tbx{'tree'}/bin/$builddate/$tbx{'build'}";
$bin_dir =~ s/ //g;
system("mkdir -m 0777 -p $bin_dir");
# LTNOTE: I'm not sure this is cross platform.
system("/tools/ns/bin/uudecode --output-file=$bin_dir/$tbx{binaryname} < $ARGV[0]");
}
}
sub unlink_log_file {
unlink( $ARGV[0] );
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,782 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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 the Tinderbox build tool.
#
# 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.
use lib '../bonsai';
require 'globals.pl';
require 'lloydcgi.pl';
require 'imagelog.pl';
require 'header.pl';
$|=1;
# Hack this until I can figure out how to do get default root. -slamm
$default_root = '/cvsroot';
# Show 12 hours by default
#
$nowdate = time;
if (not defined($maxdate = $form{maxdate})) {
$maxdate = $nowdate;
}
if ($form{showall}) {
$mindate = 0;
}
else {
$default_hours = 12;
$hours = $default_hours;
$hours = $form{hours} if $form{hours};
$mindate = $maxdate - ($hours*60*60);
}
%colormap = (
success => '00ff00',
busted => 'red',
building => 'yellow',
testfailed => 'orange'
);
%images = (
flames => '1afi003r.gif',
star => 'star.gif'
);
$tree = $form{tree};
if (exists $form{rebuildguilty} or exists $form{showall}) {
system ("./buildwho.pl -days 7 $tree > /dev/null");
undef $form{rebuildguilty};
}
&show_tree_selector, exit if $form{tree} eq '';
&do_quickparse, exit if $form{quickparse};
&do_express, exit if $form{express};
&do_rdf, exit if $form{rdf};
&do_static, exit if $form{static};
&do_flash, exit if $form{flash};
&do_panel, exit if $form{panel};
&do_tinderbox, exit;
# end of main
#=====================================================================
sub make_tree_list {
my @result;
while(<*>) {
if( -d $_ && $_ ne 'data' && $_ ne 'CVS' && -f "$_/treedata.pl") {
push @result, $_;
}
}
return @result;
}
sub show_tree_selector {
print "Content-type: text/html\n\n";
EmitHtmlHeader("tinderbox");
print "<P><TABLE WIDTH=\"100%\">";
print "<TR><TD ALIGN=CENTER>Select one of the following trees:</TD></TR>";
print "<TR><TD ALIGN=CENTER>\n";
print " <TABLE><TR><TD><UL>\n";
my @list = make_tree_list();
foreach (@list) {
print "<LI><a href=showbuilds.cgi?tree=$_>$_</a>\n";
}
print "<//UL></TD></TR></TABLE></TD></TR></TABLE>";
print "<P><TABLE WIDTH=\"100%\">";
print "<TR><TD ALIGN=CENTER><a href=admintree.cgi>";
print "Administer</a> one of the following trees:</TD></TR>";
print "<TR><TD ALIGN=CENTER>\n";
print " <TABLE><TR><TD><UL>\n";
foreach (@list) {
print "<LI><a href=admintree.cgi?tree=$_>$_</a>\n";
}
print "<//UL></TD></TR></TABLE></TD></TR></TABLE>";
}
sub do_static {
local *OUT;
$form{nocrap}=1;
my @pages = ( ['index.html', 'do_tinderbox'],
['flash.rdf', 'do_flash'],
['panel.html', 'do_panel'] );
$rel_path = '../';
while (($key, $value) = each %images) {
$images{$key} = "$rel_path$value";
}
my $oldfh = select;
foreach $pair (@pages) {
my ($page, $call) = @{$pair};
my $outfile = "$form{tree}/$page";
open(OUT,">$outfile.$$");
select OUT;
eval "$call";
close(OUT);
system "mv $outfile.$$ $outfile";
}
select $oldfh;
}
sub do_tinderbox {
&load_data;
&print_page_head;
&print_table_header;
&print_table_body;
&print_table_footer;
}
sub print_page_head {
print "Content-type: text/html",
($nowdate eq $maxdate ? "\nRefresh: 900" : ''),
"\n\n<HTML>\n" unless $form{static};
# Get the message of the day only on the first pageful
do "$tree/mod.pl" if $nowdate eq $maxdate;
use POSIX qw(strftime);
# Print time in format, "HH:MM timezone"
my $now = strftime("%H:%M %Z", localtime);
EmitHtmlTitleAndHeader("tinderbox: $tree", "tinderbox",
"tree: $tree ($now)");
&print_javascript;
print "$message_of_day\n";
# Quote and Lengend
#
unless ($form{nocrap}) {
my ($imageurl,$imagewidth,$imageheight,$quote) = &get_image;
print qq{
<table width="100%" cellpadding=0 cellspacing=0>
<tr>
<td valign=bottom>
<p><center><a href=addimage.cgi><img src="$rel_path$imageurl"
width=$imagewidth height=$imageheight><br>
$quote</a><br>
</center>
<p>
<td align=right valign=bottom>
<table cellspacing=0 cellpadding=1 border=0><tr><td align=center>
<TT>L</TT></td><td>= Show Build Log
</td></tr><tr><td align=center>
<img src="$images{star}"></td><td>= Show Log comments
</td></tr><tr><td colspan=2>
<table cellspacing=1 cellpadding=1 border=1>
<tr bgcolor="$colormap{success}"><td>Successful Build
<tr bgcolor="$colormap{building}"><td>Build in Progress
<tr bgcolor="$colormap{testfailed}"><td>Successful Build,
but Tests Failed
<tr bgcolor="$colormap{busted}"><td>Build Failed
</table>
</td></tr></table>
</td>
</tr>
</table>
};
}
if ($bonsai_tree) {
print "The tree is currently <font size=+2>";
print (&tree_open ? 'OPEN' : 'CLOSED');
print "</font>\n";
}
}
sub print_table_body {
for (my $tt=1; $tt <= $time_count; $tt++) {
last if $build_time_times->[$tt] < $mindate;
print_table_row($tt);
}
}
sub print_table_row {
my ($tt) = @_;
# Time column
#
my $query_link = '';
my $end_query = '';
my $pretty_time = &print_time($build_time_times->[$tt]);
($hour) = $pretty_time =~ /(\d\d):/;
if ($lasthour != $hour or &has_who_list($tt)) {
$query_link = &query_ref($td1, $build_time_times->[$tt]);
$end_query = '</a>';
}
if ($lasthour == $hour) {
$pretty_time =~ s/^.*&nbsp;//;
} else {
$lasthour = $hour;
}
my $hour_color = '';
$hour_color = ' bgcolor=#e7e7e7' if $build_time_times->[$tt] % 7200 <= 3600;
print "<tr align=center><td align=right$hour_color>",
"$query_link\n$pretty_time$end_query</td>\n";
# Guilty
#
print '<td>';
for $who (sort keys %{$who_list->[$tt]} ){
$qr = &who_menu($td1, $build_time_times->[$tt],
$build_time_times->[$tt-1],$who);
$who =~ s/%.*$//;
print " ${qr}$who</a>\n";
}
print '</td>';
# Build Status
#
for ($bn=1; $bn <= $name_count; $bn++) {
if (not defined($br = $build_table->[$tt][$bn])) {
# No build data for this time
print "<td></td>\n";
next;
}
next if $br == -1; # rowspan has covered this row
$hasnote = $br->{hasnote};
$noteid = $hasnote ? $br->{noteid} : 0;
$rowspan = $br->{rowspan};
$rowspan = $mindate_time_count - $tt + 1
if $tt + $rowspan - 1 > $mindate_time_count;
$color = $colormap{$br->{buildstatus}};
$status = $br->{buildstatus};
print "<td rowspan=$rowspan bgcolor=${color}>\n";
$logfile = $br->{logfile};
$errorparser = $br->{errorparser};
$buildname = $br->{buildname};
$buildtime = $br->{buildtime};
$buildtree = $br->{td}->{name};
print "<tt>\n";
# Build Note
#
$buildname = &url_encode($buildname);
my $logurl = "${rel_path}showlog.cgi?log=$buildtree/$logfile";
if ($hasnote) {
print "<a href='$logurl' onclick=\"return ",
"note(event,$noteid,'$logfile');\">",
"<img src='$images{star}' border=0></a>\n";
}
# Build Log
#
print "<A HREF='$logurl' onclick=\"return log(event,$bn,'$logfile');\">";
print "L</a>";
# What Changed
#
if( $br->{previousbuildtime} ){
my $previous_br = $build_table->[$tt+$rowspan][$bn];
my $previous_rowspan = $previous_br->{rowspan};
if (&has_who_list($tt+$rowspan,
$tt+$rowspan+$previous_rowspan-1)) {
print "\n", &query_ref($br->{td},
$br->{previousbuildtime},
$br->{buildtime});
print "C</a>";
}
}
if ($br->{binaryname} ne '') {
$binfile = "$buildtree/bin/$buildtime/$br->{buildname}/"
."$br->{binaryname}";
$binfile =~ s/ //g;
print " <a href=$rel_path$binfile>B</a>";
}
print "</tt>\n</td>";
}
print "</tr>\n";
}
sub print_table_header {
my $ii, $nspan;
print "<table border=1 bgcolor='#FFFFFF' cellspacing=1 cellpadding=1>\n";
print "<tr align=center>\n";
print "<td rowspan=1><font size=-1>Click time to <br>see changes <br>",
"since time</font></td>";
print "<td><font size=-1>",
"Click name to see what they did</font>";
print "<br><font size=-2>",
&open_showbuilds_href(rebuildguilty=>'1'),
"Rebuild guilty list</a></td>";
for ($ii=1; $ii <= $name_count; $ii++) {
my $bn = $build_name_names->[$ii];
$bn =~ s/Clobber/Clbr/g;
$bn =~ s/Depend/Dep/g;
$bn = "<font face='Helvetica,Arial' size=-1>$bn</font>";
my $last_status = &last_status($ii);
if ($last_status eq 'busted') {
if ($form{nocrap}) {
print "<td rowspan=2 bgcolor=$colormap{busted}>$bn</td>";
} else {
print "<td rowspan=2 bgcolor=000000 background='$images{flames}'>";
print "<font color=white>$bn</font></td>";
}
}
else {
print "<td rowspan=2 bgcolor=$colormap{$last_status}>$bn</td>";
}
}
print "</tr><tr>\n";
print "<TH>Build Time</TH>\n";
print "<TH>Guilty</th>\n";
print "</tr>\n";
}
sub print_table_footer {
print "</table>\n";
my $nextdate = $maxdate - $hours*60*60;
print &open_showbuilds_href(maxdate=>"$nextdate", nocrap=>'1')
."Show next $hours hours</a>";
print "<p><a href='${rel_path}admintree.cgi?tree=$tree'>",
"Administrate Tinderbox Trees</a><br>\n";
}
sub open_showbuilds_url {
my %args = (
nocrap => "$form{nocrap}",
@_
);
my $url = "${rel_path}showbuilds.cgi?tree=$form{tree}";
$url .= "&hours=$hours" if $hours ne $default_hours;
while (my ($key, $value) = each %args) {
$url .= "&$key=$value" if $value ne '';
}
return $url;
}
sub open_showbuilds_href {
return "<a href=".open_showbuilds_url(@_).">";
}
sub query_ref {
my ($td, $mindate, $maxdate, $who) = @_;
my $output = '';
$output = "<a href=${rel_path}../bonsai/cvsquery.cgi";
$output .= "?module=$td->{cvs_module}";
$output .= "&branch=$td->{cvs_branch}" if $td->{cvs_branch} ne 'HEAD';
$output .= "&cvsroot=$td->{cvs_root}" if $td->{cvs_root} ne $default_root;
$output .= "&date=explicit&mindate=$mindate";
$output .= "&maxdate=$maxdate" if $maxdate ne '';
$output .= "&who=$who" if $who ne '';
$output .= ">";
}
sub query_ref2 {
my ($td, $mindate, $maxdate, $who) = @_;
return "${rel_path}../bonsai/cvsquery.cgi?module=$td->{cvs_module}"
."&branch=$td->{cvs_branch}&cvsroot=$td->{cvs_root}"
."&date=explicit&mindate=$mindate&maxdate=$maxdate&who="
. url_encode($who);
}
sub who_menu {
my ($td, $mindate, $maxdate, $who) = @_;
my $treeflag;
$qr = "${rel_path}../registry/who.cgi?email=". url_encode($who)
. "&d=$td->{cvs_module}|$td->{cvs_branch}|$td->{cvs_root}|$mindate|$maxdate";
return "<a href='$qr' onclick=\"return who(event);\">";
}
# Check to see if anyone checked in during time slot.
# ex. has_who_list(1); # Check for checkins in most recent time slot.
# ex. has_who_list(1,5); # Check range of times.
sub has_who_list {
my ($time1, $time2) = @_;
if (not defined(@who_check_list)) {
# Build a static array of true/false values for each time slot.
$who_check_list[$time_count] = 0;
my ($t) = 1;
for (; $t<=$time_count; $t++) {
$who_check_list[$t] = 1 if each %{$who_list->[$t]};
}
}
if ($time2) {
for ($ii=$time1; $ii<=$time2; $ii++) {
return 1 if $who_check_list[$ii]
}
return 0
} else {
return $who_check_list[$time1];
}
}
sub tree_open {
my $done, $line, $a, $b;
open(BID, "<../bonsai/data/$bonsai_tree/batchid")
or print "can't open batchid<br>";
($a,$b,$bid) = split / /, <BID>;
close(BID);
open(BATCH, "<../bonsai/data/$bonsai_tree/batch-${bid}")
or print "can't open batch-${bid}<br>";;
$done = 0;
while (($line = <BATCH>) and not $done){
if ($line =~ /^set treeopen/) {
chop $line;
($a,$b,$treestate) = split / /, $line ;
$done = 1;
}
}
close(BATCH);
return $treestate;
}
sub print_javascript {
my $script;
($script = <<"__ENDJS") =~ s/^ //gm;
<script>
if (parseInt(navigator.appVersion) < 4) {
window.event = 0;
}
function who(d) {
var version = parseInt(navigator.appVersion);
if (version < 4 || version >= 5) {
return true;
}
var l = document.layers['popup'];
l.src = d.target.href;
l.top = d.target.y - 6;
l.left = d.target.x - 6;
if (l.left + l.clipWidth > window.width) {
l.left = window.width - l.clipWidth;
}
l.visibility="show";
return false;
}
function log_url(logfile) {
return "showlog.cgi?log=" + buildtree + "/" + logfile;
}
function note(d,noteid,logfile) {
var version = parseInt(navigator.appVersion);
if (version < 4 || version >= 5) {
document.location = log_url(logfile);
return false;
}
var l = document.layers['popup'];
l.document.write("<table border=1 cellspacing=1><tr><td>"
+ notes[noteid] + "</tr></table>");
l.document.close();
l.top = d.y-10;
var zz = d.x;
if (zz + l.clip.right > window.innerWidth) {
zz = (window.innerWidth-30) - l.clip.right;
if (zz < 0) { zz = 0; }
}
l.left = zz;
l.visibility="show";
return false;
}
function log(e,buildindex,logfile)
{
var logurl = log_url(logfile);
var commenturl = "addnote.cgi?log=" + buildtree + "/" + logfile;
var version = parseInt(navigator.appVersion);
if (version < 4 || version >= 5) {
document.location = logurl;
return false;
}
var q = document.layers["logpopup"];
q.top = e.target.y - 6;
var yy = e.target.x;
if ( yy + q.clip.right > window.innerWidth) {
yy = (window.innerWidth-30) - q.clip.right;
if (yy < 0) { yy = 0; }
}
q.left = yy;
q.visibility="show";
q.document.write("<TABLE BORDER=1><TR><TD><B>"
+ builds[buildindex] + "</B><BR>"
+ "<A HREF=$rel_path" + logurl + ">View Brief Log</A><BR>"
+ "<A HREF=$rel_path" + logurl + "&fulltext=1"+">View Full Log</A><BR>"
+ "<A HREF=$rel_path" + commenturl + ">Add a Comment</A><BR>"
+ "</TD></TR></TABLE>");
q.document.close();
return false;
}
notes = new Array();
builds = new Array();
__ENDJS
print $script;
$ii = 0;
while ($ii < @note_array) {
$ss = $note_array[$ii];
while ($ii < @note_array && $note_array[$ii] eq $ss) {
print "notes[$ii] = ";
$ii++;
}
$ss =~ s/\\/\\\\/g;
$ss =~ s/\"/\\\"/g;
$ss =~ s/\n/\\n/g;
print "\"$ss\";\n";
}
for ($ii=1; $ii <= $name_count; $ii++) {
if (defined($br = $build_table->[1][$ii]) and $br != -1) {
my $bn = $build_name_names->[$ii];
print "builds[$ii]='$bn';\n";
}
}
print "buildtree = '$form{tree}';\n";
($script = <<'__ENDJS') =~ s/^ //gm;
</script>
<layer name="popup" onMouseOut="this.visibility='hide';"
left=0 top=0 bgcolor="#ffffff" visibility="hide">
</layer>
<layer name="logpopup" onMouseOut="this.visibility='hide';"
left=0 top=0 bgcolor="#ffffff" visibility="hide">
</layer>
__ENDJS
print $script;
}
sub do_express {
print "Content-type: text/html\nRefresh: 900\n\n<HTML>\n";
my %build, %times;
loadquickparseinfo($form{tree}, \%build, \%times);
my @keys = sort keys %build;
my $keycount = @keys;
my $tm = &print_time(time);
print "<table border=1 cellpadding=1 cellspacing=1><tr>";
print "<th align=left colspan=$keycount>";
print &open_showbuilds_href."$tree as of $tm</a></tr><tr>\n";
foreach my $buildname (@keys) {
print "<td bgcolor='$colormap{$build{$buildname}}'>$buildname</td>";
}
print "</tr></table>\n";
}
# This is essentially do_express but it outputs a different format
sub do_panel {
print "Content-type: text/html\n\n<HTML>\n" unless $form{static};
my %build, %times;
loadquickparseinfo($form{tree}, \%build, \%times);
print q(
<head>
<META HTTP-EQUIV="Refresh" CONTENT="300">
<style>
body, td {
font-family: Verdana, Sans-Serif;
font-size: 8pt;
}
</style>
</head>
<body BGCOLOR="#FFFFFF" TEXT="#000000"
LINK="#0000EE" VLINK="#551A8B" ALINK="#FF0000">
);
# Hack the panel link for now.
print "<a target='_content' href='http://tinderbox.mozilla.org/seamonkey/'>$tree";
$bonsai_tree = '';
require "$tree/treedata.pl";
if ($bonsai_tree ne '') {
print " is ", tree_open() ? "OPEN" : "CLOSED";
}
# Add the current time
my ($minute,$hour,$mday,$mon) = (localtime)[1..4];
my $tm = sprintf("%d/%d&nbsp;%d:%02d",$mon+1,$mday,$hour,$minute);
print " as of $tm</a><br>";
print "<table border=0 cellpadding=1 cellspacing=1>";
while (my ($name, $status) = each %build) {
print "<tr><td bgcolor='$colormap{$status}'>$name</td></tr>";
}
print "</table></body>";
}
sub do_flash {
print "Content-type: text/rdf\n\n" unless $form{static};
my %build, %times;
loadquickparseinfo($form{tree}, \%build, \%times);
my ($mac,$unix,$win) = (0,0,0);
while (my ($name, $status) = each %build) {
next if $status eq 'success';
$mac = 1, next if $name =~ /Mac/;
$win = 1, next if $name =~ /Win/;
$unix = 1;
}
print q{
<RDF:RDF xmlns:RDF='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
xmlns:NC='http://home.netscape.com/NC-rdf#'>
<RDF:Description about='NC:FlashRoot'>
};
my $busted = $mac + $unix + $win;
if ($busted) {
# Construct a legible sentence; e.g., "Mac, Unix, and Windows
# are busted", "Windows is busted", etc. This is hideous. If
# you can think of something better, please fix it.
my $text;
if ($mac) {
$text .= 'Mac' . ($busted > 2 ? ', ' : ($busted > 1 ? ' and ' : ''));
}
if ($unix) {
$text .= 'Unix' . ($busted > 2 ? ', and ' : ($win ? ' and ' : ''));
}
if ($win) {
$text .= 'Windows';
}
$text .= ($busted > 1 ? ' are ' : ' is ') . 'busted';
# The Flash spec says we need to give ctime.
use POSIX;
my $tm = POSIX::ctime(time());
$tm =~ s/^...\s//; # Strip day of week
$tm =~ s/:\d\d\s/ /; # Strip seconds
chop $tm;
print qq{
<NC:child>
<RDF:Description ID='flash'>
<NC:type resource='http://www.mozilla.org/RDF#TinderboxFlash' />
<NC:source>$tree</NC:source>
<NC:description>$text</NC:description>
<NC:timestamp>$tm</NC:timestamp>
</RDF:Description>
</NC:child>
};
}
print q{
</RDF:Description>
</RDF:RDF>
};
}
sub do_quickparse {
print "Content-type: text/plain\n\n";
my @treelist = split /,/, $tree;
foreach my $t (@treelist) {
$bonsai_tree = "";
require "$t/treedata.pl";
if ($bonsai_tree ne "") {
my $state = tree_open() ? "Open" : "Close";
print "State|$t|$bonsai_tree|$state\n";
}
my %build, %times;
loadquickparseinfo($t, \%build, \%times);
foreach my $buildname (sort keys %build) {
print "Build|$t|$buildname|$build{$buildname}\n";
}
}
}
sub do_rdf {
print "Content-type: text/plain\n\n";
my $mainurl = "http://$ENV{SERVER_NAME}$ENV{SCRIPT_NAME}?tree=$tree";
my $dirurl = $mainurl;
$dirurl =~ s@/[^/]*$@@;
my %build, %times;
loadquickparseinfo($tree, \%build, \%times);
my $image = "channelok.gif";
my $imagetitle = "OK";
foreach my $buildname (sort keys %build) {
if ($build{$buildname} eq 'busted') {
$image = "channelflames.gif";
$imagetitle = "Bad";
last;
}
}
print qq{<?xml version="1.0"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://my.netscape.com/rdf/simple/0.9/">
<channel>
<title>Tinderbox - $tree</title>
<description>Build bustages for $tree</description>
<link>$mainurl</link>
</channel>
<image>
<title>$imagetitle</title>
<url>$dirurl/$image</url>
<link>$mainurl</link>
</image>
};
$bonsai_tree = '';
require "$tree/treedata.pl";
if ($bonsai_tree ne '') {
my $state = tree_open() ? "OPEN" : "CLOSED";
print "<item><title>The tree is currently $state</title>",
"<link>$mainurl</link></item>\n";
}
foreach my $buildname (sort keys %build) {
if ($build{$buildname} eq 'busted') {
print "<item><title>$buildname is in flames</title>",
"<link>$mainurl</link></item>\n";
}
}
print "</rdf:RDF>\n";
}

View File

@@ -0,0 +1,136 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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 the Tinderbox build tool.
#
# 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.
$| = 1;
use lib "../bonsai";
require 'globals.pl';
require 'imagelog.pl';
require 'lloydcgi.pl';
require 'header.pl';
check_password();
print "Content-type: text/html\n\n";
@url = ();
@quote = ();
@width = ();
@height = ();
$i = 0;
EmitHtmlHeader("tinderbox: all images");
print '<UL>
<P>These are all of the images currently in
<A HREF=http://www.mozilla.org/tinderbox.html>Tinderbox</A>.
<P>Please don\'t give out this URL: this is only here for our debugging
needs, and isn\'t linked to by the rest of Tinderbox: because looking at
all the images at once would be be cheating! you\'re supposed to let them
surprise you over time. What, do you read ahead in your desktop calendar,
too? Where\'s your sense of mystery and anticipation?
<P>
</UL>
';
if ($form{'url'} ne "") {
$oldname = "$data_dir/imagelog.txt";
open (OLD, "<$oldname") || die "Oops; can't open imagelog.txt";
$newname = "$oldname-$$";
open (NEW, ">$newname") || die "Can't open $newname";
$foundit = 0;
while (<OLD>) {
chop;
($url, $width, $height, $quote) = split(/\`/);
if ($url eq $form{'url'} && $quote eq $form{'origquote'}) {
$foundit = 1;
if ($form{'nukeit'} ne "") {
next;
}
$quote = $form{'quote'};
}
print NEW "$url`$width`$height`$quote\n";
}
close OLD;
close NEW;
if (!$foundit) {
print "<font color=red>Hey, couldn't find it!</font> Did someone\n";
print "else already edit it?<P>\n";
unlink $newname;
} else {
print "Change made.<P>";
rename ($newname, $oldname) || die "Couldn't rename $newname to $oldname";
}
$form{'doedit'} = "1";
}
$doedit = ($form{'doedit'} ne "");
if (!$doedit) {
print "
<form method=post>
<input type=hidden name=password value=\"$form{'password'}\">
<input type=hidden name=doedit value=1>
<input type=submit value='Let me edit text or remove pictures.'>
</form><P>";
}
open( IMAGELOG, "<$data_dir/imagelog.txt" ) || die "can't open file";
while( <IMAGELOG> ){
chop;
($url[$i],$width[$i],$height[$i],$quote[$i]) = split(/\`/);
$i++;
}
close( IMAGELOG );
$i--;
print "<center>";
while( $i >= 0 ){
$qurl = value_encode($url[$i]);
$qquote = value_encode($quote[$i]);
print "
<img border=2 src='$url[$i]' width='$width[$i]' height='$height[$i]'><br>
<i>$quote[$i]</i>";
if ($doedit) {
print "
<form method=post>
<input type=submit name=nukeit value='Delete this image'><br>
<input name=quote size=60 value=\"$qquote\"><br>
<input type=submit name=edit value='Change text'><hr>
<input type=hidden name=url value=\"$qurl\">
<input type=hidden name=origquote value=\"$qquote\">
<input type=hidden name=password value=\"$form{'password'}\">
</form>";
}
print "<br><br>\n";
$i--;
}

View File

@@ -0,0 +1,375 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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 the Tinderbox build tool.
#
# 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.
use lib '../bonsai';
require 'globals.pl';
require 'lloydcgi.pl';
require 'header.pl';
#############################################################
# Global variables
$LINES_AFTER_ERROR = 5;
$LINES_BEFORE_ERROR = 30;
# These variables are set by the error parser functions:
# has_error(), has_warning(), and has_errorline().
$error_file = '';
$error_file_ref = '';
$error_line = 0;
$error_guess = 0;
$next_err = 0;
@log_errors = ();
$log_line = 0;
#############################################################
# CGI inputs
if (defined($args = $form{log}) or defined($args = $form{exerpt})) {
($full_logfile, $linenum) = split /:/, $args;
($tree, $logfile) = split /\//, $full_logfile;
my $br = find_build_record($tree, $logfile);
$errorparser = $br->{errorparser};
$buildname = $br->{buildname};
$buildtime = $br->{buildtime};
$numlines = 50;
$numlines = $form{numlines} if exists $form{numlines};
} else {
$tree = $form{tree};
$errorparser = $form{errorparser};
$logfile = $form{logfile};
$buildname = $form{buildname};
$buildtime = $form{buildtime};
}
$fulltext = $form{fulltext};
$enc_buildname = &url_encode($buildname);
die "the \"tree\" parameter must be provided\n" unless $tree;
require "$tree/treedata.pl";
$time_str = print_time( $buildtime );
$|=1;
if ($linenum) {
&print_fragment;
exit;
}
&print_header;
&print_notes;
# Dynamically load the error parser
#
die "the \"errorparser\" parameter must be provided\n" unless $errorparser;
require "ep_${errorparser}.pl";
if ($fulltext)
{
&print_summary;
&print_log;
}
else
{
$brief_filename = $logfile;
$brief_filename =~ s/.gz$/.brief.html/;
if (-T "$tree/$brief_filename" and -M _ > -M $tree/$logfile)
{
open (BRIEFFILE, "<$tree/$brief_filename");
print while (<BRIEFFILE>)
}
else
{
open (BRIEFFILE, ">$tree/$brief_filename");
&print_summary;
&print_log;
}
}
# end of main
############################################################
sub print_fragment {
print "Content-type: text/html\n\n";
print "<META HTTP-EQUIV=\"EXPIRES\" CONTENT=\"1\">\n";
my $heading = "Build Log (Fragment)";
my $subheading = "$buildname on $time_str";
my $title = "$heading - $subheading";
EmitHtmlTitleAndHeader($title, $heading, $subheading);
print "<a href='showlog.cgi?tree=$tree&errorparser=$errorparser&logfile=$logfile&buildtime=$buildtime&buildname=$enc_buildname&fulltext=1'>Show Full Build Log</a>";
open(BUILD_IN, "$gzip -d -c $tree/$logfile|");
my $first_line = $linenum - ($numlines/2);
my $last_line = $linenum + ($numlines/2);
print "<pre><b>.<br>.<br>.<br></b>";
while(<BUILD_IN>) {
next if $. < $first_line;
last if $. > $last_line;
print "<b><font color='red'>" if $. == $linenum;
print;
print "</font></b>" if $. == $linenum;
}
print "<b>.<br>.<br>.<br></b></pre>";
}
sub print_header {
print "Content-type: text/html\n\n";
if( $fulltext ){
$s = 'Show <b>Brief</b> Log';
$s1 = '';
$s2 = 'Full';
}
else {
$s = 'Show <b>Full</b> Log';
$s1 = 1;
$s2 = 'Brief';
}
print "<META HTTP-EQUIV=\"EXPIRES\" CONTENT=\"1\">\n";
my $heading = "Build Log ($s2)";
my $subheading = "$buildname on $time_str";
my $title = "$heading - $subheading";
EmitHtmlTitleAndHeader($title, $heading, $subheading);
print "
<font size=+1>
<dt><a href='showlog.cgi?tree=$tree&errorparser=$errorparser&logfile=$logfile&buildtime=$buildtime&buildname=$enc_buildname&fulltext=$s1'>$s</a>
<dt><a href=\"showbuilds.cgi?tree=$tree\">Return to the Build Page</a>
<dt><a href=\"addnote.cgi?tree=$tree\&buildname=$enc_buildname\&buildtime=$buildtime\&logfile=$logfile\&errorparser=$errorparser\">
Add a Comment to the Log</a>
</font>
";
}
sub print_notes {
#
# Print notes
#
$found_note = 0;
open(NOTES,"<$tree/notes.txt")
or print "<h2>warning: Couldn't open $tree/notes.txt </h2>\n";
print "$buildtime, $buildname<br>\n";
while(<NOTES>){
chop;
($nbuildtime,$nbuildname,$nwho,$nnow,$nenc_note) = split(/\|/);
#print "$_<br>\n";
if( $nbuildtime == $buildtime && $nbuildname eq $buildname ){
if( !$found_note ){
print "<H2>Build Comments</H2>\n";
$found_note = 1;
}
$now_str = &print_time($nnow);
$note = &url_decode($nenc_note);
print "<pre>\n[<b><a href=mailto:$nwho>$nwho</a> - $now_str</b>]\n$note\n</pre>";
}
}
close(NOTES);
}
sub print_summary {
#
# Print the summary first
#
logprint('<H2>Build Error Summary</H2><PRE>');
$log_line = 0;
open( BUILD_IN, "$gzip -d -c $tree/$logfile|" );
while( $line = <BUILD_IN> ){
&output_summary_line( $line );
}
close( BUILD_IN );
push @log_errors, 9999999;
logprint('</PRE>');
}
sub print_log_section {
my ($tree, $logfile, $line_of_interest, $num_lines) = shift;
local $_;
my $first_line = $line_of_interest - $num_lines / 2;
my $last_line = $first_line + $num_lines;
print "<a href='showlog.cgi?tree=$tree&logfile=$logfile&line="
.($line_of_interest-$num_lines)."&numlines=$num_lines'>"
."Previous $num_lines</a>";
print "<font size='+1'><b>.<br>.<br>.<br></b></font>";
print "<pre>";
my $ii = 0;
open BUILD_IN, "$gzip -d -c $tree/$logfile|";
while (<BUILD_IN>) {
$ii++;
next if $ii < $first_line;
last if $ii > $last_line;
if ($ii == $line_of_intested) {
print "<b>$_</b>";
} else {
print;
}
}
close BUILD_IN;
print "</pre>";
print "<font size='+1'><b>.<br>.<br>.<br></b></font>";
print "<a href='showlog.cgi?tree=$tree&logfile=$logfile&line="
.($line_of_interest+$num_lines)."&numlines=$num_lines'>"
."Next $num_lines</a>";
}
sub print_log {
#
# reset the error counter
#
$next_err = 0;
logprint('<H2>Build Error Log</H2><pre>');
$log_line = 0;
open( BUILD_IN, "$gzip -d -c $tree/$logfile|" );
while( $line = <BUILD_IN> ){
&output_log_line( $line );
}
close( BUILD_IN );
logprint('</PRE><p>'
."<font size=+1><a name=\"err$next_err\">No More Errors</a></font>"
.'<br><br><br>');
}
sub output_summary_line {
my( $line ) = $_[0];
my( $has_error );
$has_error = &has_error( $line );
$line =~ s/&/&amp;/g;
$line =~ s/</&lt;/g;
if( $has_error ){
push @log_errors, $log_line + $LINES_AFTER_ERROR;
if( ! $last_was_error ) {
logprint("<a href=\"#err$next_err\">$line</a>");
$next_err++;
}
$last_was_error = 1;
}
else {
$last_was_error = 0;
}
$log_line++;
}
sub output_log_line {
my( $line ) = $_[0];
my( $has_error, $dur, $dur_min,$dur_sec, $dur_str, $logline );
$has_error = &has_error( $line );
$has_warning = &has_warning( $line );
$line =~ s/&/&amp;/g;
$line =~ s/</&lt;/g;
$logline = '';
if( ($has_error || $has_warning) && &has_errorline( $line ) ) {
$q = quotemeta( $error_file );
#$goto_line = ($error_line ? 10 > $error_line - 10 : 1 );
$goto_line = ($error_line > 10 ? $error_line - 10 : 1 );
$cvsblame = ($error_guess ? "cvsguess.cgi" : "cvsblame.cgi");
$line =~ s@$q@<a href=../bonsai/$cvsblame?file=$error_file_ref&rev=$cvs_branch&mark=$error_line#$goto_line $source_target>$error_file</a>@
}
if( $has_error ){
if( ! $last_was_error ) {
$logline .= "<a name=\"err$next_err\"></a>";
$next_err++;
$logline .= "<a href=\"#err$next_err\">NEXT</a> ";
}
else {
$logline .= " ";
}
$logline .= "<font color=\"000080\">$line</font>";
$last_was_error = 1;
}
elsif( $has_warning ){
$logline .= " ";
$logline .= "<font color=000080>$line</font>";
}
else {
$logline .= " $line";
$last_was_error = 0;
}
&push_log_line( $logline );
}
sub push_log_line {
my( $line ) = $_[0];
if( $fulltext ){
logprint($line);
return;
}
if( $log_line > $log_errors[$cur_error] ){
$cur_error++;
}
if( $log_line >= $log_errors[$cur_error] - $LINES_BEFORE_ERROR ){
if( $log_skip != 0 ){
logprint("\n<i><font size=+1> Skipping $log_skip Lines...</i></font>\n\n");
$log_skip = 0;
}
logprint($line);
}
else {
$log_skip++;
}
$log_line++;
}
sub logprint {
my $line = $_[0];
print $line;
print BRIEFFILE $line if not $fulltext;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

View File

@@ -0,0 +1,75 @@
# The contents of this file are subject to the Mozilla 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/MPL/
#
# 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 the Tinderbox CVS Tool rpm spec file.
#
# The Initial Developer of this code under the MPL is Christopher
# Seawood, <cls@seawood.org>. Portions created by Christopher Seawood
# are Copyright (C) 1998 Christopher Seawood. All Rights Reserved.
%define ver SNAP
%define perl /usr/bin/perl
%define cvsroot /cvsroot
%define gzip /usr/bin/gzip
%define uudecode /usr/bin/uudecode
%define bonsai ../bonsai
%define prefix /home/httpd/html/tinderbox
# This rpm is not relocateable
Summary: automated build tool
Name: tinderbox
Version: %{ver}
Release: 1
Copyright: NPL
Group: Networking/Admin
Source: %{name}-%{ver}.tar.gz
BuildRoot: /var/tmp/build-%{name}
Requires: bonsai
Packager: Christopher Seawood <cls@seawood.org>
%changelog
* Thu Nov 12 1998 Christopher Seawood <cls@seawood.org>
- Replaced ver with SNAP
* Mon Oct 26 1998 Christopher Seawood <cls@seawood.org>
- Added MPL header
* Sun Aug 31 1998 Christopher Seawood <cls@seawood.org>
- Made rpm from cvs snapshot
%description
Essentially, Tinderbox is a detective tool. It allows you to see what
is happening in the source tree. It shows you who checked in what (by
asking Bonsai); what platforms have built successfully; what platforms
are broken and exactly how they are broken (the build logs); and the
state of the files that made up the build (cvsblame) so you can figure
out who broke the build, so you can do the most important thing, hold
them accountable for their actions.
%prep
%setup -n %{name}
%build
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p ${RPM_BUILD_ROOT}%{prefix}/{data,examples}
make install PERL=%perl UUDECODE=%uudecode GZIP=%gzip BONSAI=%bonsai CVSROOT=%cvsroot PREFIX=${RPM_BUILD_ROOT}%prefix
%post
echo "Remember to set the admin passwd via '%{bonsai}/data/trapdoor password > %{prefix}/data/passwd"
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr (-, nobody, nobody)
%doc README Makefile
%{prefix}

View File

@@ -0,0 +1,123 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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 the Tinderbox build tool.
#
# 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.
use lib '../bonsai';
require "globals.pl";
require 'lloydcgi.pl';
require 'header.pl';
use Date::Parse;
use Date::Format;
my $TIMEFORMAT = "%D %T";
$| = 1;
print "Content-type: text/html\n\n<HTML>\n";
my $tree = $form{'tree'};
my $start = $form{'start'};
my $end = $form{'end'};
sub str2timeAndCheck {
my ($str) = (@_);
my $result = str2time($str);
if (defined $result && $result > 7000000) {
return $result;
}
print "<p><font color=red>Can't parse as a date: $str</font><p>\n";
return 0;
}
my $header = "<table border=1><th>Build time</th><th>Build name</th><th>Who</th><th>Note time</th><th>Note</th>";
if (defined $tree && defined $start && defined $end) {
my $first = str2timeAndCheck($start);
my $last = str2timeAndCheck($end);
if ($first > 0 && $last > 0) {
if (open(IN, "<$tree/notes.txt")) {
print "<hr><center><h1>Notes for $tree</H1><H3>from " .
time2str($TIMEFORMAT, $first) . " to " .
time2str($TIMEFORMAT, $last) . "</H3></center>\n";
my %stats;
print "$header\n";
while (<IN>) {
chop;
my ($nbuildtime,$nbuildname,$nwho,$nnow,$nenc_note)
= split /\|/;
if ($nbuildtime >= $first && $nbuildtime <= $last) {
my $note = &url_decode($nenc_note);
$nbuildtime = print_time($nbuildtime);
$nnow = print_time($nnow);
print "<tr>";
print "<td>$nbuildtime</td>";
print "<td>$nbuildname</td>";
print "<td>$nwho</td>";
print "<td>$nnow</td>";
print "<td>$note</td>";
print "</tr>\n";
if (++$count % 100 == 0) {
print "</table>$header\n";
}
}
}
} else {
print "<p><font color=red>There does not appear to be a tree " .
"named '$tree'.</font><p>";
}
}
print "</table>\n";
}
if (!defined $tree) {
$tree = "";
}
if (!defined $start) {
$start = time2str($TIMEFORMAT, time() - 7*24*60*60); # One week ago.
}
if (!defined $end) {
$end = time2str($TIMEFORMAT, time()); # #now
}
print qq|
<form>
<table>
<tr>
<th align=right>Tree:</th>
<td><input name=tree size=30 value="$tree"></td>
</tr>
<tr>
<th align=right>Start time:</th>
<td><input name=start size=30 value="$start"></td>
</tr>
<tr>
<th align=right>End time:</th>
<td><input name=end size=30 value="$end"></td>
</tr>
</table>
<INPUT TYPE=\"submit\" VALUE=\"View Notes \">
</form>
|;

View File

@@ -0,0 +1,437 @@
#! /usr/bonsaitools/bin/perl
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Mozilla Public License
# Version 1.1 (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/MPL/
#
# 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 Tinderbox
#
# The Initial Developer of the Original Code is Netscape Communications
# Corporation. Portions created by Netscape are Copyright (C) 1999
# Netscape Communications Corporation. All Rights Reserved.
#
# Contributor(s): Stephen Lamm <slamm@mozilla.org>
use FileHandle;
$tree = 'SeaMonkey';
# tinderbox/globals.pl uses many shameful globals
$form{tree} = $tree;
require 'globals.pl';
$cvsroot = '/cvsroot/mozilla';
$lxr_data_root = '/export2/lxr-data';
@ignore = (
'long long',
'__cmsg_data',
'location of the previous definition',
'\' was hidden',
'declaration of \`index\'',
);
$ignore_pat = "(?:".join('|',@ignore).")";
print STDERR "Building hash of file names...";
($file_bases, $file_fullpaths) = build_file_hash($cvsroot, $tree);
print STDERR "done.\n";
for $br (last_successful_builds($tree)) {
next unless $br->{buildname} =~ /shrike.*\b(Clobber|Clbr)\b/;
my $log_file = "$br->{logfile}";
warn "Parsing build log, $log_file\n";
$fh = new FileHandle "gunzip -c $tree/$log_file |";
&gcc_parser($fh, $cvsroot, $tree, $log_file, $file_bases, $file_fullpaths);
$fh->close;
&build_blame;
my $warn_file = "$tree/warn$log_file";
$warn_file =~ s/.gz$/.html/;
$fh->open(">$warn_file") or die "Unable to open $warn_file: $!\n";
&print_warnings_as_html($fh, $br);
$fh->close;
warn "Wrote output to $warn_file\n";
last;
}
# end of main
# ===================================================================
sub build_file_hash {
my ($cvsroot, $tree) = @_;
$lxr_data_root = "/export2/lxr-data/\L$tree";
$lxr_file_list = "\L$lxr_data_root/.glimpse_filenames";
open(LXR_FILENAMES, "<$lxr_file_list")
or die "Unable to open $lxr_file_list: $!\n";
use File::Basename;
while (<LXR_FILENAMES>) {
my ($base, $dir, $ext) = fileparse($_,'\.[^/]*');
next unless $ext =~ /^\.(cpp|h|C|s|c|mk|in)$/;
$base = "$base$ext";
$dir =~ s|$lxr_data_root/mozilla/||;
$dir =~ s|/$||;
$fullpath{"$dir/$base"}=1;
unless (exists $bases{$base}) {
$bases{$base} = $dir;
} else {
$bases{$base} = '[multiple]';
}
}
return \%bases, \%fullpath;
}
sub last_successful_builds {
my $tree = shift;
my @build_records = ();
my $br;
$maxdate = time;
$mindate = $maxdate - 5*60*60; # Go back 5 hours
print STDERR "Loading build data...";
&load_data;
print STDERR "done\n";
for (my $ii=1; $ii <= $name_count; $ii++) {
for (my $tt=1; $tt <= $time_count; $tt++) {
if (defined($br = $build_table->[$tt][$ii])
and $br->{buildstatus} eq 'success') {
push @build_records, $br;
last;
} } }
return @build_records;
}
sub gcc_parser {
my ($fh, $cvsroot, $tree, $log_file, $file_bases, $file_fullnames) = @_;
my $dir = '';
while (<$fh>) {
# Directory
#
if (/^gmake\[\d\]: Entering directory \`(.*)\'$/) {
($build_dir = $1) =~ s|.*/mozilla/||;
next;
}
# Now only match lines with "warning:"
next unless /warning:/;
next if /$ignore_pat/o;
chomp; # Yum, yum
my ($filename, $line, $warning_text);
($filename, $line, undef, $warning_text) = split /:\s*/, $_, 4;
$filename =~ s/.*\///;
# Special case for Makefiles
$filename =~ s/Makefile$/Makefile.in/;
my $dir = '';
if ($file_fullnames->{"$build_dir/$filename"}) {
$dir = $build_dir;
} else {
unless(defined($dir = $file_bases->{$filename})) {
$dir = '[no_match]';
}
}
my $file = "$dir/$filename";
unless (defined($warnings{$file}{$line})) {
# Special case for "`foo' was hidden\nby `foo2'"
$warning_text = "...was hidden " . $warning_text
if $warning_text =~ /^by \`/;
# Remember where in the build log the warning occured
$warnings{$file}{$line} = {
first_seen_line => $.,
log_file => $log_file,
warning_text => $warning_text,
};
}
$warnings{$file}{$line}->{count}++;
$total_warnings_count++;
}
}
sub dump_warning_data {
while (my ($file, $lines_hash) = each %warnings) {
while (my ($line, $record) = each %{$lines_hash}) {
print join ':',
$file,$line,
$record->{first_seen_line},
$record->{count},
$record->{warning_text};
print "\n";
}
}
}
sub build_blame {
use lib '../bonsai';
require 'utils.pl';
require 'cvsblame.pl';
while (($file, $lines_hash) = each %warnings) {
my $rcs_filename = "$cvsroot/$file,v";
unless (-e $rcs_filename) {
warn "Unable to find $rcs_filename\n";
$unblamed{$file} = 1;
next;
}
my $revision = &parse_cvs_file($rcs_filename);
@text = &extract_revision($revision);
while (my ($line, $warn_rec) = each %{$lines_hash}) {
my $line_rev = $revision_map[$line-1];
my $who = $revision_author{$line_rev};
my $source_text = join '', @text[$line-3..$line+1];
$source_text =~ s/\t/ /g;
$who = "$who%netscape.com" unless $who =~ /[%]/;
$warn_rec->{line_rev} = $line_rev;
$warn_rec->{source} = $source_text;
$warnings_by_who{$who}{$file}{$line} = $warn_rec;
$total_who_count++ unless exists $who_count{$who};
$who_count{$who} += $warn_rec->{count};
}
}
}
sub print_warnings_as_html {
my ($fh, $br) = @_;
my ($buildname, $buildtime) = ($br->{buildname}, $br->{buildtime});
my $time_str = print_time( $buildtime );
# Change the default destination for print to $fh
my $old_fh = select($fh);
print <<"__END_HEADER";
<html>
<head>
<title>Blamed Build Warnings</title>
</head>
<body>
<font size="+2" face="Helvetica,Arial"><b>
Blamed Build Warnings
</b></font><br>
<font size="+1" face="Helvetica,Arial">
$buildname on $time_str<br>
$total_warnings_count total warnings
</font><p>
__END_HEADER
for $who (sort { $who_count{$b} <=> $who_count{$a}
|| $a cmp $b } keys %who_count) {
push @who_list, $who;
}
# Summary Table (name, count)
#
use POSIX;
print "<table border=0 cellpadding=1 cellspacing=0 bgcolor=#ededed>\n";
my $num_columns = 6;
my $num_rows = ceil($#who_list / $num_columns);
for (my $ii=0; $ii < $num_rows; $ii++) {
print "<tr>";
for (my $jj=0; $jj < $num_columns; $jj++) {
my $index = $ii + $jj * $num_rows;
next if $index > $#who_list;
my $name = $who_list[$index];
my $count = $who_count{$name};
$name =~ s/%.*//;
print " " x $jj;
print "<td><a href='#$name'>$name</a>";
print "</td><td>";
print "$count";
print "</td><td>&nbsp;</td>\n";
}
print "</tr>\n";
}
print "</table><p>\n";
# Count Unblamed warnings
#
my $total_unblamed_warnigns=0;
for my $file (keys %unblamed) {
for my $linenum (keys %{$warnings{$file}}) {
$total_unblamed_warnings += $warnings{$file}{$linenum}{count};
$warnings_by_who{Unblamed}{$file}{$linenum} = $warnings{$file}{$linenum};
}
}
$who_count{Unblamed} = $total_unblamed_warnings;
# Print all the warnings
#
for $who (@who_list, "Unblamed") {
my $total_count = $who_count{$who};
my ($name, $email);
($name = $who) =~ s/%.*//;
($email = $who) =~ s/%/@/;
print "<h2>";
print "<a name='$name' href='mailto:$email'>" unless $name eq 'Unblamed';
print "$name";
print "</a>" unless $name eq 'Unblamed';
print " (1 warning)" if $total_count == 1;
print " ($total_count warnings)" if $total_count > 1;
print "</h2>";
print "\n<table>\n";
my $count = 1;
for $file (sort keys %{$warnings_by_who{$who}}) {
for $linenum (sort keys %{$warnings_by_who{$who}{$file}}) {
my $warn_rec = $warnings_by_who{$who}{$file}{$linenum};
print_count($count, $warn_rec->{count});
print_warning($tree, $br, $file, $linenum, $warn_rec);
print_source_code($linenum, $warn_rec) unless $unblamed{$file};
$count += $warn_rec->{count};
}
}
print "</table>\n";
}
print <<"__END_FOOTER";
<p>
<hr align=left>
Send questions or comments to
&lt;<a href="mailto:slamm\@netscape.com?subject=About the Blamed Build Warnings">slamm\@netcape.com</a>&gt;.
</body></html>
__END_FOOTER
# Change default destination back.
select($old_fh);
}
sub print_count {
my ($start, $count) = @_;
print "<tr><td align=right>$start";
print "-".($start+$count-1) if $count > 1;
print ".</td>";
}
sub print_warning {
my ($tree, $br, $file, $linenum, $warn_rec) = @_;
my $warning = $warn_rec->{warning_text};
print "<td>";
# File link
if ($file =~ /\[multiple\]/) {
$file =~ s/\[multiple\]\///;
print "<a href='http://lxr.mozilla.org/seamonkey/find?string=$file'>";
print "$file:$linenum";
print "</a> (multiple file matches)";
} elsif ($file =~ /\[no_match\]/) {
$file =~ s/\[no_match\]\///;
print "<b>$file:$linenum</b> (no file match)";
} else {
print "<a href='"
.file_url($file,$linenum)."'>";
print "$file:$linenum";
print "</a> ";
}
print "</td></tr><tr><td></td><td>";
# Warning text
print "\u$warning";
# Build log link
my $log_line = $warn_rec->{first_seen_line};
print " (<a href='"
.build_url($tree, $br, $log_line)
."'>";
if ($warn_rec->{count} == 1) {
print "See build log excerpt";
} else {
print "See 1st of $warn_rec->{count} occurrences in build log";
}
print "</a>)</td></tr>";
}
sub print_source_code {
my ($linenum, $warn_rec) = @_;
my $warning = $warn_rec->{warning_text};
# Source code fragment
#
my ($keyword) = $warning =~ /\`([^\']*)\'/;
print "<tr><td></td><td bgcolor=#ededed>";
print "<pre><font size='-1'>";
my $source_text = trim_common_leading_whitespace($warn_rec->{source});
$source_text =~ s/&/&amp;/gm;
$source_text =~ s/</&lt;/gm;
$source_text =~ s/>/&gt;/gm;
$source_text =~ s|\b\Q$keyword\E\b|<b>$keyword</b>|gm unless $keyword eq '';
my $line_index = $linenum - 2;
$source_text =~ s/^(.*)$/$line_index++." $1"/gme;
$source_text =~ s|^($linenum.*)$|<font color='red'>\1</font>|gm;
chomp $source_text;
print $source_text;
print "</font>";
#print "</pre>";
print "</td></tr>\n";
}
sub build_url {
my ($tree, $br, $linenum) = @_;
my $name = $br->{buildname};
$name =~ s/ /%20/g;
return "../showlog.cgi?log=$tree/$br->{logfile}:$linenum";
}
sub file_url {
my ($file, $linenum) = @_;
return "http://cvs-mirror.mozilla.org/webtools/bonsai/cvsblame.cgi"
."?file=mozilla/$file&mark=$linenum#".($linenum-10);
}
sub trim_common_leading_whitespace {
# Adapted from dequote() in Perl Cookbook by Christiansen and Torkington
local $_ = shift;
my $white; # common whitespace
if (/(?:(\s*).*\n)(?:(?:\1.*\n)|(?:\s*\n))+$/) {
$white = $1;
} else {
$white = /^(\s+)/;
}
s/^(?:$white)?//gm;
s/^\s+$//gm;
return $_;
}