Update shell xxxAW wrapper prototypes for fixed SHLWAPI functions.
[wine] / dlls / imm32 / imekl.c
1 /*
2  *      IME Keyboard Layout
3  *
4  *      Copyright 2000 Hidenori Takeshima
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22
23 #include <string.h>
24 #include "winbase.h"
25 #include "windef.h"
26 #include "wingdi.h"
27 #include "winuser.h"
28 #include "winerror.h"
29 #include "winnls.h"
30 #include "winreg.h"
31 #include "immddk.h"
32
33 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(imm);
35
36 #include "imm_private.h"
37
38 static const char       IMM32_szRegKL[] =
39         "System\\CurrentControlSet\\Control\\keyboard layouts";
40 static const char       IMM32_szIME_File[] = "IME file";
41 static const char       IMM32_szLayout_File[] = "layout file";
42
43 static IMM32_IMEKL*     IMM32_pklFirst = NULL;
44
45
46 static LONG IMM32_RegOpenKey( HKL hkl, PHKEY phkRet )
47 {
48         CHAR    szRegPath[ sizeof(IMM32_szRegKL)+16 ];
49
50         wsprintfA( szRegPath, "%s\\%08x", IMM32_szRegKL, (unsigned)hkl );
51         return RegOpenKeyExA( HKEY_LOCAL_MACHINE, szRegPath,
52                               0, KEY_READ, phkRet );
53 }
54
55 static LONG IMM32_RegCreateKey( HKL hkl, PHKEY phkRet,
56                                 LPDWORD lpdwDisposition )
57 {
58         CHAR    szRegPath[ sizeof(IMM32_szRegKL)+16 ];
59
60         wsprintfA( szRegPath, "%s\\%08x", IMM32_szRegKL, (unsigned)hkl );
61         return RegCreateKeyExA( HKEY_LOCAL_MACHINE, szRegPath,
62                                 0, "REG_SZ",
63                                 REG_OPTION_NON_VOLATILE,
64                                 KEY_ALL_ACCESS, NULL,
65                                 phkRet, lpdwDisposition );
66 }
67
68 static DWORD IMM32_GetIMEFileName( HKL hkl, LPSTR lpBuf, INT nBufLen )
69 {
70         HKEY    hkey;
71         LONG    nError;
72         DWORD   dwType;
73         DWORD   cbData;
74         CHAR    szValueName[ sizeof(IMM32_szIME_File) ];
75
76         TRACE( "hkl = %08x\n", (unsigned)hkl );
77
78         nError = IMM32_RegOpenKey( hkl, &hkey );
79         if ( nError != ERROR_SUCCESS )
80         {
81                 SetLastError( nError );
82                 return 0;
83         }
84         memcpy( szValueName, IMM32_szIME_File, sizeof(IMM32_szIME_File) );
85
86         nError = RegQueryValueExA( hkey, szValueName, NULL,
87                                    &dwType, NULL, &cbData );
88         if ( nError == ERROR_SUCCESS && dwType != REG_SZ )
89                 nError = ERROR_FILE_NOT_FOUND; /* FIXME? */
90         if ( nError == ERROR_SUCCESS && lpBuf != NULL && nBufLen != 0 )
91         {
92                 if ( nBufLen < (INT)cbData )
93                         nError = ERROR_INSUFFICIENT_BUFFER;
94                 else
95                         nError = RegQueryValueExA( hkey, szValueName, NULL,
96                                                    &dwType, lpBuf, &cbData );
97         }
98
99         RegCloseKey( hkey );
100
101         if ( nError != ERROR_SUCCESS )
102         {
103                 SetLastError( nError );
104                 return 0;
105         }
106
107         return cbData;
108 }
109
110
111 static
112 BOOL IMM32_GetIMEHandlersA( HINSTANCE hInstIME,
113                             struct IMM32_IME_EXPORTED_HANDLERS* phandlers )
114 {
115         BOOL    fError;
116
117         #define FE(name)                                                \
118                 phandlers->p##name = (IMM32_p##name)                    \
119                         GetProcAddress(hInstIME,#name);                 \
120                 if ( phandlers->p##name == NULL ) fError = TRUE;
121         #define FE_(name)                                               \
122                 phandlers->p##name.A = (IMM32_p##name##A)               \
123                         GetProcAddress(hInstIME,#name);                 \
124                 if ( phandlers->p##name.A == NULL ) fError = TRUE;
125
126         fError = FALSE;
127
128         FE_(ImeInquire)
129         FE_(ImeConfigure)
130         FE_(ImeConversionList)
131         FE(ImeDestroy)
132         FE_(ImeEnumRegisterWord)
133         FE_(ImeGetRegisterWordStyle)
134         FE_(ImeEscape)
135         FE(ImeProcessKey)
136         FE_(ImeRegisterWord)
137         FE(ImeSelect)
138         FE(ImeSetActiveContext)
139         FE_(ImeSetCompositionString)
140         FE(ImeToAsciiEx)
141         FE_(ImeUnregisterWord)
142         FE(NotifyIME)
143
144         if ( fError )
145                 return FALSE;
146
147         FE_(ImeGetImeMenuItems)
148
149         #undef  FE
150         #undef  FE_
151
152         return TRUE;
153 }
154
155 static
156 BOOL IMM32_GetIMEHandlersW( HINSTANCE hInstIME,
157                             struct IMM32_IME_EXPORTED_HANDLERS* phandlers )
158 {
159         BOOL    fError;
160
161         #define FE(name)                                                \
162                 phandlers->p##name = (IMM32_p##name)                    \
163                         GetProcAddress(hInstIME,#name);                 \
164                 if ( phandlers->p##name == NULL ) fError = TRUE;
165         #define FE_(name)                                               \
166                 phandlers->p##name.W = (IMM32_p##name##W)               \
167                         GetProcAddress(hInstIME,#name);                 \
168                 if ( phandlers->p##name.W == NULL ) fError = TRUE;
169
170         fError = FALSE;
171
172         FE_(ImeInquire)
173         FE_(ImeConfigure)
174         FE_(ImeConversionList)
175         FE(ImeDestroy)
176         FE_(ImeEnumRegisterWord)
177         FE_(ImeGetRegisterWordStyle)
178         FE_(ImeEscape)
179         FE(ImeProcessKey)
180         FE_(ImeRegisterWord)
181         FE(ImeSelect)
182         FE(ImeSetActiveContext)
183         FE_(ImeSetCompositionString)
184         FE(ImeToAsciiEx)
185         FE_(ImeUnregisterWord)
186         FE(NotifyIME)
187
188         if ( fError )
189                 return FALSE;
190
191         FE_(ImeGetImeMenuItems)
192
193         #undef  FE
194         #undef  FE_
195
196         return TRUE;
197 }
198
199
200 static IMM32_IMEKL* IMM32_LoadIME( HKL hkl )
201 {
202         IMM32_IMEKL*    pkl = NULL;
203         BOOL    fInitialized = FALSE;
204         CHAR*   pszFileName = NULL;
205         DWORD   dwBufLen;
206         IMM32_pImeInquireA      pImeInquire;
207         IMM32_pImeDestroy       pImeDestroy = NULL;
208         CHAR    szUIClassName[ (IMM32_UICLASSNAME_MAX+1)*sizeof(WCHAR) ];
209
210         dwBufLen = IMM32_GetIMEFileName( hkl, NULL, 0 );
211         if ( dwBufLen == 0 )
212                 goto err;
213         pszFileName = (CHAR*)IMM32_HeapAlloc( 0, sizeof(CHAR)*(dwBufLen+1) );
214         if ( pszFileName == NULL )
215         {
216                 SetLastError( ERROR_OUTOFMEMORY );
217                 goto err;
218         }
219         if ( !IMM32_GetIMEFileName( hkl, pszFileName, dwBufLen + 1 ) )
220                 goto err;
221
222         pkl = (IMM32_IMEKL*)
223                 IMM32_HeapAlloc( HEAP_ZERO_MEMORY, sizeof(IMM32_IMEKL) );
224         if ( pkl == NULL )
225         {
226                 SetLastError( ERROR_OUTOFMEMORY );
227                 goto err;
228         }
229
230         pkl->pNext = NULL;
231         pkl->hkl = hkl;
232         pkl->hInstIME = LoadLibraryA( pszFileName );
233         if ( pkl->hInstIME == (HINSTANCE)NULL )
234                 goto err;
235
236         pImeInquire = (IMM32_pImeInquireA)GetProcAddress
237                                         ( pkl->hInstIME, "ImeInquire" );
238         pImeDestroy = (IMM32_pImeDestroy)GetProcAddress
239                                         ( pkl->hInstIME, "ImeDestroy" );
240         if ( pImeInquire == NULL || pImeDestroy == NULL )
241                 goto err;
242
243         if ( !pImeInquire( &(pkl->imeinfo), szUIClassName, NULL ) )
244         {
245                 SetLastError( ERROR_DLL_INIT_FAILED ); /* FIXME? */
246                 goto err;
247         }
248         fInitialized = TRUE;
249
250         /* FIXME: Is this correct??? */
251         if ( pkl->imeinfo.fdwProperty & IME_PROP_UNICODE )
252                 pkl->fUnicode = TRUE;
253         else
254                 pkl->fUnicode = FALSE;
255
256         if ( pkl->fUnicode )
257         {
258                 if ( !IMM32_GetIMEHandlersW( pkl->hInstIME, &pkl->handlers ) )
259                         goto err;
260                 memcpy( pkl->UIClassName.W, szUIClassName,
261                         IMM32_UICLASSNAME_MAX*sizeof(WCHAR) );
262                 TRACE( "UI class name(Unicode): %s\n",
263                         debugstr_w(pkl->UIClassName.W) );
264         }
265         else
266         {
267                 if ( !IMM32_GetIMEHandlersA( pkl->hInstIME, &pkl->handlers ) )
268                         goto err;
269                 memcpy( pkl->UIClassName.A, szUIClassName,
270                         IMM32_UICLASSNAME_MAX*sizeof(CHAR) );
271                 TRACE( "UI class name(ASCII): %s\n",
272                         debugstr_a(pkl->UIClassName.A) );
273         }
274
275         /* The IME is loaded successfully. */
276         pkl->pszIMEFileName = pszFileName; pszFileName = NULL;
277         return pkl;
278
279 err:
280         IMM32_HeapFree( pszFileName );
281         if ( pkl != NULL )
282         {
283                 if ( pkl->hInstIME != (HINSTANCE)NULL )
284                 {
285                         if ( fInitialized )
286                                 (void)pImeDestroy(0);
287                         FreeLibrary( pkl->hInstIME );
288                 }
289                 IMM32_HeapFree( pkl );
290         }
291
292         return NULL;
293 }
294
295
296 const IMM32_IMEKL* IMM32_GetIME( HKL hkl )
297 {
298         IMM32_IMEKL*    pkl;
299
300         IMM32_Lock();
301
302         pkl = IMM32_pklFirst;
303         while ( pkl != NULL )
304         {
305                 if ( pkl->hkl == hkl )
306                         goto end;
307                 pkl = pkl->pNext;
308         }
309
310         pkl = IMM32_LoadIME( hkl );
311         if ( pkl != NULL )
312         {
313                 pkl->pNext = IMM32_pklFirst;
314                 IMM32_pklFirst = pkl;
315         }
316
317 end:
318         IMM32_Unlock();
319
320         return pkl;
321 }
322
323 void IMM32_UnloadAllIMEs( void )
324 {
325         IMM32_IMEKL*    pkl;
326         IMM32_IMEKL*    pklNext;
327
328         IMM32_Lock();
329
330         pkl = IMM32_pklFirst;
331         while ( pkl != NULL )
332         {
333                 TRACE( "hkl = %08x\n", (unsigned)pkl->hkl );
334
335                 pklNext = pkl->pNext;
336                 (void)pkl->handlers.pImeDestroy(0);
337                 FreeLibrary( pkl->hInstIME );
338                 IMM32_HeapFree( pkl->pszIMEFileName );
339                 IMM32_HeapFree( pkl );
340                 pkl = pklNext;
341         }
342         IMM32_pklFirst = NULL;
343
344         IMM32_Unlock();
345 }
346
347
348
349 /***********************************************************************
350  *              ImmInstallIMEA (IMM32.@)
351  */
352 HKL WINAPI ImmInstallIMEA(
353         LPCSTR lpszIMEFileName, LPCSTR lpszLayoutText)
354 {
355         HKL     hkl;
356         DWORD   dwLCID;
357         DWORD   dwTryCount;
358         LONG    nError;
359         HKEY    hkey;
360         DWORD   dwDisposition;
361         DWORD   cbData;
362         CHAR    szValueName[ sizeof(IMM32_szIME_File) ];
363
364         TRACE("(%s, %s)\n",
365                 debugstr_a(lpszIMEFileName), debugstr_a(lpszLayoutText) );
366
367         dwLCID = (DWORD)GetThreadLocale();
368         dwTryCount = 0;
369
370         FIXME( "IMEs don't work correctly now since\n"
371                "wine/windows/input.c doesn't handle HKL correctly.\n" );
372
373         while ( 1 )
374         {
375                 hkl = (HKL)(((0xe000|dwTryCount)<<16) | (dwLCID));
376
377                 nError = IMM32_RegCreateKey( hkl, &hkey, &dwDisposition );
378                 if ( nError != ERROR_SUCCESS )
379                         break;
380
381                 memcpy( szValueName, IMM32_szIME_File,
382                         sizeof(IMM32_szIME_File) );
383
384                 nError = RegQueryValueExA( hkey, szValueName, NULL,
385                                            NULL, NULL, &cbData );
386                 if ( nError == ERROR_SUCCESS && cbData > 0 )
387                 {
388                         RegCloseKey( hkey );
389
390                         /* try to assign an other HKL. */
391                         dwTryCount ++;
392                         if ( dwTryCount >= 0x100 )
393                         {
394                                 nError = ERROR_ACCESS_DENIED; /* FIXME */
395                                 break;
396                         }
397                         continue;
398                 }
399
400                 nError = RegSetValueExA( hkey, IMM32_szIME_File, 0,
401                                          REG_SZ, lpszIMEFileName,
402                                          strlen(lpszIMEFileName)+1 );
403                 if ( nError == ERROR_SUCCESS )
404                         nError = RegSetValueExA( hkey, IMM32_szLayout_File, 0,
405                                                  REG_SZ, lpszLayoutText,
406                                                  strlen(lpszLayoutText)+1 );
407                 RegCloseKey( hkey );
408                 break;
409         }
410
411         if ( nError != ERROR_SUCCESS )
412         {
413                 SetLastError( nError );
414                 return (HKL)NULL;
415         }
416
417         return hkl;
418 }
419
420 /***********************************************************************
421  *              ImmInstallIMEW (IMM32.@)
422  */
423 HKL WINAPI ImmInstallIMEW(
424         LPCWSTR lpszIMEFileName, LPCWSTR lpszLayoutText)
425 {
426         LPSTR   lpszParam1;
427         LPSTR   lpszParam2;
428         HKL     hkl;
429
430         TRACE("(%s, %s)\n",
431                 debugstr_w(lpszIMEFileName), debugstr_w(lpszLayoutText) );
432
433         lpszParam1 = IMM32_strdupWtoA( lpszIMEFileName );
434         lpszParam2 = IMM32_strdupWtoA( lpszLayoutText );
435         if ( ( lpszParam1 == NULL ) || ( lpszParam2 == NULL ) )
436         {
437                 SetLastError( ERROR_OUTOFMEMORY );
438                 hkl = (HKL)NULL;
439         }
440         else
441         {
442                 hkl = ImmInstallIMEA( lpszParam1, lpszParam2 );
443         }
444         IMM32_HeapFree( lpszParam1 );
445         IMM32_HeapFree( lpszParam2 );
446
447         return hkl;
448 }
449
450
451 /***********************************************************************
452  *              ImmIsIME (IMM32.@)
453  */
454 BOOL WINAPI ImmIsIME(HKL hkl)
455 {
456         const IMM32_IMEKL*      pkl;
457
458         TRACE("(0x%08x)\n", hkl);
459
460         pkl = IMM32_GetIME( hkl );
461         if ( pkl == NULL )
462                 return FALSE;
463
464         return TRUE;
465 }
466
467
468 /***********************************************************************
469  *              ImmGetIMEFileNameA (IMM32.@)
470  */
471 UINT WINAPI ImmGetIMEFileNameA(HKL hkl, LPSTR lpszFileName, UINT uBufLen)
472 {
473         const IMM32_IMEKL*      pkl;
474         UINT                    uIMEFileNameLen;
475
476         TRACE("(%08x,%p,%u)\n",hkl,lpszFileName,uBufLen);
477
478         pkl = IMM32_GetIME( hkl );
479         if ( pkl == NULL )
480                 return 0;
481
482         uIMEFileNameLen = strlen(pkl->pszIMEFileName);
483         if ( uBufLen == 0 )
484                 return uIMEFileNameLen;
485         if ( uBufLen <= uIMEFileNameLen )
486         {
487                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
488                 return 0;
489         }
490         memcpy( lpszFileName, pkl->pszIMEFileName,
491                 sizeof(CHAR)*(uIMEFileNameLen+1) );
492
493         return uIMEFileNameLen;
494 }
495
496 /***********************************************************************
497  *              ImmGetIMEFileNameW (IMM32.@)
498  */
499 UINT WINAPI ImmGetIMEFileNameW(HKL hkl, LPWSTR lpszFileName, UINT uBufLen)
500 {
501         const IMM32_IMEKL*      pkl;
502         UINT                    uIMEFileNameLen;
503
504         TRACE("(%08x,%p,%u)\n",hkl,lpszFileName,uBufLen);
505
506         pkl = IMM32_GetIME( hkl );
507         if ( pkl == NULL )
508                 return 0;
509
510         uIMEFileNameLen = IMM32_strlenAtoW(pkl->pszIMEFileName);
511         if ( uBufLen == 0 )
512                 return uIMEFileNameLen;
513         if ( uBufLen <= uIMEFileNameLen )
514         {
515                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
516                 return 0;
517         }
518         IMM32_strncpyAtoW( lpszFileName, pkl->pszIMEFileName, uBufLen );
519
520         return uIMEFileNameLen;
521 }
522
523 /***********************************************************************
524  *              ImmGetProperty (IMM32.@)
525  */
526 DWORD WINAPI ImmGetProperty(HKL hkl, DWORD fdwIndex)
527 {
528         const IMM32_IMEKL*      pkl;
529         DWORD                   dwRet;
530
531         TRACE("(0x%08x, %ld)\n", hkl, fdwIndex);
532
533         pkl = IMM32_GetIME( hkl );
534         if ( pkl == NULL )
535                 return 0;
536
537         switch ( fdwIndex )
538         {
539         case IGP_GETIMEVERSION:
540                 dwRet = IMEVER_0400;
541                 break;
542         case IGP_PROPERTY:
543                 dwRet = pkl->imeinfo.fdwProperty;
544                 break;
545         case IGP_CONVERSION:
546                 dwRet = pkl->imeinfo.fdwConversionCaps;
547                 break;
548         case IGP_SENTENCE:
549                 dwRet = pkl->imeinfo.fdwSentenceCaps;
550                 break;
551         case IGP_UI:
552                 dwRet = pkl->imeinfo.fdwUICaps;
553                 break;
554         case IGP_SETCOMPSTR:
555                 dwRet = pkl->imeinfo.fdwSCSCaps;
556                 break;
557         case IGP_SELECT:
558                 dwRet = pkl->imeinfo.fdwSelectCaps;
559                 break;
560         default:
561                 FIXME("(0x%08x, %ld): invalid/unknown property.\n",
562                       hkl, fdwIndex);
563                 SetLastError( ERROR_INVALID_PARAMETER );
564                 dwRet = 0;
565         }
566
567         return dwRet;
568 }
569
570
571 /***********************************************************************
572  *              ImmEnumRegisterWordA (IMM32.@)
573  */
574 UINT WINAPI ImmEnumRegisterWordA(
575         HKL hkl, REGISTERWORDENUMPROCA lpfnEnumProc,
576         LPCSTR lpszReading, DWORD dwStyle,
577         LPCSTR lpszRegister, LPVOID lpData)
578 {
579         const IMM32_IMEKL*      pkl;
580
581         TRACE("(0x%08x, %p, %s, %ld, %s, %p)\n",
582                 hkl, lpfnEnumProc, 
583                 debugstr_a(lpszReading), dwStyle,
584                 debugstr_a(lpszRegister), lpData);
585
586         pkl = IMM32_GetIME( hkl );
587         if ( pkl == NULL )
588                 return 0;
589
590         if ( pkl->fUnicode )
591         {
592                 FIXME( "please implement UNICODE->ANSI\n" );
593                 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
594                 return 0;
595         }
596         else
597         {
598                 return pkl->handlers.pImeEnumRegisterWord.A
599                         ( lpfnEnumProc, lpszReading, dwStyle,
600                           lpszRegister, lpData );
601         }
602 }
603
604 /***********************************************************************
605  *              ImmEnumRegisterWordW (IMM32.@)
606  */
607 UINT WINAPI ImmEnumRegisterWordW(
608         HKL hkl, REGISTERWORDENUMPROCW lpfnEnumProc,
609         LPCWSTR lpszReading, DWORD dwStyle,
610         LPCWSTR lpszRegister, LPVOID lpData)
611 {
612         const IMM32_IMEKL*      pkl;
613
614         TRACE("(0x%08x, %p, %s, %ld, %s, %p): stub\n",
615                 hkl, lpfnEnumProc, 
616                 debugstr_w(lpszReading), dwStyle,
617                 debugstr_w(lpszRegister), lpData);
618
619         pkl = IMM32_GetIME( hkl );
620         if ( pkl == NULL )
621                 return 0;
622
623         if ( pkl->fUnicode )
624         {
625                 return pkl->handlers.pImeEnumRegisterWord.W
626                         ( lpfnEnumProc, lpszReading, dwStyle,
627                           lpszRegister, lpData );
628         }
629         else
630         {
631                 FIXME( "please implement ANSI->UNICODE\n" );
632                 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
633                 return 0;
634         }
635 }
636
637
638 /***********************************************************************
639  *              ImmGetRegisterWordStyleA (IMM32.@)
640  */
641 UINT WINAPI ImmGetRegisterWordStyleA(
642         HKL hkl, UINT nItem, LPSTYLEBUFA lpStyleBuf)
643 {
644         const IMM32_IMEKL*      pkl;
645
646         TRACE("(0x%08x, %d, %p)\n", hkl, nItem, lpStyleBuf);
647
648         pkl = IMM32_GetIME( hkl );
649         if ( pkl == NULL )
650                 return 0;
651
652         if ( pkl->fUnicode )
653         {
654                 FIXME( "please implement UNICODE->ANSI\n" );
655                 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
656                 return 0;
657         }
658         else
659         {
660                 return pkl->handlers.pImeGetRegisterWordStyle.A
661                         ( nItem, lpStyleBuf );
662         }
663 }
664
665 /***********************************************************************
666  *              ImmGetRegisterWordStyleW (IMM32.@)
667  */
668 UINT WINAPI ImmGetRegisterWordStyleW(
669         HKL hkl, UINT nItem, LPSTYLEBUFW lpStyleBuf)
670 {
671         const IMM32_IMEKL*      pkl;
672
673         TRACE("(0x%08x, %d, %p)\n", hkl, nItem, lpStyleBuf);
674
675         pkl = IMM32_GetIME( hkl );
676         if ( pkl == NULL )
677                 return 0;
678
679         if ( pkl->fUnicode )
680         {
681                 return pkl->handlers.pImeGetRegisterWordStyle.W
682                         ( nItem, lpStyleBuf );
683         }
684         else
685         {
686                 FIXME( "please implement ANSI->UNICODE\n" );
687                 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
688                 return 0;
689         }
690 }
691
692