2004-08-24 23:00:15 +02:00
|
|
|
/*
|
|
|
|
* ITSS Storage implementation
|
|
|
|
*
|
|
|
|
* Copyright 2004 Mike McCormack
|
|
|
|
*
|
|
|
|
* see http://bonedaddy.net/pabs3/hhm/#chmspec
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
2006-05-18 14:49:52 +02:00
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
2004-08-24 23:00:15 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
2004-10-07 05:06:48 +02:00
|
|
|
#define COBJMACROS
|
|
|
|
|
2004-08-24 23:00:15 +02:00
|
|
|
#include "windef.h"
|
|
|
|
#include "winbase.h"
|
|
|
|
#include "winuser.h"
|
|
|
|
#include "ole2.h"
|
|
|
|
|
|
|
|
#include "chm_lib.h"
|
2004-08-25 19:30:18 +02:00
|
|
|
#include "itsstor.h"
|
2004-08-24 23:00:15 +02:00
|
|
|
|
2005-08-25 12:25:14 +02:00
|
|
|
#include "wine/itss.h"
|
2004-08-24 23:00:15 +02:00
|
|
|
#include "wine/unicode.h"
|
|
|
|
#include "wine/debug.h"
|
|
|
|
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(itss);
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
|
|
|
|
|
|
typedef struct _ITSS_IStorageImpl
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
IStorage IStorage_iface;
|
2005-07-13 13:59:15 +02:00
|
|
|
LONG ref;
|
2004-08-24 23:00:15 +02:00
|
|
|
struct chmFile *chmfile;
|
|
|
|
WCHAR dir[1];
|
|
|
|
} ITSS_IStorageImpl;
|
|
|
|
|
|
|
|
struct enum_info
|
|
|
|
{
|
|
|
|
struct enum_info *next, *prev;
|
|
|
|
struct chmUnitInfo ui;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct _IEnumSTATSTG_Impl
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
IEnumSTATSTG IEnumSTATSTG_iface;
|
2005-07-13 13:59:15 +02:00
|
|
|
LONG ref;
|
2004-08-24 23:00:15 +02:00
|
|
|
struct enum_info *first, *last, *current;
|
|
|
|
} IEnumSTATSTG_Impl;
|
|
|
|
|
|
|
|
typedef struct _IStream_Impl
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
IStream IStream_iface;
|
2005-07-13 13:59:15 +02:00
|
|
|
LONG ref;
|
2004-08-24 23:00:15 +02:00
|
|
|
ITSS_IStorageImpl *stg;
|
|
|
|
ULONGLONG addr;
|
|
|
|
struct chmUnitInfo ui;
|
|
|
|
} IStream_Impl;
|
|
|
|
|
2010-12-08 22:59:31 +01:00
|
|
|
static inline ITSS_IStorageImpl *impl_from_IStorage(IStorage *iface)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(iface, ITSS_IStorageImpl, IStorage_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline IEnumSTATSTG_Impl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(iface, IEnumSTATSTG_Impl, IEnumSTATSTG_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline IStream_Impl *impl_from_IStream(IStream *iface)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(iface, IStream_Impl, IStream_iface);
|
|
|
|
}
|
|
|
|
|
2004-08-24 23:00:15 +02:00
|
|
|
static HRESULT ITSS_create_chm_storage(
|
|
|
|
struct chmFile *chmfile, const WCHAR *dir, IStorage** ppstgOpen );
|
|
|
|
static IStream_Impl* ITSS_create_stream(
|
|
|
|
ITSS_IStorageImpl *stg, struct chmUnitInfo *ui );
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
|
|
|
|
|
|
static HRESULT WINAPI ITSS_IEnumSTATSTG_QueryInterface(
|
|
|
|
IEnumSTATSTG* iface,
|
|
|
|
REFIID riid,
|
|
|
|
void** ppvObject)
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
IEnumSTATSTG_Impl *This = impl_from_IEnumSTATSTG(iface);
|
2004-08-24 23:00:15 +02:00
|
|
|
|
|
|
|
if (IsEqualGUID(riid, &IID_IUnknown)
|
|
|
|
|| IsEqualGUID(riid, &IID_IEnumSTATSTG))
|
|
|
|
{
|
|
|
|
IEnumSTATSTG_AddRef(iface);
|
|
|
|
*ppvObject = This;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI ITSS_IEnumSTATSTG_AddRef(
|
|
|
|
IEnumSTATSTG* iface)
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
IEnumSTATSTG_Impl *This = impl_from_IEnumSTATSTG(iface);
|
2005-01-03 15:39:51 +01:00
|
|
|
return InterlockedIncrement(&This->ref);
|
2004-08-24 23:00:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI ITSS_IEnumSTATSTG_Release(
|
|
|
|
IEnumSTATSTG* iface)
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
IEnumSTATSTG_Impl *This = impl_from_IEnumSTATSTG(iface);
|
2004-08-24 23:00:15 +02:00
|
|
|
|
2005-01-03 15:39:51 +01:00
|
|
|
ULONG ref = InterlockedDecrement(&This->ref);
|
2004-08-24 23:00:15 +02:00
|
|
|
|
|
|
|
if (ref == 0)
|
|
|
|
{
|
|
|
|
while( This->first )
|
|
|
|
{
|
|
|
|
struct enum_info *t = This->first->next;
|
|
|
|
HeapFree( GetProcessHeap(), 0, This->first );
|
|
|
|
This->first = t;
|
|
|
|
}
|
2005-01-03 15:39:51 +01:00
|
|
|
HeapFree(GetProcessHeap(), 0, This);
|
2006-12-23 23:51:55 +01:00
|
|
|
ITSS_UnlockModule();
|
2004-08-24 23:00:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return ref;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI ITSS_IEnumSTATSTG_Next(
|
|
|
|
IEnumSTATSTG* iface,
|
|
|
|
ULONG celt,
|
|
|
|
STATSTG* rgelt,
|
|
|
|
ULONG* pceltFetched)
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
IEnumSTATSTG_Impl *This = impl_from_IEnumSTATSTG(iface);
|
2004-08-24 23:00:15 +02:00
|
|
|
DWORD len, n;
|
|
|
|
struct enum_info *cur;
|
|
|
|
|
2006-10-12 20:56:50 +02:00
|
|
|
TRACE("%p %u %p %p\n", This, celt, rgelt, pceltFetched );
|
2004-08-24 23:00:15 +02:00
|
|
|
|
|
|
|
cur = This->current;
|
|
|
|
n = 0;
|
|
|
|
while( (n<celt) && cur)
|
|
|
|
{
|
|
|
|
WCHAR *str;
|
|
|
|
|
|
|
|
memset( rgelt, 0, sizeof *rgelt );
|
|
|
|
|
|
|
|
/* copy the name */
|
|
|
|
str = cur->ui.path;
|
|
|
|
if( *str == '/' )
|
|
|
|
str++;
|
|
|
|
len = strlenW( str ) + 1;
|
|
|
|
rgelt->pwcsName = CoTaskMemAlloc( len*sizeof(WCHAR) );
|
|
|
|
strcpyW( rgelt->pwcsName, str );
|
|
|
|
|
|
|
|
/* determine the type */
|
|
|
|
if( rgelt->pwcsName[len-2] == '/' )
|
|
|
|
{
|
|
|
|
rgelt->pwcsName[len-2] = 0;
|
|
|
|
rgelt->type = STGTY_STORAGE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
rgelt->type = STGTY_STREAM;
|
|
|
|
|
|
|
|
/* copy the size */
|
|
|
|
rgelt->cbSize.QuadPart = cur->ui.length;
|
|
|
|
|
|
|
|
/* advance to the next item if it exists */
|
|
|
|
n++;
|
|
|
|
cur = cur->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
This->current = cur;
|
|
|
|
*pceltFetched = n;
|
|
|
|
|
|
|
|
if( n < celt )
|
|
|
|
return S_FALSE;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI ITSS_IEnumSTATSTG_Skip(
|
|
|
|
IEnumSTATSTG* iface,
|
|
|
|
ULONG celt)
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
IEnumSTATSTG_Impl *This = impl_from_IEnumSTATSTG(iface);
|
2004-08-24 23:00:15 +02:00
|
|
|
DWORD n;
|
|
|
|
struct enum_info *cur;
|
|
|
|
|
2006-10-12 20:56:50 +02:00
|
|
|
TRACE("%p %u\n", This, celt );
|
2004-08-24 23:00:15 +02:00
|
|
|
|
|
|
|
cur = This->current;
|
|
|
|
n = 0;
|
|
|
|
while( (n<celt) && cur)
|
|
|
|
{
|
|
|
|
n++;
|
|
|
|
cur = cur->next;
|
|
|
|
}
|
|
|
|
This->current = cur;
|
|
|
|
|
|
|
|
if( n < celt )
|
|
|
|
return S_FALSE;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI ITSS_IEnumSTATSTG_Reset(
|
|
|
|
IEnumSTATSTG* iface)
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
IEnumSTATSTG_Impl *This = impl_from_IEnumSTATSTG(iface);
|
2004-08-24 23:00:15 +02:00
|
|
|
|
|
|
|
TRACE("%p\n", This );
|
|
|
|
|
|
|
|
This->current = This->first;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI ITSS_IEnumSTATSTG_Clone(
|
|
|
|
IEnumSTATSTG* iface,
|
|
|
|
IEnumSTATSTG** ppenum)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
2005-06-01 21:57:42 +02:00
|
|
|
static const IEnumSTATSTGVtbl IEnumSTATSTG_vtbl =
|
2004-08-24 23:00:15 +02:00
|
|
|
{
|
|
|
|
ITSS_IEnumSTATSTG_QueryInterface,
|
|
|
|
ITSS_IEnumSTATSTG_AddRef,
|
|
|
|
ITSS_IEnumSTATSTG_Release,
|
|
|
|
ITSS_IEnumSTATSTG_Next,
|
|
|
|
ITSS_IEnumSTATSTG_Skip,
|
|
|
|
ITSS_IEnumSTATSTG_Reset,
|
|
|
|
ITSS_IEnumSTATSTG_Clone
|
|
|
|
};
|
|
|
|
|
|
|
|
static IEnumSTATSTG_Impl *ITSS_create_enum( void )
|
|
|
|
{
|
|
|
|
IEnumSTATSTG_Impl *stgenum;
|
|
|
|
|
|
|
|
stgenum = HeapAlloc( GetProcessHeap(), 0, sizeof (IEnumSTATSTG_Impl) );
|
2010-12-08 22:59:31 +01:00
|
|
|
stgenum->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTG_vtbl;
|
2004-08-24 23:00:15 +02:00
|
|
|
stgenum->ref = 1;
|
|
|
|
stgenum->first = NULL;
|
|
|
|
stgenum->last = NULL;
|
|
|
|
stgenum->current = NULL;
|
|
|
|
|
2006-12-23 23:51:55 +01:00
|
|
|
ITSS_LockModule();
|
2004-08-24 23:00:15 +02:00
|
|
|
TRACE(" -> %p\n", stgenum );
|
|
|
|
|
|
|
|
return stgenum;
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
|
|
|
2005-06-20 12:30:15 +02:00
|
|
|
static HRESULT WINAPI ITSS_IStorageImpl_QueryInterface(
|
2004-08-24 23:00:15 +02:00
|
|
|
IStorage* iface,
|
|
|
|
REFIID riid,
|
|
|
|
void** ppvObject)
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
ITSS_IStorageImpl *This = impl_from_IStorage(iface);
|
2004-08-24 23:00:15 +02:00
|
|
|
|
|
|
|
if (IsEqualGUID(riid, &IID_IUnknown)
|
|
|
|
|| IsEqualGUID(riid, &IID_IStorage))
|
|
|
|
{
|
|
|
|
IStorage_AddRef(iface);
|
|
|
|
*ppvObject = This;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
2005-06-20 12:30:15 +02:00
|
|
|
static ULONG WINAPI ITSS_IStorageImpl_AddRef(
|
2004-08-24 23:00:15 +02:00
|
|
|
IStorage* iface)
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
ITSS_IStorageImpl *This = impl_from_IStorage(iface);
|
2005-01-03 15:39:51 +01:00
|
|
|
return InterlockedIncrement(&This->ref);
|
2004-08-24 23:00:15 +02:00
|
|
|
}
|
|
|
|
|
2005-06-20 12:30:15 +02:00
|
|
|
static ULONG WINAPI ITSS_IStorageImpl_Release(
|
2004-08-24 23:00:15 +02:00
|
|
|
IStorage* iface)
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
ITSS_IStorageImpl *This = impl_from_IStorage(iface);
|
2004-08-24 23:00:15 +02:00
|
|
|
|
2005-01-03 15:39:51 +01:00
|
|
|
ULONG ref = InterlockedDecrement(&This->ref);
|
2004-08-24 23:00:15 +02:00
|
|
|
|
|
|
|
if (ref == 0)
|
|
|
|
{
|
2007-01-13 12:47:27 +01:00
|
|
|
chm_close(This->chmfile);
|
2005-01-03 15:39:51 +01:00
|
|
|
HeapFree(GetProcessHeap(), 0, This);
|
2006-12-23 23:51:55 +01:00
|
|
|
ITSS_UnlockModule();
|
2004-08-24 23:00:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return ref;
|
|
|
|
}
|
|
|
|
|
2005-06-20 12:30:15 +02:00
|
|
|
static HRESULT WINAPI ITSS_IStorageImpl_CreateStream(
|
2004-08-24 23:00:15 +02:00
|
|
|
IStorage* iface,
|
|
|
|
LPCOLESTR pwcsName,
|
|
|
|
DWORD grfMode,
|
|
|
|
DWORD reserved1,
|
|
|
|
DWORD reserved2,
|
|
|
|
IStream** ppstm)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
2005-06-20 12:30:15 +02:00
|
|
|
static HRESULT WINAPI ITSS_IStorageImpl_OpenStream(
|
2004-08-24 23:00:15 +02:00
|
|
|
IStorage* iface,
|
|
|
|
LPCOLESTR pwcsName,
|
|
|
|
void* reserved1,
|
|
|
|
DWORD grfMode,
|
|
|
|
DWORD reserved2,
|
|
|
|
IStream** ppstm)
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
ITSS_IStorageImpl *This = impl_from_IStorage(iface);
|
2004-08-24 23:00:15 +02:00
|
|
|
IStream_Impl *stm;
|
|
|
|
DWORD len;
|
|
|
|
struct chmUnitInfo ui;
|
|
|
|
int r;
|
2007-02-22 21:08:47 +01:00
|
|
|
WCHAR *path, *p;
|
2004-08-24 23:00:15 +02:00
|
|
|
|
2006-10-12 20:56:50 +02:00
|
|
|
TRACE("%p %s %p %u %u %p\n", This, debugstr_w(pwcsName),
|
2004-08-24 23:00:15 +02:00
|
|
|
reserved1, grfMode, reserved2, ppstm );
|
|
|
|
|
|
|
|
len = strlenW( This->dir ) + strlenW( pwcsName ) + 1;
|
|
|
|
path = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
|
|
|
|
strcpyW( path, This->dir );
|
2007-02-22 21:08:47 +01:00
|
|
|
|
|
|
|
if( pwcsName[0] == '/' || pwcsName[0] == '\\' )
|
2004-08-25 19:30:18 +02:00
|
|
|
{
|
2007-02-22 21:08:47 +01:00
|
|
|
p = &path[strlenW( path ) - 1];
|
2004-12-13 13:04:14 +01:00
|
|
|
while( ( path <= p ) && ( *p == '/' ) )
|
2004-08-25 19:30:18 +02:00
|
|
|
*p-- = 0;
|
|
|
|
}
|
2004-08-24 23:00:15 +02:00
|
|
|
strcatW( path, pwcsName );
|
|
|
|
|
2007-02-22 21:08:47 +01:00
|
|
|
for(p=path; *p; p++) {
|
|
|
|
if(*p == '\\')
|
|
|
|
*p = '/';
|
|
|
|
}
|
|
|
|
|
2007-03-13 18:41:22 +01:00
|
|
|
if(*--p == '/')
|
|
|
|
*p = 0;
|
|
|
|
|
2004-08-25 19:30:18 +02:00
|
|
|
TRACE("Resolving %s\n", debugstr_w(path));
|
|
|
|
|
2004-08-24 23:00:15 +02:00
|
|
|
r = chm_resolve_object(This->chmfile, path, &ui);
|
|
|
|
HeapFree( GetProcessHeap(), 0, path );
|
|
|
|
|
2007-02-22 21:08:47 +01:00
|
|
|
if( r != CHM_RESOLVE_SUCCESS ) {
|
|
|
|
WARN("Could not resolve object\n");
|
2004-08-24 23:00:15 +02:00
|
|
|
return STG_E_FILENOTFOUND;
|
2007-02-22 21:08:47 +01:00
|
|
|
}
|
2004-08-24 23:00:15 +02:00
|
|
|
|
|
|
|
stm = ITSS_create_stream( This, &ui );
|
|
|
|
if( !stm )
|
|
|
|
return E_FAIL;
|
|
|
|
|
2010-12-08 22:59:31 +01:00
|
|
|
*ppstm = &stm->IStream_iface;
|
2004-08-24 23:00:15 +02:00
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2005-06-20 12:30:15 +02:00
|
|
|
static HRESULT WINAPI ITSS_IStorageImpl_CreateStorage(
|
2004-08-24 23:00:15 +02:00
|
|
|
IStorage* iface,
|
|
|
|
LPCOLESTR pwcsName,
|
|
|
|
DWORD grfMode,
|
|
|
|
DWORD dwStgFmt,
|
|
|
|
DWORD reserved2,
|
|
|
|
IStorage** ppstg)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
2005-06-20 12:30:15 +02:00
|
|
|
static HRESULT WINAPI ITSS_IStorageImpl_OpenStorage(
|
2004-08-24 23:00:15 +02:00
|
|
|
IStorage* iface,
|
|
|
|
LPCOLESTR pwcsName,
|
|
|
|
IStorage* pstgPriority,
|
|
|
|
DWORD grfMode,
|
|
|
|
SNB snbExclude,
|
|
|
|
DWORD reserved,
|
|
|
|
IStorage** ppstg)
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
ITSS_IStorageImpl *This = impl_from_IStorage(iface);
|
2010-02-07 18:32:27 +01:00
|
|
|
struct chmFile *chmfile;
|
|
|
|
WCHAR *path, *p;
|
|
|
|
DWORD len;
|
2004-08-25 19:30:18 +02:00
|
|
|
|
2010-02-07 18:32:27 +01:00
|
|
|
TRACE("%p %s %p %u %p %u %p\n", This, debugstr_w(pwcsName),
|
2004-08-25 19:30:18 +02:00
|
|
|
pstgPriority, grfMode, snbExclude, reserved, ppstg);
|
2010-02-07 18:32:27 +01:00
|
|
|
|
2010-02-09 23:01:26 +01:00
|
|
|
chmfile = chm_dup( This->chmfile );
|
|
|
|
if( !chmfile )
|
|
|
|
return E_FAIL;
|
|
|
|
|
2012-01-27 17:48:47 +01:00
|
|
|
len = strlenW( This->dir ) + strlenW( pwcsName ) + 2; /* need room for a terminating slash */
|
2010-02-07 18:32:27 +01:00
|
|
|
path = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
|
|
|
|
strcpyW( path, This->dir );
|
|
|
|
|
|
|
|
if( pwcsName[0] == '/' || pwcsName[0] == '\\' )
|
|
|
|
{
|
|
|
|
p = &path[strlenW( path ) - 1];
|
|
|
|
while( ( path <= p ) && ( *p == '/' ) )
|
|
|
|
*p-- = 0;
|
|
|
|
}
|
|
|
|
strcatW( path, pwcsName );
|
|
|
|
|
|
|
|
for(p=path; *p; p++) {
|
|
|
|
if(*p == '\\')
|
|
|
|
*p = '/';
|
|
|
|
}
|
|
|
|
|
2012-01-27 17:48:47 +01:00
|
|
|
/* add a terminating slash if one does not already exist */
|
|
|
|
if(*(p-1) != '/')
|
|
|
|
{
|
|
|
|
*p++ = '/';
|
2010-02-07 18:32:27 +01:00
|
|
|
*p = 0;
|
2012-01-27 17:48:47 +01:00
|
|
|
}
|
2010-02-07 18:32:27 +01:00
|
|
|
|
|
|
|
TRACE("Resolving %s\n", debugstr_w(path));
|
|
|
|
|
|
|
|
return ITSS_create_chm_storage(chmfile, path, ppstg);
|
2004-08-24 23:00:15 +02:00
|
|
|
}
|
|
|
|
|
2005-06-20 12:30:15 +02:00
|
|
|
static HRESULT WINAPI ITSS_IStorageImpl_CopyTo(
|
2004-08-24 23:00:15 +02:00
|
|
|
IStorage* iface,
|
|
|
|
DWORD ciidExclude,
|
|
|
|
const IID* rgiidExclude,
|
|
|
|
SNB snbExclude,
|
|
|
|
IStorage* pstgDest)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
2005-06-20 12:30:15 +02:00
|
|
|
static HRESULT WINAPI ITSS_IStorageImpl_MoveElementTo(
|
2004-08-24 23:00:15 +02:00
|
|
|
IStorage* iface,
|
|
|
|
LPCOLESTR pwcsName,
|
|
|
|
IStorage* pstgDest,
|
|
|
|
LPCOLESTR pwcsNewName,
|
|
|
|
DWORD grfFlags)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
2005-06-20 12:30:15 +02:00
|
|
|
static HRESULT WINAPI ITSS_IStorageImpl_Commit(
|
2004-08-24 23:00:15 +02:00
|
|
|
IStorage* iface,
|
|
|
|
DWORD grfCommitFlags)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
2005-06-20 12:30:15 +02:00
|
|
|
static HRESULT WINAPI ITSS_IStorageImpl_Revert(
|
2004-08-24 23:00:15 +02:00
|
|
|
IStorage* iface)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ITSS_chm_enumerator(
|
|
|
|
struct chmFile *h,
|
|
|
|
struct chmUnitInfo *ui,
|
|
|
|
void *context)
|
|
|
|
{
|
|
|
|
struct enum_info *info;
|
|
|
|
IEnumSTATSTG_Impl* stgenum = context;
|
|
|
|
|
|
|
|
TRACE("adding %s to enumeration\n", debugstr_w(ui->path) );
|
|
|
|
|
|
|
|
info = HeapAlloc( GetProcessHeap(), 0, sizeof (struct enum_info) );
|
2008-02-29 23:06:59 +01:00
|
|
|
info->ui = *ui;
|
2004-08-24 23:00:15 +02:00
|
|
|
|
|
|
|
info->next = NULL;
|
|
|
|
info->prev = stgenum->last;
|
|
|
|
if( stgenum->last )
|
|
|
|
stgenum->last->next = info;
|
|
|
|
else
|
|
|
|
stgenum->first = info;
|
|
|
|
stgenum->last = info;
|
|
|
|
|
|
|
|
return CHM_ENUMERATOR_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2005-06-20 12:30:15 +02:00
|
|
|
static HRESULT WINAPI ITSS_IStorageImpl_EnumElements(
|
2004-08-24 23:00:15 +02:00
|
|
|
IStorage* iface,
|
|
|
|
DWORD reserved1,
|
|
|
|
void* reserved2,
|
|
|
|
DWORD reserved3,
|
|
|
|
IEnumSTATSTG** ppenum)
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
ITSS_IStorageImpl *This = impl_from_IStorage(iface);
|
2004-08-24 23:00:15 +02:00
|
|
|
IEnumSTATSTG_Impl* stgenum;
|
|
|
|
|
2006-10-12 20:56:50 +02:00
|
|
|
TRACE("%p %d %p %d %p\n", This, reserved1, reserved2, reserved3, ppenum );
|
2004-08-24 23:00:15 +02:00
|
|
|
|
|
|
|
stgenum = ITSS_create_enum();
|
|
|
|
if( !stgenum )
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
chm_enumerate_dir(This->chmfile,
|
|
|
|
This->dir,
|
|
|
|
CHM_ENUMERATE_ALL,
|
|
|
|
ITSS_chm_enumerator,
|
|
|
|
stgenum );
|
|
|
|
|
|
|
|
stgenum->current = stgenum->first;
|
|
|
|
|
2010-12-08 22:59:31 +01:00
|
|
|
*ppenum = &stgenum->IEnumSTATSTG_iface;
|
2004-08-24 23:00:15 +02:00
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2005-06-20 12:30:15 +02:00
|
|
|
static HRESULT WINAPI ITSS_IStorageImpl_DestroyElement(
|
2004-08-24 23:00:15 +02:00
|
|
|
IStorage* iface,
|
|
|
|
LPCOLESTR pwcsName)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
2005-06-20 12:30:15 +02:00
|
|
|
static HRESULT WINAPI ITSS_IStorageImpl_RenameElement(
|
2004-08-24 23:00:15 +02:00
|
|
|
IStorage* iface,
|
|
|
|
LPCOLESTR pwcsOldName,
|
|
|
|
LPCOLESTR pwcsNewName)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
2005-06-20 12:30:15 +02:00
|
|
|
static HRESULT WINAPI ITSS_IStorageImpl_SetElementTimes(
|
2004-08-24 23:00:15 +02:00
|
|
|
IStorage* iface,
|
|
|
|
LPCOLESTR pwcsName,
|
|
|
|
const FILETIME* pctime,
|
|
|
|
const FILETIME* patime,
|
|
|
|
const FILETIME* pmtime)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
2005-06-20 12:30:15 +02:00
|
|
|
static HRESULT WINAPI ITSS_IStorageImpl_SetClass(
|
2004-08-24 23:00:15 +02:00
|
|
|
IStorage* iface,
|
|
|
|
REFCLSID clsid)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
2005-06-20 12:30:15 +02:00
|
|
|
static HRESULT WINAPI ITSS_IStorageImpl_SetStateBits(
|
2004-08-24 23:00:15 +02:00
|
|
|
IStorage* iface,
|
|
|
|
DWORD grfStateBits,
|
|
|
|
DWORD grfMask)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
2005-06-20 12:30:15 +02:00
|
|
|
static HRESULT WINAPI ITSS_IStorageImpl_Stat(
|
2004-08-24 23:00:15 +02:00
|
|
|
IStorage* iface,
|
|
|
|
STATSTG* pstatstg,
|
|
|
|
DWORD grfStatFlag)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
2005-06-01 21:57:42 +02:00
|
|
|
static const IStorageVtbl ITSS_IStorageImpl_Vtbl =
|
2004-08-24 23:00:15 +02:00
|
|
|
{
|
|
|
|
ITSS_IStorageImpl_QueryInterface,
|
|
|
|
ITSS_IStorageImpl_AddRef,
|
|
|
|
ITSS_IStorageImpl_Release,
|
|
|
|
ITSS_IStorageImpl_CreateStream,
|
|
|
|
ITSS_IStorageImpl_OpenStream,
|
|
|
|
ITSS_IStorageImpl_CreateStorage,
|
|
|
|
ITSS_IStorageImpl_OpenStorage,
|
|
|
|
ITSS_IStorageImpl_CopyTo,
|
|
|
|
ITSS_IStorageImpl_MoveElementTo,
|
|
|
|
ITSS_IStorageImpl_Commit,
|
|
|
|
ITSS_IStorageImpl_Revert,
|
|
|
|
ITSS_IStorageImpl_EnumElements,
|
|
|
|
ITSS_IStorageImpl_DestroyElement,
|
|
|
|
ITSS_IStorageImpl_RenameElement,
|
|
|
|
ITSS_IStorageImpl_SetElementTimes,
|
|
|
|
ITSS_IStorageImpl_SetClass,
|
|
|
|
ITSS_IStorageImpl_SetStateBits,
|
|
|
|
ITSS_IStorageImpl_Stat,
|
|
|
|
};
|
|
|
|
|
|
|
|
static HRESULT ITSS_create_chm_storage(
|
|
|
|
struct chmFile *chmfile, const WCHAR *dir, IStorage** ppstgOpen )
|
|
|
|
{
|
|
|
|
ITSS_IStorageImpl *stg;
|
|
|
|
|
|
|
|
TRACE("%p %s\n", chmfile, debugstr_w( dir ) );
|
|
|
|
|
2012-12-10 10:31:41 +01:00
|
|
|
stg = HeapAlloc( GetProcessHeap(), 0,
|
|
|
|
FIELD_OFFSET( ITSS_IStorageImpl, dir[strlenW( dir ) + 1] ));
|
2010-12-08 22:59:31 +01:00
|
|
|
stg->IStorage_iface.lpVtbl = &ITSS_IStorageImpl_Vtbl;
|
2004-08-24 23:00:15 +02:00
|
|
|
stg->ref = 1;
|
|
|
|
stg->chmfile = chmfile;
|
|
|
|
strcpyW( stg->dir, dir );
|
|
|
|
|
2010-12-08 22:59:31 +01:00
|
|
|
*ppstgOpen = &stg->IStorage_iface;
|
2004-08-24 23:00:15 +02:00
|
|
|
|
2006-12-23 23:51:55 +01:00
|
|
|
ITSS_LockModule();
|
2004-08-24 23:00:15 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT ITSS_StgOpenStorage(
|
|
|
|
const WCHAR* pwcsName,
|
|
|
|
IStorage* pstgPriority,
|
|
|
|
DWORD grfMode,
|
|
|
|
SNB snbExclude,
|
|
|
|
DWORD reserved,
|
|
|
|
IStorage** ppstgOpen)
|
|
|
|
{
|
|
|
|
struct chmFile *chmfile;
|
|
|
|
static const WCHAR szRoot[] = { '/', 0 };
|
|
|
|
|
|
|
|
TRACE("%s\n", debugstr_w(pwcsName) );
|
|
|
|
|
|
|
|
chmfile = chm_openW( pwcsName );
|
|
|
|
if( !chmfile )
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
return ITSS_create_chm_storage( chmfile, szRoot, ppstgOpen );
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
|
|
|
|
|
|
static HRESULT WINAPI ITSS_IStream_QueryInterface(
|
|
|
|
IStream* iface,
|
|
|
|
REFIID riid,
|
|
|
|
void** ppvObject)
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
IStream_Impl *This = impl_from_IStream(iface);
|
2004-08-24 23:00:15 +02:00
|
|
|
|
|
|
|
if (IsEqualGUID(riid, &IID_IUnknown)
|
|
|
|
|| IsEqualGUID(riid, &IID_ISequentialStream)
|
|
|
|
|| IsEqualGUID(riid, &IID_IStream))
|
|
|
|
{
|
|
|
|
IStream_AddRef(iface);
|
|
|
|
*ppvObject = This;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI ITSS_IStream_AddRef(
|
|
|
|
IStream* iface)
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
IStream_Impl *This = impl_from_IStream(iface);
|
2005-01-03 15:39:51 +01:00
|
|
|
return InterlockedIncrement(&This->ref);
|
2004-08-24 23:00:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI ITSS_IStream_Release(
|
|
|
|
IStream* iface)
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
IStream_Impl *This = impl_from_IStream(iface);
|
2004-08-24 23:00:15 +02:00
|
|
|
|
2005-01-03 15:39:51 +01:00
|
|
|
ULONG ref = InterlockedDecrement(&This->ref);
|
2004-08-24 23:00:15 +02:00
|
|
|
|
|
|
|
if (ref == 0)
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
IStorage_Release( &This->stg->IStorage_iface );
|
2006-12-23 23:51:55 +01:00
|
|
|
HeapFree(GetProcessHeap(), 0, This);
|
|
|
|
ITSS_UnlockModule();
|
2004-08-24 23:00:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return ref;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI ITSS_IStream_Read(
|
|
|
|
IStream* iface,
|
|
|
|
void* pv,
|
|
|
|
ULONG cb,
|
|
|
|
ULONG* pcbRead)
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
IStream_Impl *This = impl_from_IStream(iface);
|
2004-08-24 23:00:15 +02:00
|
|
|
ULONG count;
|
|
|
|
|
2006-10-12 20:56:50 +02:00
|
|
|
TRACE("%p %p %u %p\n", This, pv, cb, pcbRead);
|
2004-08-24 23:00:15 +02:00
|
|
|
|
|
|
|
count = chm_retrieve_object(This->stg->chmfile,
|
|
|
|
&This->ui, pv, This->addr, cb);
|
|
|
|
This->addr += count;
|
|
|
|
if( pcbRead )
|
|
|
|
*pcbRead = count;
|
2007-02-22 21:08:47 +01:00
|
|
|
|
2007-01-13 12:46:29 +01:00
|
|
|
return count ? S_OK : S_FALSE;
|
2004-08-24 23:00:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI ITSS_IStream_Write(
|
|
|
|
IStream* iface,
|
|
|
|
const void* pv,
|
|
|
|
ULONG cb,
|
|
|
|
ULONG* pcbWritten)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI ITSS_IStream_Seek(
|
|
|
|
IStream* iface,
|
|
|
|
LARGE_INTEGER dlibMove,
|
|
|
|
DWORD dwOrigin,
|
|
|
|
ULARGE_INTEGER* plibNewPosition)
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
IStream_Impl *This = impl_from_IStream(iface);
|
2004-08-24 23:00:15 +02:00
|
|
|
LONGLONG newpos;
|
|
|
|
|
2006-10-12 20:56:50 +02:00
|
|
|
TRACE("%p %s %u %p\n", This,
|
2004-08-24 23:00:15 +02:00
|
|
|
wine_dbgstr_longlong( dlibMove.QuadPart ), dwOrigin, plibNewPosition );
|
|
|
|
|
|
|
|
newpos = This->addr;
|
|
|
|
switch( dwOrigin )
|
|
|
|
{
|
|
|
|
case STREAM_SEEK_CUR:
|
|
|
|
newpos = This->addr + dlibMove.QuadPart;
|
|
|
|
break;
|
|
|
|
case STREAM_SEEK_SET:
|
|
|
|
newpos = dlibMove.QuadPart;
|
|
|
|
break;
|
|
|
|
case STREAM_SEEK_END:
|
|
|
|
newpos = This->ui.length + dlibMove.QuadPart;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ( newpos < 0 ) || ( newpos > This->ui.length ) )
|
|
|
|
return STG_E_INVALIDPOINTER;
|
|
|
|
|
|
|
|
This->addr = newpos;
|
|
|
|
if( plibNewPosition )
|
|
|
|
plibNewPosition->QuadPart = This->addr;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI ITSS_IStream_SetSize(
|
|
|
|
IStream* iface,
|
|
|
|
ULARGE_INTEGER libNewSize)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI ITSS_IStream_CopyTo(
|
|
|
|
IStream* iface,
|
|
|
|
IStream* pstm,
|
|
|
|
ULARGE_INTEGER cb,
|
|
|
|
ULARGE_INTEGER* pcbRead,
|
|
|
|
ULARGE_INTEGER* pcbWritten)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI ITSS_IStream_Commit(
|
|
|
|
IStream* iface,
|
|
|
|
DWORD grfCommitFlags)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI ITSS_IStream_Revert(
|
|
|
|
IStream* iface)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI ITSS_IStream_LockRegion(
|
|
|
|
IStream* iface,
|
|
|
|
ULARGE_INTEGER libOffset,
|
|
|
|
ULARGE_INTEGER cb,
|
|
|
|
DWORD dwLockType)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI ITSS_IStream_UnlockRegion(
|
|
|
|
IStream* iface,
|
|
|
|
ULARGE_INTEGER libOffset,
|
|
|
|
ULARGE_INTEGER cb,
|
|
|
|
DWORD dwLockType)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI ITSS_IStream_Stat(
|
|
|
|
IStream* iface,
|
|
|
|
STATSTG* pstatstg,
|
|
|
|
DWORD grfStatFlag)
|
|
|
|
{
|
2010-12-08 22:59:31 +01:00
|
|
|
IStream_Impl *This = impl_from_IStream(iface);
|
2004-08-25 19:30:18 +02:00
|
|
|
|
2006-10-12 20:56:50 +02:00
|
|
|
TRACE("%p %p %d\n", This, pstatstg, grfStatFlag);
|
2004-08-25 19:30:18 +02:00
|
|
|
|
|
|
|
memset( pstatstg, 0, sizeof *pstatstg );
|
|
|
|
if( !( grfStatFlag & STATFLAG_NONAME ) )
|
|
|
|
{
|
|
|
|
FIXME("copy the name\n");
|
|
|
|
}
|
|
|
|
pstatstg->type = STGTY_STREAM;
|
|
|
|
pstatstg->cbSize.QuadPart = This->ui.length;
|
|
|
|
pstatstg->grfMode = STGM_READ;
|
2008-02-29 23:06:59 +01:00
|
|
|
pstatstg->clsid = CLSID_ITStorage;
|
2004-08-25 19:30:18 +02:00
|
|
|
|
|
|
|
return S_OK;
|
2004-08-24 23:00:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI ITSS_IStream_Clone(
|
|
|
|
IStream* iface,
|
|
|
|
IStream** ppstm)
|
|
|
|
{
|
|
|
|
FIXME("\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
2005-06-01 21:57:42 +02:00
|
|
|
static const IStreamVtbl ITSS_IStream_vtbl =
|
2004-08-24 23:00:15 +02:00
|
|
|
{
|
|
|
|
ITSS_IStream_QueryInterface,
|
|
|
|
ITSS_IStream_AddRef,
|
|
|
|
ITSS_IStream_Release,
|
|
|
|
ITSS_IStream_Read,
|
|
|
|
ITSS_IStream_Write,
|
|
|
|
ITSS_IStream_Seek,
|
|
|
|
ITSS_IStream_SetSize,
|
|
|
|
ITSS_IStream_CopyTo,
|
|
|
|
ITSS_IStream_Commit,
|
|
|
|
ITSS_IStream_Revert,
|
|
|
|
ITSS_IStream_LockRegion,
|
|
|
|
ITSS_IStream_UnlockRegion,
|
|
|
|
ITSS_IStream_Stat,
|
|
|
|
ITSS_IStream_Clone,
|
|
|
|
};
|
|
|
|
|
|
|
|
static IStream_Impl *ITSS_create_stream(
|
|
|
|
ITSS_IStorageImpl *stg, struct chmUnitInfo *ui )
|
|
|
|
{
|
|
|
|
IStream_Impl *stm;
|
|
|
|
|
|
|
|
stm = HeapAlloc( GetProcessHeap(), 0, sizeof (IStream_Impl) );
|
2010-12-08 22:59:31 +01:00
|
|
|
stm->IStream_iface.lpVtbl = &ITSS_IStream_vtbl;
|
2004-08-24 23:00:15 +02:00
|
|
|
stm->ref = 1;
|
|
|
|
stm->addr = 0;
|
2008-02-29 23:06:59 +01:00
|
|
|
stm->ui = *ui;
|
2004-08-24 23:00:15 +02:00
|
|
|
stm->stg = stg;
|
2010-12-08 22:59:31 +01:00
|
|
|
IStorage_AddRef( &stg->IStorage_iface );
|
2006-12-23 23:51:55 +01:00
|
|
|
|
|
|
|
ITSS_LockModule();
|
2004-08-24 23:00:15 +02:00
|
|
|
|
|
|
|
TRACE(" -> %p\n", stm );
|
|
|
|
|
|
|
|
return stm;
|
|
|
|
}
|