Release 960611
[wine] / misc / shell.c
1 /*
2  *                              Shell Library Functions
3  */
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <string.h>
7 #include <unistd.h>
8 #include <ctype.h>
9 #include "windows.h"
10 #include "file.h"
11 #include "shell.h"
12 #include "module.h"
13 #include "neexe.h"
14 #include "resource.h"
15 #include "dlgs.h"
16 #include "win.h"
17 #include "stddebug.h"
18 #include "debug.h"
19 #include "xmalloc.h"
20 #include "winreg.h"
21
22 extern HANDLE   CURSORICON_LoadHandler( HANDLE, HINSTANCE, BOOL);
23 extern WORD     GetIconID( HANDLE hResource, DWORD resType );
24
25 /*************************************************************************
26  *                              DragAcceptFiles         [SHELL.9]
27  */
28 void DragAcceptFiles(HWND hWnd, BOOL b)
29 {
30     WND* wnd = WIN_FindWndPtr(hWnd);
31
32     if( wnd )
33         wnd->dwExStyle = b? wnd->dwExStyle | WS_EX_ACCEPTFILES
34                           : wnd->dwExStyle & ~WS_EX_ACCEPTFILES;
35 }
36
37
38 /*************************************************************************
39  *                              DragQueryFile           [SHELL.11]
40  */
41 UINT DragQueryFile(HDROP16 hDrop, WORD wFile, LPSTR lpszFile, WORD wLength)
42 {
43     /* hDrop is a global memory block allocated with GMEM_SHARE 
44      * with DROPFILESTRUCT as a header and filenames following
45      * it, zero length filename is in the end */       
46     
47     LPDROPFILESTRUCT lpDropFileStruct;
48     LPSTR lpCurrent;
49     WORD  i;
50     
51     dprintf_reg(stddeb,"DragQueryFile(%04x, %i, %p, %u)\n",
52                 hDrop,wFile,lpszFile,wLength);
53     
54     lpDropFileStruct = (LPDROPFILESTRUCT) GlobalLock16(hDrop); 
55     if(!lpDropFileStruct)
56     {
57         dprintf_reg(stddeb,"DragQueryFile: unable to lock handle!\n");
58         return 0;
59     } 
60     lpCurrent = (LPSTR) lpDropFileStruct + lpDropFileStruct->wSize;
61     
62     i = 0;
63     while (i++ < wFile)
64     {
65         while (*lpCurrent++);  /* skip filename */
66         if (!*lpCurrent) 
67             return (wFile == 0xFFFF) ? i : 0;  
68     }
69     
70     i = strlen(lpCurrent); 
71     if (!lpszFile) return i+1;   /* needed buffer size */
72     
73     i = (wLength > i) ? i : wLength-1;
74     strncpy(lpszFile, lpCurrent, i);
75     lpszFile[i] = '\0';
76     
77     GlobalUnlock16(hDrop);
78     return i;
79 }
80
81
82 /*************************************************************************
83  *                              DragFinish              [SHELL.12]
84  */
85 void DragFinish(HDROP16 h)
86 {
87     GlobalFree16((HGLOBAL16)h);
88 }
89
90
91 /*************************************************************************
92  *                              DragQueryPoint          [SHELL.13]
93  */
94 BOOL DragQueryPoint(HDROP16 hDrop, POINT16 *p)
95 {
96     LPDROPFILESTRUCT lpDropFileStruct;  
97     BOOL             bRet;
98
99     lpDropFileStruct = (LPDROPFILESTRUCT) GlobalLock16(hDrop);
100
101     memcpy(p,&lpDropFileStruct->ptMousePos,sizeof(POINT16));
102     bRet = lpDropFileStruct->fInNonClientArea;
103
104     GlobalUnlock16(hDrop);
105     return bRet;
106 }
107
108 /*************************************************************************
109  *                              SHELL_FindExecutable
110  * Utility for code sharing between FindExecutable and ShellExecute
111  */
112 static HINSTANCE SHELL_FindExecutable( LPCSTR lpFile, 
113                                       LPCSTR lpDirectory,
114                                       LPCSTR lpOperation,
115                                       LPSTR lpResult)
116 {
117     char *extension = NULL; /* pointer to file extension */
118     char tmpext[5];         /* local copy to mung as we please */
119     char filetype[256];     /* registry name for this filetype */
120     LONG filetypelen=256;   /* length of above */
121     char command[256];      /* command from registry */
122     LONG commandlen=256;    /* This is the most DOS can handle :) */
123     char buffer[256];       /* Used to GetProfileString */
124     HINSTANCE retval=31;    /* default - 'No association was found' */
125     char *tok;              /* token pointer */
126     int i;                  /* random counter */
127
128     dprintf_exec(stddeb, "SHELL_FindExecutable: File %s, Dir %s\n", 
129                  (lpFile != NULL?lpFile:"-"), 
130                  (lpDirectory != NULL?lpDirectory:"-"));
131
132     lpResult[0]='\0'; /* Start off with an empty return string */
133
134     /* trap NULL parameters on entry */
135     if (( lpFile == NULL ) || ( lpDirectory == NULL ) || 
136         ( lpResult == NULL ) || ( lpOperation == NULL ))
137     {
138         /* FIXME - should throw a warning, perhaps! */
139         return 2; /* File not found. Close enough, I guess. */
140     }
141
142     /* First thing we need is the file's extension */
143     extension = strchr( lpFile, '.' ); /* Assumes first "." is the one... */
144     if ((extension == NULL) || (extension == &lpFile[strlen(lpFile)]))
145     {
146         return 31; /* no association */
147     }
148
149     /* Make local copy & lowercase it for reg & 'programs=' lookup */
150     strncpy( tmpext, extension, 5 );
151     if (strlen(extension)<=4)
152         tmpext[strlen(extension)]='\0';
153     else
154         tmpext[4]='\0';
155     for (i=0;i<strlen(tmpext);i++) tmpext[i]=tolower(tmpext[i]);
156     dprintf_exec(stddeb, "SHELL_FindExecutable: %s file\n", tmpext);
157     
158     /* Three places to check: */
159     /* 1. win.ini, [windows], programs (NB no leading '.') */
160     /* 2. Registry, HKEY_CLASS_ROOT\<filetype>\shell\open\command */
161     /* 3. win.ini, [extensions], extension (NB no leading '.' */
162     /* All I know of the order is that registry is checked before */
163     /* extensions; however, it'd make sense to check the programs */
164     /* section first, so that's what happens here. */
165
166     /* See if it's a program */
167     GetProfileString("windows", "programs", "exe pif bat com",
168                       buffer, sizeof(buffer)); /* FIXME check return code! */
169
170     for (i=0;i<strlen(buffer); i++) buffer[i]=tolower(buffer[i]);
171
172     tok = strtok(buffer, " \t"); /* ? */
173     while( tok!= NULL)
174     {
175         if (strcmp(tok, &tmpext[1])==0) /* have to skip the leading "." */
176         {
177             strcpy(lpResult, lpFile); /* Need to perhaps check that */
178                                       /* the file has a path attached */
179             dprintf_exec(stddeb, "SHELL_FindExecutable: found %s\n",
180                          lpResult);
181             return 33; /* Greater than 32 to indicate success FIXME */
182                        /* what are the correct values here? */
183         }
184         tok=strtok(NULL, " \t");
185     }
186
187     /* Check registry */
188     if (RegQueryValue16( (HKEY)HKEY_CLASSES_ROOT, tmpext, filetype,
189                          &filetypelen ) == SHELL_ERROR_SUCCESS )
190     {
191         filetype[filetypelen]='\0';
192         dprintf_exec(stddeb, "SHELL_FindExecutable: File type: %s\n",
193                      filetype);
194
195         /* Looking for ...buffer\shell\lpOperation\command */
196         strcat( filetype, "\\shell\\" );
197         strcat( filetype, lpOperation );
198         strcat( filetype, "\\command" );
199         
200         if (RegQueryValue16( (HKEY)HKEY_CLASSES_ROOT, filetype, command,
201                              &commandlen ) == SHELL_ERROR_SUCCESS )
202         {
203             /* Is there a replace() function anywhere? */
204             command[commandlen]='\0';
205             strcpy( lpResult, command );
206             tok=strstr( lpResult, "%1" );
207             if (tok != NULL)
208             {
209                 tok[0]='\0'; /* truncate string at the percent */
210                 strcat( lpResult, lpFile ); /* what if no dir in lpFile? */
211                 tok=strstr( command, "%1" );
212                 if ((tok!=NULL) && (strlen(tok)>2))
213                 {
214                     strcat( lpResult, &tok[2] );
215                 }
216             }
217             retval=33;
218         }
219     }
220     else /* Check win.ini */
221     {
222         /* Toss the leading dot */
223         extension++;
224         GetProfileString( "extensions", extension, "", command,
225                          sizeof(command));
226         if (strlen(command)!=0)
227         {
228             strcpy( lpResult, command );
229             tok=strstr( lpResult, "^" ); /* should be ^.extension? */
230             if (tok != NULL)
231             {
232                 tok[0]='\0';
233                 strcat( lpResult, lpFile ); /* what if no dir in lpFile? */
234                 tok=strstr( command, "^" ); /* see above */
235                 if ((tok != NULL) && (strlen(tok)>5))
236                 {
237                     strcat( lpResult, &tok[5]);
238                 }
239             }
240             retval=33;
241         }
242     }
243
244     dprintf_exec(stddeb, "SHELL_FindExecutable: returning %s\n", lpResult);
245     return retval;
246 }
247
248 /*************************************************************************
249  *                              ShellExecute            [SHELL.20]
250  */
251 HINSTANCE ShellExecute(HWND hWnd, LPCSTR lpOperation, LPCSTR lpFile,
252                        LPSTR lpParameters, LPCSTR lpDirectory,
253                        INT iShowCmd)
254 {
255     HINSTANCE retval=31;
256     char cmd[256];
257
258     dprintf_exec(stddeb, "ShellExecute(%04x,'%s','%s','%s','%s',%x)\n",
259                 hWnd, lpOperation ? lpOperation:"<null>", lpFile ? lpFile:"<null>",
260                 lpParameters ? lpParameters : "<null>", 
261                 lpDirectory ? lpDirectory : "<null>", iShowCmd);
262
263     if (lpFile==NULL) return 0; /* should not happen */
264     if (lpOperation==NULL) /* default is open */
265       lpOperation="open";
266
267     retval = SHELL_FindExecutable( lpFile, lpDirectory, lpOperation, cmd );
268
269     if ( retval <= 32 )
270     {
271         return retval;
272     }
273
274     if (lpParameters)
275     {
276         strcat(cmd," ");
277         strcat(cmd,lpParameters);
278     }
279
280     dprintf_exec(stddeb,"ShellExecute:starting %s\n",cmd);
281     return WinExec(cmd,iShowCmd);
282 }
283
284 /*************************************************************************
285  *                              FindExecutable          [SHELL.21]
286  */
287 HINSTANCE FindExecutable(LPCSTR lpFile, LPCSTR lpDirectory, LPSTR lpResult)
288 {
289     HINSTANCE retval=31;    /* default - 'No association was found' */
290
291     dprintf_exec(stddeb, "FindExecutable: File %s, Dir %s\n", 
292                  (lpFile != NULL?lpFile:"-"), 
293                  (lpDirectory != NULL?lpDirectory:"-"));
294
295     lpResult[0]='\0'; /* Start off with an empty return string */
296
297     /* trap NULL parameters on entry */
298     if (( lpFile == NULL ) || ( lpDirectory == NULL ) || 
299         ( lpResult == NULL ))
300     {
301         /* FIXME - should throw a warning, perhaps! */
302         return 2; /* File not found. Close enough, I guess. */
303     }
304
305     retval = SHELL_FindExecutable( lpFile, lpDirectory, "open",
306                                   lpResult );
307
308     dprintf_exec(stddeb, "FindExecutable: returning %s\n", lpResult);
309     return retval;
310 }
311
312 static char AppName[128], AppMisc[1536];
313
314 /*************************************************************************
315  *                              AboutDlgProc            [SHELL.33]
316  */
317 LRESULT AboutDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
318 {
319   char Template[512], AppTitle[512];
320  
321   switch(msg) {
322    case WM_INITDIALOG:
323 #ifdef WINELIB32
324     SendDlgItemMessage32A(hWnd,stc1,STM_SETICON,lParam,0);
325 #else
326     SendDlgItemMessage16(hWnd,stc1,STM_SETICON,LOWORD(lParam),0);
327 #endif
328     GetWindowText32A(hWnd, Template, sizeof(Template));
329     sprintf(AppTitle, Template, AppName);
330     SetWindowText32A(hWnd, AppTitle);
331     SetWindowText32A(GetDlgItem(hWnd,100), AppMisc);
332     return 1;
333     
334    case WM_COMMAND:
335     switch (wParam) {
336      case IDOK:
337       EndDialog(hWnd, TRUE);
338       return TRUE;
339     }
340     break;
341   }
342   return FALSE;
343 }
344
345 /*************************************************************************
346  *                              ShellAbout              [SHELL.22]
347  */
348 INT ShellAbout(HWND hWnd, LPCSTR szApp, LPCSTR szOtherStuff, HICON hIcon)
349 {
350     HANDLE handle;
351     BOOL bRet;
352
353     if (szApp) strncpy(AppName, szApp, sizeof(AppName));
354     else *AppName = 0;
355     AppName[sizeof(AppName)-1]=0;
356
357     if (szOtherStuff) strncpy(AppMisc, szOtherStuff, sizeof(AppMisc));
358     else *AppMisc = 0;
359     AppMisc[sizeof(AppMisc)-1]=0;
360
361     if (!hIcon) hIcon = LoadIcon(0,MAKEINTRESOURCE(OIC_WINEICON));
362     handle = SYSRES_LoadResource( SYSRES_DIALOG_SHELL_ABOUT_MSGBOX );
363     if (!handle) return FALSE;
364     bRet = DialogBoxIndirectParam16( WIN_GetWindowInstance( hWnd ),
365                                    handle, hWnd,
366                                    MODULE_GetWndProcEntry16("AboutDlgProc"), 
367                                    (LONG)hIcon );
368     SYSRES_FreeResource( handle );
369     return bRet;
370 }
371
372 /*************************************************************************
373  *                              SHELL_GetResourceTable
374  *
375  * FIXME: Implement GetPEResourceTable in w32sys.c and call it here.
376  */
377 BYTE* SHELL_GetResourceTable(HFILE hFile)
378 {
379   struct mz_header_s mz_header;
380   struct ne_header_s ne_header;
381   int           size;
382   
383   _llseek( hFile, 0, SEEK_SET );
384   if ((FILE_Read(hFile,&mz_header,sizeof(mz_header)) != sizeof(mz_header)) ||
385       (mz_header.mz_magic != MZ_SIGNATURE)) return (BYTE*)-1;
386
387   _llseek( hFile, mz_header.ne_offset, SEEK_SET );
388   if (FILE_Read( hFile, &ne_header, sizeof(ne_header) ) != sizeof(ne_header))
389       return NULL;
390
391   if (ne_header.ne_magic == PE_SIGNATURE) 
392      { fprintf(stdnimp,"Win32 FIXME: file %s line %i\n", __FILE__, __LINE__ );
393        return NULL; }
394
395   if (ne_header.ne_magic != NE_SIGNATURE) return NULL;
396
397   size = ne_header.rname_tab_offset - ne_header.resource_tab_offset;
398
399   if( size > sizeof(NE_TYPEINFO) )
400     {
401       BYTE* pTypeInfo = (BYTE*)xmalloc(size);
402
403       if( !pTypeInfo ) return NULL;
404
405       _llseek(hFile, mz_header.ne_offset+ne_header.resource_tab_offset, SEEK_SET);
406       if( FILE_Read( hFile, (char*)pTypeInfo, size) != size )
407         { free(pTypeInfo); return NULL; }
408       return pTypeInfo;
409     }
410   /* no resources */
411
412   return NULL;
413 }
414
415 /*************************************************************************
416  *                      SHELL_LoadResource
417  */
418 HANDLE  SHELL_LoadResource(HINSTANCE hInst, HFILE hFile, NE_NAMEINFO* pNInfo, WORD sizeShift)
419 {
420  BYTE*  ptr;
421  HANDLE handle = DirectResAlloc( hInst, 0x10, (DWORD)pNInfo->length << sizeShift);
422
423  if( (ptr = (BYTE*)GlobalLock16( handle )) )
424    {
425     _llseek( hFile, (DWORD)pNInfo->offset << sizeShift, SEEK_SET);
426      FILE_Read( hFile, (char*)ptr, pNInfo->length << sizeShift);
427      return handle;
428    }
429  return (HANDLE)0;
430 }
431
432 /*************************************************************************
433  *                      InternalExtractIcon             [SHELL.39]
434  *
435  * This abortion is called directly by Progman
436  */
437 HICON InternalExtractIcon(HINSTANCE hInstance, LPCSTR lpszExeFileName, UINT nIconIndex, WORD n )
438 {
439   HANDLE        hRet = 0;
440   HICON*        RetPtr = NULL;
441   BYTE*         pData;
442   OFSTRUCT      ofs;
443   HFILE         hFile = OpenFile( lpszExeFileName, &ofs, OF_READ );
444   
445   dprintf_reg(stddeb, "InternalExtractIcon(%04x, file '%s', start from %d, extract %d\n", 
446                        hInstance, lpszExeFileName, nIconIndex, n);
447
448   if( hFile == HFILE_ERROR || !n ) return 0;
449
450   hRet = GlobalAlloc16( GMEM_FIXED, sizeof(HICON)*n);
451   RetPtr = (HICON*)GlobalLock16(hRet);
452
453  *RetPtr = (n == 0xFFFF)? 0: 1;                         /* error return values */
454
455   pData = SHELL_GetResourceTable(hFile);
456   if( pData ) 
457     if( pData == (BYTE*)-1 )
458       {
459         /* FIXME: possible .ICO file */
460
461         fprintf(stddeb,"InternalExtractIcon: cannot handle file %s\n", lpszExeFileName);
462       }
463     else                                                /* got resource table */
464       {
465         UINT         iconDirCount = 0;
466         UINT         iconCount = 0;
467         NE_TYPEINFO* pTInfo = (NE_TYPEINFO*)(pData + 2);
468         NE_NAMEINFO* pIconStorage = NULL;
469         NE_NAMEINFO* pIconDir = NULL;
470
471         /* find icon directory and icon repository */
472
473         while( pTInfo->type_id && !(pIconStorage && pIconDir) )
474           {
475            if( pTInfo->type_id == NE_RSCTYPE_GROUP_ICON ) 
476                {
477                  iconDirCount = pTInfo->count;
478                  pIconDir = ((NE_NAMEINFO*)(pTInfo + 1));
479                  dprintf_reg(stddeb,"\tfound directory - %i icon families\n", iconDirCount);
480                }
481            if( pTInfo->type_id == NE_RSCTYPE_ICON ) 
482                { 
483                  iconCount = pTInfo->count;
484                  pIconStorage = ((NE_NAMEINFO*)(pTInfo + 1));
485                  dprintf_reg(stddeb,"\ttotal icons - %i\n", iconCount);
486                }
487            pTInfo = (NE_TYPEINFO *)((char*)(pTInfo+1)+pTInfo->count*sizeof(NE_NAMEINFO));
488           }
489
490         /* load resources and create icons */
491
492         if( pIconStorage && pIconDir )
493
494             if( nIconIndex == (UINT)-1 ) RetPtr[0] = iconDirCount;
495             else if( nIconIndex < iconDirCount )
496               {
497                   HANDLE hIcon;
498                   UINT   i, icon;
499
500                   if( n > iconDirCount - nIconIndex ) n = iconDirCount - nIconIndex;
501
502                   for( i = nIconIndex; i < nIconIndex + n; i++ ) 
503                      {
504                        hIcon = SHELL_LoadResource( hInstance, hFile, pIconDir + (i - nIconIndex), 
505                                                                                  *(WORD*)pData );
506                        RetPtr[i-nIconIndex] = GetIconID( hIcon, 3 );
507                        GlobalFree16(hIcon); 
508                      }
509
510                   for( icon = nIconIndex; icon < nIconIndex + n; icon++ )
511                      {
512                        hIcon = 0;
513                        for( i = 0; i < iconCount; i++ )
514                           if( pIconStorage[i].id == (RetPtr[icon-nIconIndex] | 0x8000) )
515                               hIcon = SHELL_LoadResource( hInstance, hFile, pIconStorage + i,
516                                                                              *(WORD*)pData );
517                        RetPtr[icon-nIconIndex] = (hIcon)?CURSORICON_LoadHandler( hIcon, hInstance, FALSE ):0;
518                      }
519               }
520         free(pData);
521       }
522
523  _lclose( hFile );
524  
525   /* return array with icon handles */
526
527   return hRet;
528 }
529
530 /*************************************************************************
531  *                              ExtractIcon             [SHELL.34]
532  */
533 HICON ExtractIcon(HINSTANCE hInstance, LPCSTR lpszExeFileName, WORD nIconIndex)
534 {
535   HANDLE handle = InternalExtractIcon(hInstance,lpszExeFileName,nIconIndex, 1);
536
537   if( handle )
538     {
539       HICON* ptr = (HICON*)GlobalLock16(handle);
540       HICON  hIcon = *ptr;
541
542       GlobalFree16(handle);
543       return hIcon;
544     }
545   return 0;
546 }
547
548 /*************************************************************************
549  *                              ExtractAssociatedIcon   [SHELL.36]
550  */
551 HICON ExtractAssociatedIcon(HINSTANCE hInst,LPSTR lpIconPath, LPWORD lpiIcon)
552 {
553     HICON hIcon = ExtractIcon(hInst, lpIconPath, *lpiIcon);
554
555     /* MAKEINTRESOURCE(2) seems to be "default" icon according to Progman 
556      *
557      * For data files it probably should call FindExecutable and load
558      * icon from there. As of now FindExecutable is empty stub.
559      */
560
561     if( hIcon < 2 ) hIcon = LoadIcon( hInst, MAKEINTRESOURCE(2));
562
563     return hIcon;
564 }
565
566 /*************************************************************************
567  *                              FindEnvironmentString   [SHELL.38]
568  *
569  * Returns a pointer into the DOS environment... Ugh.
570  */
571 LPSTR SHELL_FindString(LPSTR lpEnv, LPCSTR entry)
572 {
573   UINT  l = strlen(entry); 
574   for( ; *lpEnv ; lpEnv+=strlen(lpEnv)+1 )
575      {
576        if( lstrncmpi32A(lpEnv, entry, l) ) continue;
577        
578        if( !*(lpEnv+l) )
579          return (lpEnv + l);            /* empty entry */
580        else if ( *(lpEnv+l)== '=' )
581          return (lpEnv + l + 1);
582      }
583   return NULL;
584 }
585
586 SEGPTR FindEnvironmentString(LPSTR str)
587 {
588  SEGPTR  spEnv = GetDOSEnvironment();
589  LPSTR  lpEnv = (LPSTR)PTR_SEG_TO_LIN(spEnv);
590  
591  LPSTR  lpString = (spEnv)?SHELL_FindString(lpEnv, str):NULL; 
592
593  if( lpString )         /*  offset should be small enough */
594      return spEnv + (lpString - lpEnv);
595
596  return (SEGPTR)NULL;
597 }
598
599 /*************************************************************************
600  *                              DoEnvironmentSubst      [SHELL.37]
601  *
602  * Replace %KEYWORD% in the str with the value of variable KEYWORD
603  * from "DOS" environment.
604  */
605 DWORD DoEnvironmentSubst(LPSTR str,WORD length)
606 {
607   LPSTR   lpEnv = (LPSTR)PTR_SEG_TO_LIN(GetDOSEnvironment());
608   LPSTR   lpBuffer = (LPSTR)xmalloc(length);
609   LPSTR   lpstr = str;
610   LPSTR   lpbstr = lpBuffer;
611
612   AnsiToOem(str,str);
613
614   dprintf_reg(stddeb,"DoEnvSubst: accept %s", str);
615
616   while( *lpstr && lpbstr - lpBuffer < length )
617    {
618      LPSTR lpend = lpstr;
619
620      if( *lpstr == '%' )
621        {
622           do { lpend++; } while( *lpend && *lpend != '%' );
623           if( *lpend == '%' && lpend - lpstr > 1 )      /* found key */
624             {
625                LPSTR lpKey;
626               *lpend = '\0';  
627                lpKey = SHELL_FindString(lpEnv, lpstr+1);
628                if( lpKey )                              /* found key value */
629                  {
630                    int l = strlen(lpKey);
631
632                    if( l > length - (lpbstr - lpBuffer) - 1 )
633                      {
634                        fprintf(stdnimp,"File %s, line %i: Env subst aborted - string too short\n", 
635                                         __FILE__, __LINE__);
636                       *lpend = '%';
637                        break;
638                      }
639                    strcpy(lpbstr, lpKey);
640                    lpbstr += l;
641                  }
642                else break;
643               *lpend = '%';
644                lpstr = lpend + 1;
645             }
646           else break;                                   /* back off and whine */
647
648           continue;
649        } 
650
651      *lpbstr++ = *lpstr++;
652    }
653
654  *lpbstr = '\0';
655   if( lpstr - str == strlen(str) )
656     {
657       strncpy(str, lpBuffer, length);
658       length = 1;
659     }
660   else
661       length = 0;
662
663   dprintf_reg(stddeb," return %s\n", str);
664
665   OemToAnsi(str,str);
666   free(lpBuffer);
667
668   /*  Return str length in the LOWORD
669    *  and 1 in HIWORD if subst was successful.
670    */
671  return (DWORD)MAKELONG(strlen(str), length);
672 }
673
674 /*************************************************************************
675  *                              RegisterShellHook       [SHELL.102]
676  */
677 int RegisterShellHook(void *ptr) 
678 {
679         dprintf_reg(stdnimp, "RegisterShellHook : Empty Stub !!!\n");
680         return 0;
681 }
682
683
684 /*************************************************************************
685  *                              ShellHookProc           [SHELL.103]
686  */
687 int ShellHookProc(void) 
688 {
689         dprintf_reg(stdnimp, "ShellHookProc : Empty Stub !!!\n");
690         return 0;
691 }