Some more cleanups.
[wine] / dlls / shell32 / shell.c
1 /*
2  *                              Shell Library Functions
3  *
4  *  1998 Marcus Meissner
5  */
6 #include <stdlib.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include <ctype.h>
10 #include "windef.h"
11 #include "wingdi.h"
12 #include "wine/winuser16.h"
13 #include "wine/winbase16.h"
14 #include "wine/shell16.h"
15 #include "winerror.h"
16 #include "dlgs.h"
17 #include "shellapi.h"
18 #include "shlobj.h"
19 #include "debugtools.h"
20 #include "winreg.h"
21 #include "shlwapi.h"
22
23 DEFAULT_DEBUG_CHANNEL(shell);
24 DECLARE_DEBUG_CHANNEL(exec);
25
26
27 typedef struct {     /* structure for dropped files */
28  WORD     wSize;
29  POINT16  ptMousePos;
30  BOOL16   fInNonClientArea;
31  /* memory block with filenames follows */
32 } DROPFILESTRUCT16, *LPDROPFILESTRUCT16;
33
34 static const char*      lpstrMsgWndCreated = "OTHERWINDOWCREATED";
35 static const char*      lpstrMsgWndDestroyed = "OTHERWINDOWDESTROYED";
36 static const char*      lpstrMsgShellActivate = "ACTIVATESHELLWINDOW";
37
38 static HWND16   SHELL_hWnd = 0;
39 static HHOOK    SHELL_hHook = 0;
40 static UINT16   uMsgWndCreated = 0;
41 static UINT16   uMsgWndDestroyed = 0;
42 static UINT16   uMsgShellActivate = 0;
43 HINSTANCE16     SHELL_hInstance = 0;
44 HINSTANCE SHELL_hInstance32;
45 static int SHELL_Attach = 0;
46
47 /***********************************************************************
48  * SHELL_DllEntryPoint [SHELL.entry]
49  *
50  * Initialization code for shell.dll. Automatically loads the
51  * 32-bit shell32.dll to allow thunking up to 32-bit code.
52  *
53  * RETURNS:
54  */
55 BOOL WINAPI SHELL_DllEntryPoint(DWORD Reason, HINSTANCE16 hInst,
56                                 WORD ds, WORD HeapSize, DWORD res1, WORD res2)
57 {
58     TRACE("(%08lx, %04x, %04x, %04x, %08lx, %04x)\n",
59           Reason, hInst, ds, HeapSize, res1, res2);
60
61     switch(Reason)
62     {
63     case DLL_PROCESS_ATTACH:
64         if (SHELL_Attach++) break;
65         SHELL_hInstance = hInst;
66         if(!SHELL_hInstance32)
67         {
68             if(!(SHELL_hInstance32 = LoadLibraryA("shell32.dll")))
69             {
70                 ERR("Could not load sibling shell32.dll\n");
71                 return FALSE;
72             }
73         }
74         break;
75
76     case DLL_PROCESS_DETACH:
77         if(!--SHELL_Attach)
78         {
79             SHELL_hInstance = 0;
80             if(SHELL_hInstance32)
81                 FreeLibrary(SHELL_hInstance32);
82         }
83         break;
84     }
85     return TRUE;
86 }
87
88 /*************************************************************************
89  *                              DragAcceptFiles16               [SHELL.9]
90  */
91 void WINAPI DragAcceptFiles16(HWND16 hWnd, BOOL16 b)
92 {
93   DragAcceptFiles(hWnd, b);
94 }
95
96 /*************************************************************************
97  *                              DragQueryFile16         [SHELL.11]
98  */
99 UINT16 WINAPI DragQueryFile16(
100         HDROP16 hDrop,
101         WORD wFile,
102         LPSTR lpszFile,
103         WORD wLength)
104 {
105         LPSTR lpDrop;
106         UINT i = 0;
107         LPDROPFILESTRUCT16 lpDropFileStruct = (LPDROPFILESTRUCT16) GlobalLock16(hDrop); 
108    
109         TRACE("(%04x, %x, %p, %u)\n", hDrop,wFile,lpszFile,wLength);
110     
111         if(!lpDropFileStruct) goto end;
112     
113         lpDrop = (LPSTR) lpDropFileStruct + lpDropFileStruct->wSize;
114         wFile = (wFile==0xffff) ? 0xffffffff : wFile;
115
116         while (i++ < wFile)
117         {
118           while (*lpDrop++); /* skip filename */
119           if (!*lpDrop) 
120           {
121             i = (wFile == 0xFFFFFFFF) ? i : 0; 
122             goto end;
123           }
124         }
125     
126         i = strlen(lpDrop);
127         i++;
128         if (!lpszFile ) goto end;   /* needed buffer size */
129         i = (wLength > i) ? i : wLength;
130         lstrcpynA (lpszFile,  lpDrop,  i);
131 end:
132         GlobalUnlock16(hDrop);
133         return i;
134 }
135
136 /*************************************************************************
137  *                              DragFinish16            [SHELL.12]
138  */
139 void WINAPI DragFinish16(HDROP16 h)
140 {
141     TRACE("\n");
142     GlobalFree16((HGLOBAL16)h);
143 }
144
145
146 /*************************************************************************
147  *                              DragQueryPoint16                [SHELL.13]
148  */
149 BOOL16 WINAPI DragQueryPoint16(HDROP16 hDrop, POINT16 *p)
150 {
151   LPDROPFILESTRUCT16 lpDropFileStruct;  
152   BOOL16           bRet;
153   TRACE("\n");
154   lpDropFileStruct = (LPDROPFILESTRUCT16) GlobalLock16(hDrop);
155   
156   memcpy(p,&lpDropFileStruct->ptMousePos,sizeof(POINT16));
157   bRet = lpDropFileStruct->fInNonClientArea;
158   
159   GlobalUnlock16(hDrop);
160   return bRet;
161 }
162
163 /*************************************************************************
164  *      SHELL_FindExecutable [Internal]
165  *
166  * Utility for code sharing between FindExecutable and ShellExecute
167  */
168 HINSTANCE SHELL_FindExecutable( LPCSTR lpFile, 
169                                          LPCSTR lpOperation,
170                                          LPSTR lpResult)
171 { char *extension = NULL; /* pointer to file extension */
172     char tmpext[5];         /* local copy to mung as we please */
173     char filetype[256];     /* registry name for this filetype */
174     LONG filetypelen=256;   /* length of above */
175     char command[256];      /* command from registry */
176     LONG commandlen=256;    /* This is the most DOS can handle :) */
177     char buffer[256];       /* Used to GetProfileString */
178     HINSTANCE retval=31;  /* default - 'No association was found' */
179     char *tok;              /* token pointer */
180     int i;                  /* random counter */
181     char xlpFile[256] = ""; /* result of SearchPath */
182
183   TRACE("%s\n", (lpFile != NULL?lpFile:"-") );
184
185     lpResult[0]='\0'; /* Start off with an empty return string */
186
187     /* trap NULL parameters on entry */
188     if (( lpFile == NULL ) || ( lpResult == NULL ) || ( lpOperation == NULL ))
189   { WARN_(exec)("(lpFile=%s,lpResult=%s,lpOperation=%s): NULL parameter\n",
190            lpFile, lpOperation, lpResult);
191         return 2; /* File not found. Close enough, I guess. */
192     }
193
194     if (SearchPathA( NULL, lpFile,".exe",sizeof(xlpFile),xlpFile,NULL))
195   { TRACE("SearchPathA returned non-zero\n");
196         lpFile = xlpFile;
197     }
198
199     /* First thing we need is the file's extension */
200     extension = strrchr( xlpFile, '.' ); /* Assume last "." is the one; */
201                                         /* File->Run in progman uses */
202                                         /* .\FILE.EXE :( */
203   TRACE("xlpFile=%s,extension=%s\n", xlpFile, extension);
204
205     if ((extension == NULL) || (extension == &xlpFile[strlen(xlpFile)]))
206   { WARN("Returning 31 - No association\n");
207         return 31; /* no association */
208     }
209
210     /* Make local copy & lowercase it for reg & 'programs=' lookup */
211     lstrcpynA( tmpext, extension, 5 );
212     CharLowerA( tmpext );
213   TRACE("%s file\n", tmpext);
214     
215     /* Three places to check: */
216     /* 1. win.ini, [windows], programs (NB no leading '.') */
217     /* 2. Registry, HKEY_CLASS_ROOT\<filetype>\shell\open\command */
218     /* 3. win.ini, [extensions], extension (NB no leading '.' */
219     /* All I know of the order is that registry is checked before */
220     /* extensions; however, it'd make sense to check the programs */
221     /* section first, so that's what happens here. */
222
223     /* See if it's a program - if GetProfileString fails, we skip this
224      * section. Actually, if GetProfileString fails, we've probably
225      * got a lot more to worry about than running a program... */
226     if ( GetProfileStringA("windows", "programs", "exe pif bat com",
227                                                   buffer, sizeof(buffer)) > 0 )
228   { for (i=0;i<strlen(buffer); i++) buffer[i]=tolower(buffer[i]);
229
230                 tok = strtok(buffer, " \t"); /* ? */
231                 while( tok!= NULL)
232                   {
233                         if (strcmp(tok, &tmpext[1])==0) /* have to skip the leading "." */
234                           {
235                                 strcpy(lpResult, xlpFile);
236                                 /* Need to perhaps check that the file has a path
237                                  * attached */
238         TRACE("found %s\n", lpResult);
239         return 33;
240
241                 /* Greater than 32 to indicate success FIXME According to the
242                  * docs, I should be returning a handle for the
243                  * executable. Does this mean I'm supposed to open the
244                  * executable file or something? More RTFM, I guess... */
245                           }
246                         tok=strtok(NULL, " \t");
247                   }
248           }
249
250     /* Check registry */
251     if (RegQueryValue16( HKEY_CLASSES_ROOT, tmpext, filetype,
252                          &filetypelen ) == ERROR_SUCCESS )
253     {
254         filetype[filetypelen]='\0';
255         TRACE("File type: %s\n", filetype);
256
257         /* Looking for ...buffer\shell\lpOperation\command */
258         strcat( filetype, "\\shell\\" );
259         strcat( filetype, lpOperation );
260         strcat( filetype, "\\command" );
261         
262         if (RegQueryValue16( HKEY_CLASSES_ROOT, filetype, command,
263                              &commandlen ) == ERROR_SUCCESS )
264         {
265             LPSTR tmp;
266             char param[256];
267             LONG paramlen = 256;
268
269
270             /* Get the parameters needed by the application 
271                from the associated ddeexec key */ 
272             tmp = strstr(filetype,"command");
273             tmp[0] = '\0';
274             strcat(filetype,"ddeexec");
275
276             if(RegQueryValue16( HKEY_CLASSES_ROOT, filetype, param,&paramlen ) == ERROR_SUCCESS)
277             {
278               strcat(command," ");
279               strcat(command,param);
280               commandlen += paramlen;
281             }
282
283             /* Is there a replace() function anywhere? */
284             command[commandlen]='\0';
285             strcpy( lpResult, command );
286             tok=strstr( lpResult, "%1" );
287             if (tok != NULL)
288             {
289                 tok[0]='\0'; /* truncate string at the percent */
290                 strcat( lpResult, xlpFile ); /* what if no dir in xlpFile? */
291                 tok=strstr( command, "%1" );
292                 if ((tok!=NULL) && (strlen(tok)>2))
293                 {
294                     strcat( lpResult, &tok[2] );
295                 }
296             }
297             retval=33; /* FIXME see above */
298         }
299     }
300     else /* Check win.ini */
301     {
302         /* Toss the leading dot */
303         extension++;
304         if ( GetProfileStringA( "extensions", extension, "", command,
305                                   sizeof(command)) > 0)
306           {
307                 if (strlen(command)!=0)
308                   {
309                         strcpy( lpResult, command );
310                         tok=strstr( lpResult, "^" ); /* should be ^.extension? */
311                         if (tok != NULL)
312                           {
313                                 tok[0]='\0';
314                                 strcat( lpResult, xlpFile ); /* what if no dir in xlpFile? */
315                                 tok=strstr( command, "^" ); /* see above */
316                                 if ((tok != NULL) && (strlen(tok)>5))
317                                   {
318                                         strcat( lpResult, &tok[5]);
319                                   }
320                           }
321                         retval=33; /* FIXME - see above */
322                   }
323           }
324         }
325
326     TRACE("returning %s\n", lpResult);
327     return retval;
328 }
329
330 /*************************************************************************
331  *                              ShellExecute16          [SHELL.20]
332  */
333 HINSTANCE16 WINAPI ShellExecute16( HWND16 hWnd, LPCSTR lpOperation,
334                                    LPCSTR lpFile, LPCSTR lpParameters,
335                                    LPCSTR lpDirectory, INT16 iShowCmd )
336 {   HINSTANCE16 retval=31;
337     char old_dir[1024];
338     char cmd[1024] = "";
339
340     TRACE("(%04x,'%s','%s','%s','%s',%x)\n",
341                 hWnd, lpOperation ? lpOperation:"<null>", lpFile ? lpFile:"<null>",
342                 lpParameters ? lpParameters : "<null>", 
343                 lpDirectory ? lpDirectory : "<null>", iShowCmd);
344
345     if (lpFile==NULL) return 0; /* should not happen */
346     if (lpOperation==NULL) /* default is open */
347       lpOperation="open";
348
349     if (lpDirectory)
350     { GetCurrentDirectoryA( sizeof(old_dir), old_dir );
351         SetCurrentDirectoryA( lpDirectory );
352     }
353
354     /* First try to execute lpFile with lpParameters directly */ 
355     strcpy(cmd,lpFile);
356     strcat(cmd,lpParameters ? lpParameters : "");
357
358     retval = WinExec16( cmd, iShowCmd );
359
360     /* Unable to execute lpFile directly
361        Check if we can match an application to lpFile */
362     if(retval < 32)
363     { 
364       cmd[0] = '\0';
365       retval = SHELL_FindExecutable( lpFile, lpOperation, cmd );
366
367       if (retval > 32)  /* Found */
368       {
369         if (lpParameters)
370         {
371             strcat(cmd," ");
372             strcat(cmd,lpParameters);
373         }
374         retval = WinExec16( cmd, iShowCmd );
375       }
376       else if(PathIsURLA((LPSTR)lpFile))    /* File not found, check for URL */
377       {
378         char lpstrProtocol[256];
379         LONG cmdlen = 512;
380         LPSTR lpstrRes;
381         INT iSize;
382       
383         lpstrRes = strchr(lpFile,':');
384         iSize = lpstrRes - lpFile;
385         
386         /* Looking for ...protocol\shell\lpOperation\command */
387         strncpy(lpstrProtocol,lpFile,iSize);
388         lpstrProtocol[iSize]='\0';
389         strcat( lpstrProtocol, "\\shell\\" );
390         strcat( lpstrProtocol, lpOperation );
391         strcat( lpstrProtocol, "\\command" );
392         
393         /* Remove File Protocol from lpFile */
394         /* In the case file://path/file     */
395         if(!strncasecmp(lpFile,"file",iSize))
396         {
397           lpFile += iSize;
398           while(*lpFile == ':') lpFile++;
399         }
400         
401
402         /* Get the application for the protocol and execute it */
403         if (RegQueryValue16( HKEY_CLASSES_ROOT, lpstrProtocol, cmd,
404                              &cmdlen ) == ERROR_SUCCESS )
405         {
406             LPSTR tok;
407             LPSTR tmp;
408             char param[256] = "";
409             LONG paramlen = 256;
410
411             /* Get the parameters needed by the application 
412                from the associated ddeexec key */ 
413             tmp = strstr(lpstrProtocol,"command");
414             tmp[0] = '\0';
415             strcat(lpstrProtocol,"ddeexec");
416
417             if(RegQueryValue16( HKEY_CLASSES_ROOT, lpstrProtocol, param,&paramlen ) == ERROR_SUCCESS)
418             {
419               strcat(cmd," ");
420               strcat(cmd,param);
421               cmdlen += paramlen;
422             }
423             
424             /* Is there a replace() function anywhere? */
425             cmd[cmdlen]='\0';
426
427             tok=strstr( cmd, "%1" );
428             if (tok != NULL)
429             {
430                 tok[0]='\0'; /* truncate string at the percent */
431                 strcat( cmd, lpFile ); /* what if no dir in xlpFile? */
432                 tok=strstr( cmd, "%1" );
433                 if ((tok!=NULL) && (strlen(tok)>2))
434                 {
435                     strcat( cmd, &tok[2] );
436                 }
437             }
438  
439             retval = WinExec16( cmd, iShowCmd );
440         }
441       }
442     /* Check if file specified is in the form www.??????.*** */
443       else if(!strncasecmp(lpFile,"www",3))
444       {
445         /* if so, append lpFile http:// and call ShellExecute */ 
446         char lpstrTmpFile[256] = "http://" ;
447         strcat(lpstrTmpFile,lpFile);
448         retval = ShellExecuteA(hWnd,lpOperation,lpstrTmpFile,NULL,NULL,0);
449       }
450     }
451     if (lpDirectory)
452       SetCurrentDirectoryA( old_dir );
453     return retval;
454 }
455
456 /*************************************************************************
457  *             FindExecutable16   (SHELL.21)
458  */
459 HINSTANCE16 WINAPI FindExecutable16( LPCSTR lpFile, LPCSTR lpDirectory,
460                                      LPSTR lpResult )
461 { return (HINSTANCE16)FindExecutableA( lpFile, lpDirectory, lpResult );
462 }
463
464
465 /*************************************************************************
466  *             AboutDlgProc16   (SHELL.33)
467  */
468 BOOL16 WINAPI AboutDlgProc16( HWND16 hWnd, UINT16 msg, WPARAM16 wParam,
469                                LPARAM lParam )
470 { return AboutDlgProc( hWnd, msg, wParam, lParam );
471 }
472
473
474 /*************************************************************************
475  *             ShellAbout16   (SHELL.22)
476  */
477 BOOL16 WINAPI ShellAbout16( HWND16 hWnd, LPCSTR szApp, LPCSTR szOtherStuff,
478                             HICON16 hIcon )
479 { return ShellAboutA( hWnd, szApp, szOtherStuff, hIcon );
480 }
481
482 /*************************************************************************
483  *                      InternalExtractIcon             [SHELL.39]
484  *
485  * This abortion is called directly by Progman
486  */
487 HGLOBAL16 WINAPI InternalExtractIcon16(HINSTANCE16 hInstance,
488                                      LPCSTR lpszExeFileName, UINT16 nIconIndex, WORD n )
489 {
490     HGLOBAL16 hRet = 0;
491     HICON16 *RetPtr = NULL;
492     OFSTRUCT ofs;
493     HFILE hFile;
494
495         TRACE("(%04x,file %s,start %d,extract %d\n", 
496                        hInstance, lpszExeFileName, nIconIndex, n);
497
498         if( !n )
499           return 0;
500
501         hFile = OpenFile( lpszExeFileName, &ofs, OF_READ );
502
503         hRet = GlobalAlloc16( GMEM_FIXED | GMEM_ZEROINIT, sizeof(HICON16)*n);
504         RetPtr = (HICON16*)GlobalLock16(hRet);
505
506         if (hFile == HFILE_ERROR)
507         { /* not found - load from builtin module if available */
508           HINSTANCE hInst = (HINSTANCE)LoadLibrary16(lpszExeFileName);
509
510           if (hInst < 32) /* hmm, no Win16 module - try Win32 :-) */
511             hInst = LoadLibraryA(lpszExeFileName);
512           if (hInst)
513           {
514             int i;
515             for (i=nIconIndex; i < nIconIndex + n; i++)
516               RetPtr[i-nIconIndex] =
517                       (HICON16)LoadIconA(hInst, (LPCSTR)(DWORD)i);
518             FreeLibrary(hInst);
519             return hRet;
520           }
521           GlobalFree16( hRet );
522           return 0;
523         }
524
525         if (nIconIndex == (UINT16)-1)  /* get number of icons */
526         {
527             RetPtr[0] = PrivateExtractIconsA( ofs.szPathName, -1, 0, 0, NULL, 0, 0, 0 );
528         }
529         else
530         {
531             HRESULT res;
532             HICON *icons;
533             icons = HeapAlloc( GetProcessHeap(), 0, n * sizeof(*icons) );
534             res = PrivateExtractIconsA( ofs.szPathName, nIconIndex,
535                                         GetSystemMetrics(SM_CXICON),
536                                         GetSystemMetrics(SM_CYICON),
537                                         icons, 0, n, 0 );
538             if (!res)
539             {
540                 int i;
541                 for (i = 0; i < n; i++) RetPtr[i] = (HICON16)icons[i];
542             }
543             else
544             {
545                 GlobalFree16( hRet );
546                 hRet = 0;
547             }
548             HeapFree( GetProcessHeap(), 0, icons );
549         }
550         return hRet;
551 }
552
553 /*************************************************************************
554  *             ExtractIcon16   (SHELL.34)
555  */
556 HICON16 WINAPI ExtractIcon16( HINSTANCE16 hInstance, LPCSTR lpszExeFileName,
557         UINT16 nIconIndex )
558 {   TRACE("\n");
559     return ExtractIconA( hInstance, lpszExeFileName, nIconIndex );
560 }
561
562 /*************************************************************************
563  *             ExtractIconEx16   (SHELL.40)
564  */
565 HICON16 WINAPI ExtractIconEx16(
566         LPCSTR lpszFile, INT16 nIconIndex, HICON16 *phiconLarge,
567         HICON16 *phiconSmall, UINT16 nIcons
568 ) {
569     HICON       *ilarge,*ismall;
570     UINT16      ret;
571     int         i;
572
573     if (phiconLarge)
574         ilarge = (HICON*)HeapAlloc(GetProcessHeap(),0,nIcons*sizeof(HICON));
575     else
576         ilarge = NULL;
577     if (phiconSmall)
578         ismall = (HICON*)HeapAlloc(GetProcessHeap(),0,nIcons*sizeof(HICON));
579     else
580         ismall = NULL;
581     ret = ExtractIconExA(lpszFile,nIconIndex,ilarge,ismall,nIcons);
582     if (ilarge) {
583         for (i=0;i<nIcons;i++)
584             phiconLarge[i]=ilarge[i];
585         HeapFree(GetProcessHeap(),0,ilarge);
586     }
587     if (ismall) {
588         for (i=0;i<nIcons;i++)
589             phiconSmall[i]=ismall[i];
590         HeapFree(GetProcessHeap(),0,ismall);
591     }
592     return ret;
593 }
594
595 /*************************************************************************
596  *                              ExtractAssociatedIcon   [SHELL.36]
597  * 
598  * Return icon for given file (either from file itself or from associated
599  * executable) and patch parameters if needed.
600  */
601 HICON16 WINAPI ExtractAssociatedIcon16(HINSTANCE16 hInst, LPSTR lpIconPath, LPWORD lpiIcon)
602 {       HICON16 hIcon;
603         WORD wDummyIcon = 0;
604
605         TRACE("\n");
606
607         if(lpiIcon == NULL)
608             lpiIcon = &wDummyIcon;
609
610         hIcon = ExtractIcon16(hInst, lpIconPath, *lpiIcon);
611
612         if( hIcon < 2 )
613         { if( hIcon == 1 ) /* no icons found in given file */
614           { char  tempPath[0x80];
615             UINT16  uRet = FindExecutable16(lpIconPath,NULL,tempPath);
616
617             if( uRet > 32 && tempPath[0] )
618             { strcpy(lpIconPath,tempPath);
619               hIcon = ExtractIcon16(hInst, lpIconPath, *lpiIcon);
620               if( hIcon > 2 ) 
621                 return hIcon;
622             }
623             else hIcon = 0;
624           }
625
626           if( hIcon == 1 ) 
627             *lpiIcon = 2;   /* MSDOS icon - we found .exe but no icons in it */
628           else
629             *lpiIcon = 6;   /* generic icon - found nothing */
630
631           GetModuleFileName16(hInst, lpIconPath, 0x80);
632           hIcon = LoadIconA( hInst, MAKEINTRESOURCEA(*lpiIcon));
633         }
634         return hIcon;
635 }
636
637 /*************************************************************************
638  *                              ExtractAssociatedIconA
639  * 
640  * Return icon for given file (either from file itself or from associated
641  * executable) and patch parameters if needed.
642  */
643 HICON WINAPI ExtractAssociatedIconA(HINSTANCE hInst, LPSTR lpIconPath, LPWORD lpiIcon)
644 {       TRACE("\n");
645         return ExtractAssociatedIcon16(hInst,lpIconPath,lpiIcon);
646 }
647
648 /*************************************************************************
649  *                              FindEnvironmentString   [SHELL.38]
650  *
651  * Returns a pointer into the DOS environment... Ugh.
652  */
653 LPSTR SHELL_FindString(LPSTR lpEnv, LPCSTR entry)
654 { UINT16 l;
655
656   TRACE("\n");
657
658   l = strlen(entry); 
659   for( ; *lpEnv ; lpEnv+=strlen(lpEnv)+1 )
660   { if( strncasecmp(lpEnv, entry, l) ) 
661       continue;
662         if( !*(lpEnv+l) )
663             return (lpEnv + l);                 /* empty entry */
664         else if ( *(lpEnv+l)== '=' )
665             return (lpEnv + l + 1);
666     }
667     return NULL;
668 }
669
670 /**********************************************************************/
671
672 SEGPTR WINAPI FindEnvironmentString16(LPSTR str)
673 { SEGPTR  spEnv;
674   LPSTR lpEnv,lpString;
675   TRACE("\n");
676     
677   spEnv = GetDOSEnvironment16();
678
679   lpEnv = MapSL(spEnv);
680   lpString = (spEnv)?SHELL_FindString(lpEnv, str):NULL; 
681
682     if( lpString )              /*  offset should be small enough */
683         return spEnv + (lpString - lpEnv);
684     return (SEGPTR)NULL;
685 }
686
687 /*************************************************************************
688  *                              DoEnvironmentSubst      [SHELL.37]
689  *
690  * Replace %KEYWORD% in the str with the value of variable KEYWORD
691  * from "DOS" environment.
692  */
693 DWORD WINAPI DoEnvironmentSubst16(LPSTR str,WORD length)
694 {
695   LPSTR   lpEnv = MapSL(GetDOSEnvironment16());
696   LPSTR   lpBuffer = (LPSTR)HeapAlloc( GetProcessHeap(), 0, length);
697   LPSTR   lpstr = str;
698   LPSTR   lpbstr = lpBuffer;
699
700   CharToOemA(str,str);
701
702   TRACE("accept %s\n", str);
703
704   while( *lpstr && lpbstr - lpBuffer < length )
705    {
706      LPSTR lpend = lpstr;
707
708      if( *lpstr == '%' )
709        {
710           do { lpend++; } while( *lpend && *lpend != '%' );
711           if( *lpend == '%' && lpend - lpstr > 1 )      /* found key */
712             {
713                LPSTR lpKey;
714               *lpend = '\0';  
715                lpKey = SHELL_FindString(lpEnv, lpstr+1);
716                if( lpKey )                              /* found key value */
717                  {
718                    int l = strlen(lpKey);
719
720                    if( l > length - (lpbstr - lpBuffer) - 1 )
721                      {
722            WARN("-- Env subst aborted - string too short\n");
723                       *lpend = '%';
724                        break;
725                      }
726                    strcpy(lpbstr, lpKey);
727                    lpbstr += l;
728                  }
729                else break;
730               *lpend = '%';
731                lpstr = lpend + 1;
732             }
733           else break;                                   /* back off and whine */
734
735           continue;
736        } 
737
738      *lpbstr++ = *lpstr++;
739    }
740
741  *lpbstr = '\0';
742   if( lpstr - str == strlen(str) )
743     {
744       strncpy(str, lpBuffer, length);
745       length = 1;
746     }
747   else
748       length = 0;
749
750   TRACE("-- return %s\n", str);
751
752   OemToCharA(str,str);
753   HeapFree( GetProcessHeap(), 0, lpBuffer);
754
755   /*  Return str length in the LOWORD
756    *  and 1 in HIWORD if subst was successful.
757    */
758  return (DWORD)MAKELONG(strlen(str), length);
759 }
760
761 /*************************************************************************
762  *                              ShellHookProc           [SHELL.103]
763  * System-wide WH_SHELL hook.
764  */
765 LRESULT WINAPI ShellHookProc16(INT16 code, WPARAM16 wParam, LPARAM lParam)
766 {
767     TRACE("%i, %04x, %08x\n", code, wParam, 
768                                                       (unsigned)lParam );
769     if( SHELL_hHook && SHELL_hWnd )
770     {
771         UINT16  uMsg = 0;
772         switch( code )
773         {
774             case HSHELL_WINDOWCREATED:          uMsg = uMsgWndCreated;   break;
775             case HSHELL_WINDOWDESTROYED:        uMsg = uMsgWndDestroyed; break;
776             case HSHELL_ACTIVATESHELLWINDOW:    uMsg = uMsgShellActivate;
777         }
778         PostMessageA( SHELL_hWnd, uMsg, wParam, 0 );
779     }
780     return CallNextHookEx16( WH_SHELL, code, wParam, lParam );
781 }
782
783 /*************************************************************************
784  *                              RegisterShellHook       [SHELL.102]
785  */
786 BOOL WINAPI RegisterShellHook16(HWND16 hWnd, UINT16 uAction)
787
788     TRACE("%04x [%u]\n", hWnd, uAction );
789
790     switch( uAction )
791     { 
792     case 2:  /* register hWnd as a shell window */
793         if( !SHELL_hHook )
794         { 
795             HMODULE16 hShell = GetModuleHandle16( "SHELL" );
796             HOOKPROC16 hookProc = (HOOKPROC16)GetProcAddress16( hShell, (LPCSTR)103 );
797             SHELL_hHook = SetWindowsHookEx16( WH_SHELL, hookProc, hShell, 0 );
798             if ( SHELL_hHook )
799             { 
800                 uMsgWndCreated = RegisterWindowMessageA( lpstrMsgWndCreated );
801                 uMsgWndDestroyed = RegisterWindowMessageA( lpstrMsgWndDestroyed );
802                 uMsgShellActivate = RegisterWindowMessageA( lpstrMsgShellActivate );
803             } 
804             else 
805                 WARN("-- unable to install ShellHookProc()!\n");
806         }
807
808         if ( SHELL_hHook )
809             return ((SHELL_hWnd = hWnd) != 0);
810         break;
811
812     default:
813         WARN("-- unknown code %i\n", uAction );
814         SHELL_hWnd = 0; /* just in case */
815     }
816     return FALSE;
817 }
818
819
820 /***********************************************************************
821  *           DriveType16   (SHELL.262)
822  */
823 UINT16 WINAPI DriveType16( UINT16 drive )
824 {
825     UINT ret;
826     char path[] = "A:\\";
827     path[0] += drive;
828     ret = GetDriveTypeA(path);
829     switch(ret)  /* some values are not supported in Win16 */
830     {
831     case DRIVE_CDROM:
832         ret = DRIVE_REMOTE;
833         break;
834     case DRIVE_NO_ROOT_DIR:
835         ret = DRIVE_UNKNOWN;
836         break;
837     }
838     return ret;
839 }