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