Release 960712
[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 = strrchr( lpFile, '.' ); /* Assume last "." is the one; */
144                                         /* File->Run in progman uses */
145                                         /* .\FILE.EXE :( */
146     if ((extension == NULL) || (extension == &lpFile[strlen(lpFile)]))
147     {
148         return 31; /* no association */
149     }
150
151     /* Make local copy & lowercase it for reg & 'programs=' lookup */
152     strncpy( tmpext, extension, 5 );
153     if (strlen(extension)<=4)
154         tmpext[strlen(extension)]='\0';
155     else
156         tmpext[4]='\0';
157     for (i=0;i<strlen(tmpext);i++) tmpext[i]=tolower(tmpext[i]);
158     dprintf_exec(stddeb, "SHELL_FindExecutable: %s file\n", tmpext);
159     
160     /* Three places to check: */
161     /* 1. win.ini, [windows], programs (NB no leading '.') */
162     /* 2. Registry, HKEY_CLASS_ROOT\<filetype>\shell\open\command */
163     /* 3. win.ini, [extensions], extension (NB no leading '.' */
164     /* All I know of the order is that registry is checked before */
165     /* extensions; however, it'd make sense to check the programs */
166     /* section first, so that's what happens here. */
167
168     /* See if it's a program */
169     GetProfileString("windows", "programs", "exe pif bat com",
170                       buffer, sizeof(buffer)); /* FIXME check return code! */
171
172     for (i=0;i<strlen(buffer); i++) buffer[i]=tolower(buffer[i]);
173
174     tok = strtok(buffer, " \t"); /* ? */
175     while( tok!= NULL)
176     {
177         if (strcmp(tok, &tmpext[1])==0) /* have to skip the leading "." */
178         {
179             strcpy(lpResult, lpFile); /* Need to perhaps check that */
180                                       /* the file has a path attached */
181             dprintf_exec(stddeb, "SHELL_FindExecutable: found %s\n",
182                          lpResult);
183             return 33; /* Greater than 32 to indicate success FIXME */
184                        /* According to the docs, I should be returning */
185                        /* a handle for the executable. Does this mean */
186                        /* I'm supposed to open the executable file or */
187                        /* something? More RTFM, I guess... */
188         }
189         tok=strtok(NULL, " \t");
190     }
191
192     /* Check registry */
193     if (RegQueryValue16( (HKEY)HKEY_CLASSES_ROOT, tmpext, filetype,
194                          &filetypelen ) == SHELL_ERROR_SUCCESS )
195     {
196         filetype[filetypelen]='\0';
197         dprintf_exec(stddeb, "SHELL_FindExecutable: File type: %s\n",
198                      filetype);
199
200         /* Looking for ...buffer\shell\lpOperation\command */
201         strcat( filetype, "\\shell\\" );
202         strcat( filetype, lpOperation );
203         strcat( filetype, "\\command" );
204         
205         if (RegQueryValue16( (HKEY)HKEY_CLASSES_ROOT, filetype, command,
206                              &commandlen ) == SHELL_ERROR_SUCCESS )
207         {
208             /* Is there a replace() function anywhere? */
209             command[commandlen]='\0';
210             strcpy( lpResult, command );
211             tok=strstr( lpResult, "%1" );
212             if (tok != NULL)
213             {
214                 tok[0]='\0'; /* truncate string at the percent */
215                 strcat( lpResult, lpFile ); /* what if no dir in lpFile? */
216                 tok=strstr( command, "%1" );
217                 if ((tok!=NULL) && (strlen(tok)>2))
218                 {
219                     strcat( lpResult, &tok[2] );
220                 }
221             }
222             retval=33;
223         }
224     }
225     else /* Check win.ini */
226     {
227         /* Toss the leading dot */
228         extension++;
229         GetProfileString( "extensions", extension, "", command,
230                          sizeof(command));
231         if (strlen(command)!=0)
232         {
233             strcpy( lpResult, command );
234             tok=strstr( lpResult, "^" ); /* should be ^.extension? */
235             if (tok != NULL)
236             {
237                 tok[0]='\0';
238                 strcat( lpResult, lpFile ); /* what if no dir in lpFile? */
239                 tok=strstr( command, "^" ); /* see above */
240                 if ((tok != NULL) && (strlen(tok)>5))
241                 {
242                     strcat( lpResult, &tok[5]);
243                 }
244             }
245             retval=33;
246         }
247     }
248
249     dprintf_exec(stddeb, "SHELL_FindExecutable: returning %s\n", lpResult);
250     return retval;
251 }
252
253 /*************************************************************************
254  *                              ShellExecute            [SHELL.20]
255  */
256 HINSTANCE ShellExecute(HWND hWnd, LPCSTR lpOperation, LPCSTR lpFile,
257                        LPSTR lpParameters, LPCSTR lpDirectory,
258                        INT iShowCmd)
259 {
260     HINSTANCE retval=31;
261     char cmd[256];
262
263     dprintf_exec(stddeb, "ShellExecute(%04x,'%s','%s','%s','%s',%x)\n",
264                 hWnd, lpOperation ? lpOperation:"<null>", lpFile ? lpFile:"<null>",
265                 lpParameters ? lpParameters : "<null>", 
266                 lpDirectory ? lpDirectory : "<null>", iShowCmd);
267
268     if (lpFile==NULL) return 0; /* should not happen */
269     if (lpOperation==NULL) /* default is open */
270       lpOperation="open";
271
272     retval = SHELL_FindExecutable( lpFile, lpDirectory, lpOperation, cmd );
273
274     if ( retval <= 32 )
275     {
276         return retval;
277     }
278
279     if (lpParameters)
280     {
281         strcat(cmd," ");
282         strcat(cmd,lpParameters);
283     }
284
285     dprintf_exec(stddeb,"ShellExecute:starting %s\n",cmd);
286     return WinExec(cmd,iShowCmd);
287 }
288
289 /*************************************************************************
290  *                              FindExecutable          [SHELL.21]
291  */
292 HINSTANCE FindExecutable(LPCSTR lpFile, LPCSTR lpDirectory, LPSTR lpResult)
293 {
294     HINSTANCE retval=31;    /* default - 'No association was found' */
295
296     dprintf_exec(stddeb, "FindExecutable: File %s, Dir %s\n", 
297                  (lpFile != NULL?lpFile:"-"), 
298                  (lpDirectory != NULL?lpDirectory:"-"));
299
300     lpResult[0]='\0'; /* Start off with an empty return string */
301
302     /* trap NULL parameters on entry */
303     if (( lpFile == NULL ) || ( lpDirectory == NULL ) || 
304         ( lpResult == NULL ))
305     {
306         /* FIXME - should throw a warning, perhaps! */
307         return 2; /* File not found. Close enough, I guess. */
308     }
309
310     retval = SHELL_FindExecutable( lpFile, lpDirectory, "open",
311                                   lpResult );
312
313     dprintf_exec(stddeb, "FindExecutable: returning %s\n", lpResult);
314     return retval;
315 }
316
317 static char AppName[128], AppMisc[1536];
318
319 /*************************************************************************
320  *                              AboutDlgProc            [SHELL.33]
321  */
322 LRESULT AboutDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
323 {
324   char Template[512], AppTitle[512];
325  
326   switch(msg) {
327    case WM_INITDIALOG:
328     SendDlgItemMessage32A(hWnd,stc1,STM_SETICON,lParam,0);
329     GetWindowText32A(hWnd, Template, sizeof(Template));
330     sprintf(AppTitle, Template, AppName);
331     SetWindowText32A(hWnd, AppTitle);
332     SetWindowText32A(GetDlgItem(hWnd,100), AppMisc);
333     return 1;
334     
335    case WM_COMMAND:
336     switch (wParam) {
337      case IDOK:
338       EndDialog(hWnd, TRUE);
339       return TRUE;
340     }
341     break;
342   }
343   return FALSE;
344 }
345
346 /*************************************************************************
347  *                              ShellAbout              [SHELL.22]
348  */
349 INT ShellAbout(HWND hWnd, LPCSTR szApp, LPCSTR szOtherStuff, HICON hIcon)
350 {
351     HANDLE handle;
352     BOOL bRet;
353
354     if (szApp) strncpy(AppName, szApp, sizeof(AppName));
355     else *AppName = 0;
356     AppName[sizeof(AppName)-1]=0;
357
358     if (szOtherStuff) strncpy(AppMisc, szOtherStuff, sizeof(AppMisc));
359     else *AppMisc = 0;
360     AppMisc[sizeof(AppMisc)-1]=0;
361
362     if (!hIcon) hIcon = LoadIcon(0,MAKEINTRESOURCE(OIC_WINEICON));
363     handle = SYSRES_LoadResource( SYSRES_DIALOG_SHELL_ABOUT_MSGBOX );
364     if (!handle) return FALSE;
365     bRet = DialogBoxIndirectParam16( WIN_GetWindowInstance( hWnd ),
366                                    handle, hWnd,
367                                    MODULE_GetWndProcEntry16("AboutDlgProc"), 
368                                    (LONG)hIcon );
369     SYSRES_FreeResource( handle );
370     return bRet;
371 }
372
373 /*************************************************************************
374  *                              SHELL_GetResourceTable
375  *
376  * FIXME: Implement GetPEResourceTable in w32sys.c and call it here.
377  */
378 BYTE* SHELL_GetResourceTable(HFILE hFile)
379 {
380   struct mz_header_s mz_header;
381   struct ne_header_s ne_header;
382   int           size;
383   
384   _llseek( hFile, 0, SEEK_SET );
385   if ((FILE_Read(hFile,&mz_header,sizeof(mz_header)) != sizeof(mz_header)) ||
386       (mz_header.mz_magic != MZ_SIGNATURE)) return (BYTE*)-1;
387
388   _llseek( hFile, mz_header.ne_offset, SEEK_SET );
389   if (FILE_Read( hFile, &ne_header, sizeof(ne_header) ) != sizeof(ne_header))
390       return NULL;
391
392   if (ne_header.ne_magic == PE_SIGNATURE) 
393      { fprintf(stdnimp,"Win32 FIXME: file %s line %i\n", __FILE__, __LINE__ );
394        return NULL; }
395
396   if (ne_header.ne_magic != NE_SIGNATURE) return NULL;
397
398   size = ne_header.rname_tab_offset - ne_header.resource_tab_offset;
399
400   if( size > sizeof(NE_TYPEINFO) )
401     {
402       BYTE* pTypeInfo = (BYTE*)xmalloc(size);
403
404       if( !pTypeInfo ) return NULL;
405
406       _llseek(hFile, mz_header.ne_offset+ne_header.resource_tab_offset, SEEK_SET);
407       if( FILE_Read( hFile, (char*)pTypeInfo, size) != size )
408         { free(pTypeInfo); return NULL; }
409       return pTypeInfo;
410     }
411   /* no resources */
412
413   return NULL;
414 }
415
416 /*************************************************************************
417  *                      SHELL_LoadResource
418  */
419 HANDLE  SHELL_LoadResource(HINSTANCE hInst, HFILE hFile, NE_NAMEINFO* pNInfo, WORD sizeShift)
420 {
421  BYTE*  ptr;
422  HANDLE handle = DirectResAlloc( hInst, 0x10, (DWORD)pNInfo->length << sizeShift);
423
424  if( (ptr = (BYTE*)GlobalLock16( handle )) )
425    {
426     _llseek( hFile, (DWORD)pNInfo->offset << sizeShift, SEEK_SET);
427      FILE_Read( hFile, (char*)ptr, pNInfo->length << sizeShift);
428      return handle;
429    }
430  return (HANDLE)0;
431 }
432
433 /*************************************************************************
434  *                      InternalExtractIcon             [SHELL.39]
435  *
436  * This abortion is called directly by Progman
437  */
438 HICON InternalExtractIcon(HINSTANCE hInstance, LPCSTR lpszExeFileName, UINT nIconIndex, WORD n )
439 {
440   HANDLE        hRet = 0;
441   HICON*        RetPtr = NULL;
442   BYTE*         pData;
443   OFSTRUCT      ofs;
444   HFILE         hFile = OpenFile( lpszExeFileName, &ofs, OF_READ );
445   
446   dprintf_reg(stddeb, "InternalExtractIcon(%04x, file '%s', start from %d, extract %d\n", 
447                        hInstance, lpszExeFileName, nIconIndex, n);
448
449   if( hFile == HFILE_ERROR || !n ) return 0;
450
451   hRet = GlobalAlloc16( GMEM_FIXED, sizeof(HICON)*n);
452   RetPtr = (HICON*)GlobalLock16(hRet);
453
454  *RetPtr = (n == 0xFFFF)? 0: 1;                         /* error return values */
455
456   pData = SHELL_GetResourceTable(hFile);
457   if( pData ) 
458     if( pData == (BYTE*)-1 )
459       {
460         /* FIXME: possible .ICO file */
461
462         fprintf(stddeb,"InternalExtractIcon: cannot handle file %s\n", lpszExeFileName);
463       }
464     else                                                /* got resource table */
465       {
466         UINT         iconDirCount = 0;
467         UINT         iconCount = 0;
468         NE_TYPEINFO* pTInfo = (NE_TYPEINFO*)(pData + 2);
469         NE_NAMEINFO* pIconStorage = NULL;
470         NE_NAMEINFO* pIconDir = NULL;
471
472         /* find icon directory and icon repository */
473
474         while( pTInfo->type_id && !(pIconStorage && pIconDir) )
475           {
476            if( pTInfo->type_id == NE_RSCTYPE_GROUP_ICON ) 
477                {
478                  iconDirCount = pTInfo->count;
479                  pIconDir = ((NE_NAMEINFO*)(pTInfo + 1));
480                  dprintf_reg(stddeb,"\tfound directory - %i icon families\n", iconDirCount);
481                }
482            if( pTInfo->type_id == NE_RSCTYPE_ICON ) 
483                { 
484                  iconCount = pTInfo->count;
485                  pIconStorage = ((NE_NAMEINFO*)(pTInfo + 1));
486                  dprintf_reg(stddeb,"\ttotal icons - %i\n", iconCount);
487                }
488            pTInfo = (NE_TYPEINFO *)((char*)(pTInfo+1)+pTInfo->count*sizeof(NE_NAMEINFO));
489           }
490
491         /* load resources and create icons */
492
493         if( pIconStorage && pIconDir )
494
495             if( nIconIndex == (UINT)-1 ) RetPtr[0] = iconDirCount;
496             else if( nIconIndex < iconDirCount )
497               {
498                   HANDLE hIcon;
499                   UINT   i, icon;
500
501                   if( n > iconDirCount - nIconIndex ) n = iconDirCount - nIconIndex;
502
503                   for( i = nIconIndex; i < nIconIndex + n; i++ ) 
504                      {
505                        hIcon = SHELL_LoadResource( hInstance, hFile, pIconDir + i, 
506                                                                   *(WORD*)pData );
507                        RetPtr[i-nIconIndex] = GetIconID( hIcon, 3 );
508                        GlobalFree16(hIcon); 
509                      }
510
511                   for( icon = nIconIndex; icon < nIconIndex + n; icon++ )
512                      {
513                        hIcon = 0;
514                        for( i = 0; i < iconCount; i++ )
515                           if( pIconStorage[i].id == (RetPtr[icon-nIconIndex] | 0x8000) )
516                               hIcon = SHELL_LoadResource( hInstance, hFile, pIconStorage + i,
517                                                                              *(WORD*)pData );
518                        RetPtr[icon-nIconIndex] = (hIcon)?CURSORICON_LoadHandler( hIcon, hInstance, FALSE ):0;
519                      }
520               }
521         free(pData);
522       }
523
524  _lclose( hFile );
525  
526   /* return array with icon handles */
527
528   return hRet;
529 }
530
531 /*************************************************************************
532  *                              ExtractIcon             [SHELL.34]
533  */
534 HICON ExtractIcon(HINSTANCE hInstance, LPCSTR lpszExeFileName, WORD nIconIndex)
535 {
536   HANDLE handle = InternalExtractIcon(hInstance,lpszExeFileName,nIconIndex, 1);
537
538   if( handle )
539     {
540       HICON* ptr = (HICON*)GlobalLock16(handle);
541       HICON  hIcon = *ptr;
542
543       GlobalFree16(handle);
544       return hIcon;
545     }
546   return 0;
547 }
548
549 /*************************************************************************
550  *                              ExtractAssociatedIcon   [SHELL.36]
551  * 
552  * Return icon for given file (either from file itself or from associated
553  * executable) and patch parameters if needed.
554  */
555 HICON ExtractAssociatedIcon(HINSTANCE hInst,LPSTR lpIconPath, LPWORD lpiIcon)
556 {
557     HICON hIcon = ExtractIcon(hInst, lpIconPath, *lpiIcon);
558
559     if( hIcon < 2 )
560       {
561
562         if( hIcon == 1 ) /* no icons found in given file */
563           {
564             char  tempPath[0x80];
565             UINT  uRet = FindExecutable(lpIconPath,NULL,tempPath);
566
567             if( uRet > 32 && tempPath[0] )
568               {
569                 strcpy(lpIconPath,tempPath);
570                 hIcon = ExtractIcon(hInst, lpIconPath, *lpiIcon);
571
572                 if( hIcon > 2 ) return hIcon;
573               }
574             else hIcon = 0;
575           }
576         
577         if( hIcon == 1 ) 
578           *lpiIcon = 2;   /* MSDOS icon - we found .exe but no icons in it */
579         else
580           *lpiIcon = 6;   /* generic icon - found nothing */
581
582         GetModuleFileName(hInst, lpIconPath, 0x80);
583         hIcon = LoadIcon( hInst, MAKEINTRESOURCE(*lpiIcon));
584       }
585
586     return hIcon;
587 }
588
589 /*************************************************************************
590  *                              FindEnvironmentString   [SHELL.38]
591  *
592  * Returns a pointer into the DOS environment... Ugh.
593  */
594 LPSTR SHELL_FindString(LPSTR lpEnv, LPCSTR entry)
595 {
596   UINT  l = strlen(entry); 
597   for( ; *lpEnv ; lpEnv+=strlen(lpEnv)+1 )
598      {
599        if( lstrncmpi32A(lpEnv, entry, l) ) continue;
600        
601        if( !*(lpEnv+l) )
602          return (lpEnv + l);            /* empty entry */
603        else if ( *(lpEnv+l)== '=' )
604          return (lpEnv + l + 1);
605      }
606   return NULL;
607 }
608
609 SEGPTR FindEnvironmentString(LPSTR str)
610 {
611  SEGPTR  spEnv = GetDOSEnvironment();
612  LPSTR  lpEnv = (LPSTR)PTR_SEG_TO_LIN(spEnv);
613  
614  LPSTR  lpString = (spEnv)?SHELL_FindString(lpEnv, str):NULL; 
615
616  if( lpString )         /*  offset should be small enough */
617      return spEnv + (lpString - lpEnv);
618
619  return (SEGPTR)NULL;
620 }
621
622 /*************************************************************************
623  *                              DoEnvironmentSubst      [SHELL.37]
624  *
625  * Replace %KEYWORD% in the str with the value of variable KEYWORD
626  * from "DOS" environment.
627  */
628 DWORD DoEnvironmentSubst(LPSTR str,WORD length)
629 {
630   LPSTR   lpEnv = (LPSTR)PTR_SEG_TO_LIN(GetDOSEnvironment());
631   LPSTR   lpBuffer = (LPSTR)xmalloc(length);
632   LPSTR   lpstr = str;
633   LPSTR   lpbstr = lpBuffer;
634
635   AnsiToOem(str,str);
636
637   dprintf_reg(stddeb,"DoEnvSubst: accept %s", str);
638
639   while( *lpstr && lpbstr - lpBuffer < length )
640    {
641      LPSTR lpend = lpstr;
642
643      if( *lpstr == '%' )
644        {
645           do { lpend++; } while( *lpend && *lpend != '%' );
646           if( *lpend == '%' && lpend - lpstr > 1 )      /* found key */
647             {
648                LPSTR lpKey;
649               *lpend = '\0';  
650                lpKey = SHELL_FindString(lpEnv, lpstr+1);
651                if( lpKey )                              /* found key value */
652                  {
653                    int l = strlen(lpKey);
654
655                    if( l > length - (lpbstr - lpBuffer) - 1 )
656                      {
657                        fprintf(stdnimp,"File %s, line %i: Env subst aborted - string too short\n", 
658                                         __FILE__, __LINE__);
659                       *lpend = '%';
660                        break;
661                      }
662                    strcpy(lpbstr, lpKey);
663                    lpbstr += l;
664                  }
665                else break;
666               *lpend = '%';
667                lpstr = lpend + 1;
668             }
669           else break;                                   /* back off and whine */
670
671           continue;
672        } 
673
674      *lpbstr++ = *lpstr++;
675    }
676
677  *lpbstr = '\0';
678   if( lpstr - str == strlen(str) )
679     {
680       strncpy(str, lpBuffer, length);
681       length = 1;
682     }
683   else
684       length = 0;
685
686   dprintf_reg(stddeb," return %s\n", str);
687
688   OemToAnsi(str,str);
689   free(lpBuffer);
690
691   /*  Return str length in the LOWORD
692    *  and 1 in HIWORD if subst was successful.
693    */
694  return (DWORD)MAKELONG(strlen(str), length);
695 }
696
697 /*************************************************************************
698  *                              RegisterShellHook       [SHELL.102]
699  */
700 int RegisterShellHook(void *ptr) 
701 {
702         dprintf_reg(stdnimp, "RegisterShellHook : Empty Stub !!!\n");
703         return 0;
704 }
705
706
707 /*************************************************************************
708  *                              ShellHookProc           [SHELL.103]
709  */
710 int ShellHookProc(void) 
711 {
712         dprintf_reg(stdnimp, "ShellHookProc : Empty Stub !!!\n");
713         return 0;
714 }