Don't define COBJMACROS in objbase.h.
[wine/wine-gecko.git] / dlls / itss / storage.c
blob13fc9c1dd03b3fa9b70f6ec964840d4584e79e65
1 /*
2 * ITSS Storage implementation
4 * Copyright 2004 Mike McCormack
6 * see http://bonedaddy.net/pabs3/hhm/#chmspec
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "config.h"
25 #include <stdarg.h>
26 #include <stdio.h>
28 #define COBJMACROS
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winuser.h"
33 #include "winnls.h"
34 #include "winreg.h"
35 #include "ole2.h"
37 #include "uuids.h"
39 #include "itss.h"
40 #include "chm_lib.h"
41 #include "itsstor.h"
43 #include "wine/unicode.h"
44 #include "wine/debug.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(itss);
48 /************************************************************************/
50 typedef struct _ITSS_IStorageImpl
52 IStorageVtbl *vtbl_IStorage;
53 DWORD ref;
54 struct chmFile *chmfile;
55 WCHAR dir[1];
56 } ITSS_IStorageImpl;
58 struct enum_info
60 struct enum_info *next, *prev;
61 struct chmUnitInfo ui;
64 typedef struct _IEnumSTATSTG_Impl
66 IEnumSTATSTGVtbl *vtbl_IEnumSTATSTG;
67 DWORD ref;
68 struct enum_info *first, *last, *current;
69 } IEnumSTATSTG_Impl;
71 typedef struct _IStream_Impl
73 IStreamVtbl *vtbl_IStream;
74 DWORD ref;
75 ITSS_IStorageImpl *stg;
76 ULONGLONG addr;
77 struct chmUnitInfo ui;
78 } IStream_Impl;
80 static HRESULT ITSS_create_chm_storage(
81 struct chmFile *chmfile, const WCHAR *dir, IStorage** ppstgOpen );
82 static IStream_Impl* ITSS_create_stream(
83 ITSS_IStorageImpl *stg, struct chmUnitInfo *ui );
85 /************************************************************************/
87 static HRESULT WINAPI ITSS_IEnumSTATSTG_QueryInterface(
88 IEnumSTATSTG* iface,
89 REFIID riid,
90 void** ppvObject)
92 IEnumSTATSTG_Impl *This = (IEnumSTATSTG_Impl *)iface;
94 if (IsEqualGUID(riid, &IID_IUnknown)
95 || IsEqualGUID(riid, &IID_IEnumSTATSTG))
97 IEnumSTATSTG_AddRef(iface);
98 *ppvObject = This;
99 return S_OK;
102 WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
103 return E_NOINTERFACE;
106 static ULONG WINAPI ITSS_IEnumSTATSTG_AddRef(
107 IEnumSTATSTG* iface)
109 IEnumSTATSTG_Impl *This = (IEnumSTATSTG_Impl *)iface;
110 return ++(This->ref);
113 static ULONG WINAPI ITSS_IEnumSTATSTG_Release(
114 IEnumSTATSTG* iface)
116 IEnumSTATSTG_Impl *This = (IEnumSTATSTG_Impl *)iface;
118 ULONG ref = --This->ref;
120 if (ref == 0)
122 while( This->first )
124 struct enum_info *t = This->first->next;
125 HeapFree( GetProcessHeap(), 0, This->first );
126 This->first = t;
128 HeapFree(GetProcessHeap(), 0, This);
131 return ref;
134 static HRESULT WINAPI ITSS_IEnumSTATSTG_Next(
135 IEnumSTATSTG* iface,
136 ULONG celt,
137 STATSTG* rgelt,
138 ULONG* pceltFetched)
140 IEnumSTATSTG_Impl *This = (IEnumSTATSTG_Impl *)iface;
141 DWORD len, n;
142 struct enum_info *cur;
144 TRACE("%p %lu %p %p\n", This, celt, rgelt, pceltFetched );
146 cur = This->current;
147 n = 0;
148 while( (n<celt) && cur)
150 WCHAR *str;
152 memset( rgelt, 0, sizeof *rgelt );
154 /* copy the name */
155 str = cur->ui.path;
156 if( *str == '/' )
157 str++;
158 len = strlenW( str ) + 1;
159 rgelt->pwcsName = CoTaskMemAlloc( len*sizeof(WCHAR) );
160 strcpyW( rgelt->pwcsName, str );
162 /* determine the type */
163 if( rgelt->pwcsName[len-2] == '/' )
165 rgelt->pwcsName[len-2] = 0;
166 rgelt->type = STGTY_STORAGE;
168 else
169 rgelt->type = STGTY_STREAM;
171 /* copy the size */
172 rgelt->cbSize.QuadPart = cur->ui.length;
174 /* advance to the next item if it exists */
175 n++;
176 cur = cur->next;
179 This->current = cur;
180 *pceltFetched = n;
182 if( n < celt )
183 return S_FALSE;
185 return S_OK;
188 static HRESULT WINAPI ITSS_IEnumSTATSTG_Skip(
189 IEnumSTATSTG* iface,
190 ULONG celt)
192 IEnumSTATSTG_Impl *This = (IEnumSTATSTG_Impl *)iface;
193 DWORD n;
194 struct enum_info *cur;
196 TRACE("%p %lu\n", This, celt );
198 cur = This->current;
199 n = 0;
200 while( (n<celt) && cur)
202 n++;
203 cur = cur->next;
205 This->current = cur;
207 if( n < celt )
208 return S_FALSE;
210 return S_OK;
213 static HRESULT WINAPI ITSS_IEnumSTATSTG_Reset(
214 IEnumSTATSTG* iface)
216 IEnumSTATSTG_Impl *This = (IEnumSTATSTG_Impl *)iface;
218 TRACE("%p\n", This );
220 This->current = This->first;
222 return S_OK;
225 static HRESULT WINAPI ITSS_IEnumSTATSTG_Clone(
226 IEnumSTATSTG* iface,
227 IEnumSTATSTG** ppenum)
229 FIXME("\n");
230 return E_NOTIMPL;
233 struct IEnumSTATSTGVtbl IEnumSTATSTG_vtbl =
235 ITSS_IEnumSTATSTG_QueryInterface,
236 ITSS_IEnumSTATSTG_AddRef,
237 ITSS_IEnumSTATSTG_Release,
238 ITSS_IEnumSTATSTG_Next,
239 ITSS_IEnumSTATSTG_Skip,
240 ITSS_IEnumSTATSTG_Reset,
241 ITSS_IEnumSTATSTG_Clone
244 static IEnumSTATSTG_Impl *ITSS_create_enum( void )
246 IEnumSTATSTG_Impl *stgenum;
248 stgenum = HeapAlloc( GetProcessHeap(), 0, sizeof (IEnumSTATSTG_Impl) );
249 stgenum->vtbl_IEnumSTATSTG = &IEnumSTATSTG_vtbl;
250 stgenum->ref = 1;
251 stgenum->first = NULL;
252 stgenum->last = NULL;
253 stgenum->current = NULL;
255 TRACE(" -> %p\n", stgenum );
257 return stgenum;
260 /************************************************************************/
262 HRESULT WINAPI ITSS_IStorageImpl_QueryInterface(
263 IStorage* iface,
264 REFIID riid,
265 void** ppvObject)
267 ITSS_IStorageImpl *This = (ITSS_IStorageImpl *)iface;
269 if (IsEqualGUID(riid, &IID_IUnknown)
270 || IsEqualGUID(riid, &IID_IStorage))
272 IStorage_AddRef(iface);
273 *ppvObject = This;
274 return S_OK;
277 WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
278 return E_NOINTERFACE;
281 ULONG WINAPI ITSS_IStorageImpl_AddRef(
282 IStorage* iface)
284 ITSS_IStorageImpl *This = (ITSS_IStorageImpl *)iface;
285 return ++(This->ref);
288 ULONG WINAPI ITSS_IStorageImpl_Release(
289 IStorage* iface)
291 ITSS_IStorageImpl *This = (ITSS_IStorageImpl *)iface;
293 ULONG ref = --This->ref;
295 if (ref == 0)
297 HeapFree(GetProcessHeap(), 0, This);
300 return ref;
303 HRESULT WINAPI ITSS_IStorageImpl_CreateStream(
304 IStorage* iface,
305 LPCOLESTR pwcsName,
306 DWORD grfMode,
307 DWORD reserved1,
308 DWORD reserved2,
309 IStream** ppstm)
311 FIXME("\n");
312 return E_NOTIMPL;
315 HRESULT WINAPI ITSS_IStorageImpl_OpenStream(
316 IStorage* iface,
317 LPCOLESTR pwcsName,
318 void* reserved1,
319 DWORD grfMode,
320 DWORD reserved2,
321 IStream** ppstm)
323 ITSS_IStorageImpl *This = (ITSS_IStorageImpl *)iface;
324 IStream_Impl *stm;
325 DWORD len;
326 struct chmUnitInfo ui;
327 int r;
328 WCHAR *path;
330 TRACE("%p %s %p %lu %lu %p\n", This, debugstr_w(pwcsName),
331 reserved1, grfMode, reserved2, ppstm );
333 len = strlenW( This->dir ) + strlenW( pwcsName ) + 1;
334 path = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
335 strcpyW( path, This->dir );
336 if( pwcsName[0] == '/' )
338 WCHAR *p = &path[strlenW( path ) - 1];
339 while( ( path <= p ) && ( *p = '/' ) )
340 *p-- = 0;
342 strcatW( path, pwcsName );
344 TRACE("Resolving %s\n", debugstr_w(path));
346 r = chm_resolve_object(This->chmfile, path, &ui);
347 HeapFree( GetProcessHeap(), 0, path );
349 if( r != CHM_RESOLVE_SUCCESS )
350 return STG_E_FILENOTFOUND;
352 stm = ITSS_create_stream( This, &ui );
353 if( !stm )
354 return E_FAIL;
356 *ppstm = (IStream*) stm;
358 return S_OK;
361 HRESULT WINAPI ITSS_IStorageImpl_CreateStorage(
362 IStorage* iface,
363 LPCOLESTR pwcsName,
364 DWORD grfMode,
365 DWORD dwStgFmt,
366 DWORD reserved2,
367 IStorage** ppstg)
369 FIXME("\n");
370 return E_NOTIMPL;
373 HRESULT WINAPI ITSS_IStorageImpl_OpenStorage(
374 IStorage* iface,
375 LPCOLESTR pwcsName,
376 IStorage* pstgPriority,
377 DWORD grfMode,
378 SNB snbExclude,
379 DWORD reserved,
380 IStorage** ppstg)
382 ITSS_IStorageImpl *This = (ITSS_IStorageImpl *)iface;
384 FIXME("%p %s %p %lu %p %lu %p\n", This, debugstr_w(pwcsName),
385 pstgPriority, grfMode, snbExclude, reserved, ppstg);
386 return E_NOTIMPL;
389 HRESULT WINAPI ITSS_IStorageImpl_CopyTo(
390 IStorage* iface,
391 DWORD ciidExclude,
392 const IID* rgiidExclude,
393 SNB snbExclude,
394 IStorage* pstgDest)
396 FIXME("\n");
397 return E_NOTIMPL;
400 HRESULT WINAPI ITSS_IStorageImpl_MoveElementTo(
401 IStorage* iface,
402 LPCOLESTR pwcsName,
403 IStorage* pstgDest,
404 LPCOLESTR pwcsNewName,
405 DWORD grfFlags)
407 FIXME("\n");
408 return E_NOTIMPL;
411 HRESULT WINAPI ITSS_IStorageImpl_Commit(
412 IStorage* iface,
413 DWORD grfCommitFlags)
415 FIXME("\n");
416 return E_NOTIMPL;
419 HRESULT WINAPI ITSS_IStorageImpl_Revert(
420 IStorage* iface)
422 FIXME("\n");
423 return E_NOTIMPL;
426 static int ITSS_chm_enumerator(
427 struct chmFile *h,
428 struct chmUnitInfo *ui,
429 void *context)
431 struct enum_info *info;
432 IEnumSTATSTG_Impl* stgenum = context;
434 TRACE("adding %s to enumeration\n", debugstr_w(ui->path) );
436 info = HeapAlloc( GetProcessHeap(), 0, sizeof (struct enum_info) );
437 memcpy( &info->ui, ui, sizeof info->ui );
439 info->next = NULL;
440 info->prev = stgenum->last;
441 if( stgenum->last )
442 stgenum->last->next = info;
443 else
444 stgenum->first = info;
445 stgenum->last = info;
447 return CHM_ENUMERATOR_CONTINUE;
450 HRESULT WINAPI ITSS_IStorageImpl_EnumElements(
451 IStorage* iface,
452 DWORD reserved1,
453 void* reserved2,
454 DWORD reserved3,
455 IEnumSTATSTG** ppenum)
457 ITSS_IStorageImpl *This = (ITSS_IStorageImpl *)iface;
458 IEnumSTATSTG_Impl* stgenum;
460 TRACE("%p %ld %p %ld %p\n", This, reserved1, reserved2, reserved3, ppenum );
462 stgenum = ITSS_create_enum();
463 if( !stgenum )
464 return E_FAIL;
466 chm_enumerate_dir(This->chmfile,
467 This->dir,
468 CHM_ENUMERATE_ALL,
469 ITSS_chm_enumerator,
470 stgenum );
472 stgenum->current = stgenum->first;
474 *ppenum = (IEnumSTATSTG*) stgenum;
476 return S_OK;
479 HRESULT WINAPI ITSS_IStorageImpl_DestroyElement(
480 IStorage* iface,
481 LPCOLESTR pwcsName)
483 FIXME("\n");
484 return E_NOTIMPL;
487 HRESULT WINAPI ITSS_IStorageImpl_RenameElement(
488 IStorage* iface,
489 LPCOLESTR pwcsOldName,
490 LPCOLESTR pwcsNewName)
492 FIXME("\n");
493 return E_NOTIMPL;
496 HRESULT WINAPI ITSS_IStorageImpl_SetElementTimes(
497 IStorage* iface,
498 LPCOLESTR pwcsName,
499 const FILETIME* pctime,
500 const FILETIME* patime,
501 const FILETIME* pmtime)
503 FIXME("\n");
504 return E_NOTIMPL;
507 HRESULT WINAPI ITSS_IStorageImpl_SetClass(
508 IStorage* iface,
509 REFCLSID clsid)
511 FIXME("\n");
512 return E_NOTIMPL;
515 HRESULT WINAPI ITSS_IStorageImpl_SetStateBits(
516 IStorage* iface,
517 DWORD grfStateBits,
518 DWORD grfMask)
520 FIXME("\n");
521 return E_NOTIMPL;
524 HRESULT WINAPI ITSS_IStorageImpl_Stat(
525 IStorage* iface,
526 STATSTG* pstatstg,
527 DWORD grfStatFlag)
529 FIXME("\n");
530 return E_NOTIMPL;
533 static IStorageVtbl ITSS_IStorageImpl_Vtbl =
535 ITSS_IStorageImpl_QueryInterface,
536 ITSS_IStorageImpl_AddRef,
537 ITSS_IStorageImpl_Release,
538 ITSS_IStorageImpl_CreateStream,
539 ITSS_IStorageImpl_OpenStream,
540 ITSS_IStorageImpl_CreateStorage,
541 ITSS_IStorageImpl_OpenStorage,
542 ITSS_IStorageImpl_CopyTo,
543 ITSS_IStorageImpl_MoveElementTo,
544 ITSS_IStorageImpl_Commit,
545 ITSS_IStorageImpl_Revert,
546 ITSS_IStorageImpl_EnumElements,
547 ITSS_IStorageImpl_DestroyElement,
548 ITSS_IStorageImpl_RenameElement,
549 ITSS_IStorageImpl_SetElementTimes,
550 ITSS_IStorageImpl_SetClass,
551 ITSS_IStorageImpl_SetStateBits,
552 ITSS_IStorageImpl_Stat,
555 static HRESULT ITSS_create_chm_storage(
556 struct chmFile *chmfile, const WCHAR *dir, IStorage** ppstgOpen )
558 ITSS_IStorageImpl *stg;
559 DWORD len;
561 TRACE("%p %s\n", chmfile, debugstr_w( dir ) );
563 len = strlenW( dir ) + 1;
564 stg = HeapAlloc( GetProcessHeap(), 0,
565 sizeof (ITSS_IStorageImpl) + len*sizeof(WCHAR) );
566 stg->vtbl_IStorage = &ITSS_IStorageImpl_Vtbl;
567 stg->ref = 1;
568 stg->chmfile = chmfile;
569 strcpyW( stg->dir, dir );
571 *ppstgOpen = (IStorage*) stg;
573 return S_OK;
576 HRESULT ITSS_StgOpenStorage(
577 const WCHAR* pwcsName,
578 IStorage* pstgPriority,
579 DWORD grfMode,
580 SNB snbExclude,
581 DWORD reserved,
582 IStorage** ppstgOpen)
584 struct chmFile *chmfile;
585 static const WCHAR szRoot[] = { '/', 0 };
587 TRACE("%s\n", debugstr_w(pwcsName) );
589 chmfile = chm_openW( pwcsName );
590 if( !chmfile )
591 return E_FAIL;
593 return ITSS_create_chm_storage( chmfile, szRoot, ppstgOpen );
596 /************************************************************************/
598 static HRESULT WINAPI ITSS_IStream_QueryInterface(
599 IStream* iface,
600 REFIID riid,
601 void** ppvObject)
603 IStream_Impl *This = (IStream_Impl *)iface;
605 if (IsEqualGUID(riid, &IID_IUnknown)
606 || IsEqualGUID(riid, &IID_ISequentialStream)
607 || IsEqualGUID(riid, &IID_IStream))
609 IStream_AddRef(iface);
610 *ppvObject = This;
611 return S_OK;
614 WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
615 return E_NOINTERFACE;
618 static ULONG WINAPI ITSS_IStream_AddRef(
619 IStream* iface)
621 IStream_Impl *This = (IStream_Impl *)iface;
622 return ++(This->ref);
625 static ULONG WINAPI ITSS_IStream_Release(
626 IStream* iface)
628 IStream_Impl *This = (IStream_Impl *)iface;
630 ULONG ref = --This->ref;
632 if (ref == 0)
634 IStorage_Release( (IStorage*) This->stg );
635 HeapFree(GetProcessHeap(), 0, This);
638 return ref;
641 static HRESULT WINAPI ITSS_IStream_Read(
642 IStream* iface,
643 void* pv,
644 ULONG cb,
645 ULONG* pcbRead)
647 IStream_Impl *This = (IStream_Impl *)iface;
648 ULONG count;
650 TRACE("%p %p %lu %p\n", This, pv, cb, pcbRead);
652 count = chm_retrieve_object(This->stg->chmfile,
653 &This->ui, pv, This->addr, cb);
654 This->addr += count;
655 if( pcbRead )
656 *pcbRead = count;
658 return S_OK;
661 static HRESULT WINAPI ITSS_IStream_Write(
662 IStream* iface,
663 const void* pv,
664 ULONG cb,
665 ULONG* pcbWritten)
667 FIXME("\n");
668 return E_NOTIMPL;
671 static HRESULT WINAPI ITSS_IStream_Seek(
672 IStream* iface,
673 LARGE_INTEGER dlibMove,
674 DWORD dwOrigin,
675 ULARGE_INTEGER* plibNewPosition)
677 IStream_Impl *This = (IStream_Impl *)iface;
678 LONGLONG newpos;
680 TRACE("%p %s %lu %p\n", This,
681 wine_dbgstr_longlong( dlibMove.QuadPart ), dwOrigin, plibNewPosition );
683 newpos = This->addr;
684 switch( dwOrigin )
686 case STREAM_SEEK_CUR:
687 newpos = This->addr + dlibMove.QuadPart;
688 break;
689 case STREAM_SEEK_SET:
690 newpos = dlibMove.QuadPart;
691 break;
692 case STREAM_SEEK_END:
693 newpos = This->ui.length + dlibMove.QuadPart;
694 break;
697 if( ( newpos < 0 ) || ( newpos > This->ui.length ) )
698 return STG_E_INVALIDPOINTER;
700 This->addr = newpos;
701 if( plibNewPosition )
702 plibNewPosition->QuadPart = This->addr;
704 return S_OK;
707 static HRESULT WINAPI ITSS_IStream_SetSize(
708 IStream* iface,
709 ULARGE_INTEGER libNewSize)
711 FIXME("\n");
712 return E_NOTIMPL;
715 static HRESULT WINAPI ITSS_IStream_CopyTo(
716 IStream* iface,
717 IStream* pstm,
718 ULARGE_INTEGER cb,
719 ULARGE_INTEGER* pcbRead,
720 ULARGE_INTEGER* pcbWritten)
722 FIXME("\n");
723 return E_NOTIMPL;
726 static HRESULT WINAPI ITSS_IStream_Commit(
727 IStream* iface,
728 DWORD grfCommitFlags)
730 FIXME("\n");
731 return E_NOTIMPL;
734 static HRESULT WINAPI ITSS_IStream_Revert(
735 IStream* iface)
737 FIXME("\n");
738 return E_NOTIMPL;
741 static HRESULT WINAPI ITSS_IStream_LockRegion(
742 IStream* iface,
743 ULARGE_INTEGER libOffset,
744 ULARGE_INTEGER cb,
745 DWORD dwLockType)
747 FIXME("\n");
748 return E_NOTIMPL;
751 static HRESULT WINAPI ITSS_IStream_UnlockRegion(
752 IStream* iface,
753 ULARGE_INTEGER libOffset,
754 ULARGE_INTEGER cb,
755 DWORD dwLockType)
757 FIXME("\n");
758 return E_NOTIMPL;
761 static HRESULT WINAPI ITSS_IStream_Stat(
762 IStream* iface,
763 STATSTG* pstatstg,
764 DWORD grfStatFlag)
766 IStream_Impl *This = (IStream_Impl *)iface;
768 TRACE("%p %p %ld\n", This, pstatstg, grfStatFlag);
770 memset( pstatstg, 0, sizeof *pstatstg );
771 if( !( grfStatFlag & STATFLAG_NONAME ) )
773 FIXME("copy the name\n");
775 pstatstg->type = STGTY_STREAM;
776 pstatstg->cbSize.QuadPart = This->ui.length;
777 pstatstg->grfMode = STGM_READ;
778 memcpy( &pstatstg->clsid, &CLSID_ITStorage, sizeof (CLSID) );
780 return S_OK;
783 static HRESULT WINAPI ITSS_IStream_Clone(
784 IStream* iface,
785 IStream** ppstm)
787 FIXME("\n");
788 return E_NOTIMPL;
791 struct IStreamVtbl ITSS_IStream_vtbl =
793 ITSS_IStream_QueryInterface,
794 ITSS_IStream_AddRef,
795 ITSS_IStream_Release,
796 ITSS_IStream_Read,
797 ITSS_IStream_Write,
798 ITSS_IStream_Seek,
799 ITSS_IStream_SetSize,
800 ITSS_IStream_CopyTo,
801 ITSS_IStream_Commit,
802 ITSS_IStream_Revert,
803 ITSS_IStream_LockRegion,
804 ITSS_IStream_UnlockRegion,
805 ITSS_IStream_Stat,
806 ITSS_IStream_Clone,
809 static IStream_Impl *ITSS_create_stream(
810 ITSS_IStorageImpl *stg, struct chmUnitInfo *ui )
812 IStream_Impl *stm;
814 stm = HeapAlloc( GetProcessHeap(), 0, sizeof (IStream_Impl) );
815 stm->vtbl_IStream = &ITSS_IStream_vtbl;
816 stm->ref = 1;
817 stm->addr = 0;
818 memcpy( &stm->ui, ui, sizeof stm->ui );
819 stm->stg = stg;
820 IStorage_AddRef( (IStorage*) stg );
822 TRACE(" -> %p\n", stm );
824 return stm;