Added a first-cut version of MapVirtualKeyExW() that has the same
[wine] / dlls / setupapi / setupx_main.c
1 /*
2  *      SETUPX library
3  *
4  *      Copyright 1998,2000  Andreas Mohr
5  *
6  * FIXME: Rather non-functional functions for now.
7  *
8  * See:
9  * http://www.geocities.com/SiliconValley/Network/5317/drivers.html
10  * http://willemer.de/informatik/windows/inf_info.htm (German)
11  * http://www.microsoft.com/ddk/ddkdocs/win98ddk/devinst_12uw.htm
12  * DDK: setupx.h
13  * http://mmatrix.tripod.com/customsystemfolder/infsysntaxfull.html
14  * http://www.rdrop.com/~cary/html/inf_faq.html
15  *
16  * Stuff tested with:
17  * - rs405deu.exe (German Acroread 4.05 setup)
18  * - ie5setup.exe
19  * - Netmeeting
20  *
21  * FIXME:
22  * - check buflen
23  */
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include "winreg.h"
28 #include "wine/winuser16.h"
29 #include "setupx16.h"
30 #include "winerror.h"
31 #include "debugtools.h"
32
33 DEFAULT_DEBUG_CHANNEL(setupx);
34
35 /***********************************************************************
36  *              SURegOpenKey
37  */
38 DWORD WINAPI SURegOpenKey( HKEY hkey, LPCSTR lpszSubKey, LPHKEY retkey )
39 {
40     FIXME("(%x,%s,%p), semi-stub.\n",hkey,debugstr_a(lpszSubKey),retkey);
41     return RegOpenKeyA( hkey, lpszSubKey, retkey );
42 }
43
44 /***********************************************************************
45  *              SURegQueryValueEx
46  */
47 DWORD WINAPI SURegQueryValueEx( HKEY hkey, LPSTR lpszValueName,
48                                 LPDWORD lpdwReserved, LPDWORD lpdwType,
49                                 LPBYTE lpbData, LPDWORD lpcbData )
50 {
51     FIXME("(%x,%s,%p,%p,%p,%ld), semi-stub.\n",hkey,debugstr_a(lpszValueName),
52           lpdwReserved,lpdwType,lpbData,lpcbData?*lpcbData:0);
53     return RegQueryValueExA( hkey, lpszValueName, lpdwReserved, lpdwType,
54                                lpbData, lpcbData );
55 }
56
57 /***********************************************************************
58  *              InstallHinfSection
59  *
60  * hwnd = parent window
61  * hinst = instance of SETUPX.DLL
62  * lpszCmdLine = e.g. "DefaultInstall 132 C:\MYINSTALL\MYDEV.INF"
63  * Here "DefaultInstall" is the .inf file section to be installed (optional).
64  * 132 is the standard parameter, it seems.
65  * 133 means don't prompt user for reboot.
66  * 
67  * nCmdShow = nCmdShow of CreateProcess
68  * FIXME: is the return type correct ?
69  */
70 DWORD WINAPI InstallHinfSection16( HWND16 hwnd, HINSTANCE16 hinst, LPCSTR lpszCmdLine, INT16 nCmdShow)
71 {
72     FIXME("(%04x, %04x, %s, %d), stub.\n", hwnd, hinst, lpszCmdLine, nCmdShow);
73     return 0;
74 }
75
76 typedef struct
77 {
78     LPCSTR RegValName;
79     LPCSTR StdString; /* fallback string; sub dir of windows directory */
80 } LDID_DATA;
81
82 static const LDID_DATA LDID_Data[34] =
83 {
84     { /* 0 (LDID_NULL) -- not defined */
85         NULL,
86         NULL
87     },
88     { /* 1 (LDID_SRCPATH) = source of installation. hmm, what to do here ? */
89         "SourcePath", /* hmm, does SETUPX have to care about updating it ?? */
90         NULL
91     },
92     { /* 2 (LDID_SETUPTEMP) = setup temp dir */
93         "SetupTempDir",
94         NULL
95     },
96     { /* 3 (LDID_UNINSTALL) = uninstall backup dir */
97         "UninstallDir",
98         NULL
99     },
100     { /* 4 (LDID_BACKUP) = backup dir */
101         "BackupDir",
102         NULL
103     },
104     { /* 5 (LDID_SETUPSCRATCH) = setup scratch dir */
105         "SetupScratchDir",
106         NULL
107     },
108     { /* 6 -- not defined */
109         NULL,
110         NULL
111     },
112     { /* 7 -- not defined */
113         NULL,
114         NULL
115     },
116     { /* 8 -- not defined */
117         NULL,
118         NULL
119     },
120     { /* 9 -- not defined */
121         NULL,
122         NULL
123     },
124     { /* 10 (LDID_WIN) = windows dir */
125         "WinDir",
126         ""
127     },
128     { /* 11 (LDID_SYS) = system dir */
129         "SysDir",
130         NULL /* call GetSystemDirectory() instead */
131     },
132     { /* 12 (LDID_IOS) = IOSubSys dir */
133         NULL, /* FIXME: registry string ? */
134         "SYSTEM\\IOSUBSYS"
135     },
136     { /* 13 (LDID_CMD) = COMMAND dir */
137         NULL, /* FIXME: registry string ? */
138         "COMMAND"
139     },
140     { /* 14 (LDID_CPL) = control panel dir */
141         NULL,
142         ""
143     },
144     { /* 15 (LDID_PRINT) = windows printer dir */
145         NULL,
146         "SYSTEM" /* correct ?? */
147     },
148     { /* 16 (LDID_MAIL) = destination mail dir */
149         NULL,
150         ""
151     },
152     { /* 17 (LDID_INF) = INF dir */
153         "SetupScratchDir", /* correct ? */
154         "INF"
155     },
156     { /* 18 (LDID_HELP) = HELP dir */
157         NULL, /* ??? */
158         "HELP"
159     },
160     { /* 19 (LDID_WINADMIN) = Admin dir */
161         "WinAdminDir",
162         ""
163     },
164     { /* 20 (LDID_FONTS) = Fonts dir */
165         NULL, /* ??? */
166         "FONTS"
167     },
168     { /* 21 (LDID_VIEWERS) = Viewers */
169         NULL, /* ??? */
170         "SYSTEM\\VIEWERS"
171     },
172     { /* 22 (LDID_VMM32) = VMM32 dir */
173         NULL, /* ??? */
174         "SYSTEM\\VMM32"
175     },
176     { /* 23 (LDID_COLOR) = ICM dir */
177         "ICMPath",
178         "SYSTEM\\COLOR"
179     },
180     { /* 24 (LDID_APPS) = root of boot drive ? */
181         "AppsDir",
182         "C:\\"
183     },
184     { /* 25 (LDID_SHARED) = shared dir */
185         "SharedDir",
186         ""
187     },
188     { /* 26 (LDID_WINBOOT) = Windows boot dir */
189         "WinBootDir",
190         ""
191     },
192     { /* 27 (LDID_MACHINE) = machine specific files */
193         "MachineDir",
194         NULL
195     },
196     { /* 28 (LDID_HOST_WINBOOT) = Host Windows boot dir */
197         "HostWinBootDir",
198         NULL
199     },
200     { /* 29 -- not defined */
201         NULL,
202         NULL
203     },
204     { /* 30 (LDID_BOOT) = Root of boot drive */
205         "BootDir",
206         NULL
207     },
208     { /* 31 (LDID_BOOT_HOST) = Root of boot drive host */
209         "BootHost",
210         NULL
211     },
212     { /* 32 (LDID_OLD_WINBOOT) = subdir of root */
213         "OldWinBootDir",
214         NULL
215     },
216     { /* 33 (LDID_OLD_WIN) = old win dir */
217         "OldWinDir",
218         NULL
219     }
220     /* the rest (34-38) isn't too interesting, so I'll forget about it */
221 };
222
223 static void SETUPX_IsolateSubString(LPSTR *begin, LPSTR *end)
224 {
225     LPSTR p, q;
226
227     p = *begin;
228     q = *end;
229
230     while ((p < q) && ((*p == ' ') || (*p == '\t'))) p++;
231     while ((p < q) && (*p == '"')) p++;
232
233     while ((q-1 >= p) && ((*(q-1) == ' ') || (*(q-1) == '\t'))) q--;
234     while ((q-1 >= p) && (*(q-1) == '"')) q--;
235
236     *begin = p;
237     *end = q;
238 }
239
240 /*
241  * Example: HKLM,"Software\Microsoft\Windows\CurrentVersion","ProgramFilesDir",,"C:\"
242  */
243 static BOOL SETUPX_LookupRegistryString(LPSTR regstr, LPSTR buffer, DWORD buflen)
244 {
245     HANDLE heap = GetProcessHeap();
246     LPSTR items[5];
247     LPSTR p, q, next;
248     int len, n;
249     HKEY hkey, hsubkey;
250     DWORD dwType;
251
252     TRACE("retrieving '%s'\n", regstr);
253
254     p = regstr;
255
256     /* isolate root key, subkey, value, flag, defval */
257     for (n=0; n < 5; n++)
258     {
259         q = strchr(p, ',');
260         if (!q)
261         {
262             if (n == 4)
263                 q = p+strlen(p);
264             else
265                 return FALSE;
266         }
267         next = q+1;
268         if (q < regstr)
269             return FALSE;
270         SETUPX_IsolateSubString(&p, &q);
271         len = (int)q - (int)p;
272         items[n] = HeapAlloc(heap, 0, len+1);
273         strncpy(items[n], p, len);
274         items[n][len] = '\0';
275         p = next;
276     }
277     TRACE("got '%s','%s','%s','%s','%s'\n",
278                         items[0], items[1], items[2], items[3], items[4]);
279     
280     /* check root key */
281     if (!strcasecmp(items[0], "HKCR"))
282         hkey = HKEY_CLASSES_ROOT;
283     else
284     if (!strcasecmp(items[0], "HKCU"))
285         hkey = HKEY_CURRENT_USER;
286     else
287     if (!strcasecmp(items[0], "HKLM"))
288         hkey = HKEY_LOCAL_MACHINE;
289     else
290     if (!strcasecmp(items[0], "HKU"))
291         hkey = HKEY_USERS;
292     else
293     { /* HKR ? -> relative to key passed to GenInstallEx */
294         FIXME("unsupported regkey '%s'\n", items[0]);
295         goto regfailed;
296     }
297
298     if (RegOpenKeyA(hkey, items[1], &hsubkey) != ERROR_SUCCESS)
299         goto regfailed;
300
301     if (RegQueryValueExA(hsubkey, items[2], 0, &dwType, buffer, &buflen)
302                 != ERROR_SUCCESS)
303         goto regfailed;
304     goto done;
305
306 regfailed:
307     strcpy(buffer, items[4]); /* I don't care about buflen */
308 done:
309     for (n=0; n < 5; n++)
310         HeapFree(heap, 0, items[n]);
311     TRACE("return '%s'\n", buffer);
312     return TRUE;
313 }
314
315 /*
316  * Find the value of a custom LDID in a .inf file
317  * e.g. for 49301:
318  * 49300,49301=ProgramFilesDir,5
319  * -- profile section lookup -->
320  * [ProgramFilesDir]
321  * HKLM,"Software\Microsoft\Windows\CurrentVersion","ProgramFilesDir",,"%24%"
322  * -- GenFormStrWithoutPlaceHolders16 -->
323  * HKLM,"Software\Microsoft\Windows\CurrentVersion","ProgramFilesDir",,"C:\"
324  * -- registry lookup -->
325  * C:\Program Files (or C:\ if not found in registry)
326  * 
327  * FIXME:
328  * - maybe we ought to add a caching array for speed ? - I don't care :)
329  * - not sure whether the processing is correct - sometimes there are equal
330  *   LDIDs for both install and removal sections.
331  */
332 static BOOL SETUPX_TranslateCustomLDID(int ldid, LPSTR buffer, WORD buflen, INT16 hInf)
333 {
334     char ldidstr[6], sectionbuf[0xffff], entrybuf[0xffff], section[256];
335     LPCSTR filename;
336     LPSTR pSec, pEnt, pEqual, p, pEnd;
337     BOOL ret = FALSE;
338
339     sprintf(ldidstr, "%d", ldid);
340     filename = IP_GetFileName(hInf);
341     if (!GetPrivateProfileStringA(NULL, NULL, NULL,
342                                 sectionbuf, sizeof(sectionbuf), filename))
343     {
344         ERR("section buffer too small ?\n");
345         return FALSE;
346     }
347     for (pSec=sectionbuf; *pSec; pSec += strlen(pSec)+1)
348     {
349         if (!GetPrivateProfileSectionA(pSec,
350                                 entrybuf, sizeof(entrybuf), filename))
351         {
352             ERR("entry buffer too small ?\n");
353             return FALSE;
354         }
355         for (pEnt=entrybuf; *pEnt; pEnt += strlen(pEnt)+1)
356         {
357             if (strstr(pEnt, ldidstr))
358             {
359                 pEqual = strchr(pEnt, '=');
360                 if (!pEqual) /* crippled entry ?? */
361                     continue;
362
363                 /* make sure we found the LDID on left side of the equation */
364                 if (pEnt+strlen(ldidstr) <= pEqual)
365                 { /* found */
366
367                     /* but we don't want entries in the strings section */
368                     if (!strcasecmp(pSec, "Strings"))
369                         goto next_section;
370                     p = pEqual+1;
371                     while ((*p == ' ') || (*p == '\t')) p++;
372                     goto found;
373                 }
374             }
375         }
376 next_section:
377     }
378     return FALSE;
379 found:
380     TRACE("found entry '%s'\n", p);
381     /* strip off any flags we get
382      * FIXME: what are these flags used for ?? */
383     pEnd = strchr(p, ',');
384     strncpy(section, p, (int)pEnd - (int)p);
385     section[(int)pEnd - (int)p] = '\0';
386
387     /* get the location of the registry key from that section */
388     if (!GetPrivateProfileSectionA(section, entrybuf, sizeof(entrybuf), filename))
389     {
390         ERR("entrybuf too small ?\n");
391         return FALSE;
392     }
393     GenFormStrWithoutPlaceHolders16(sectionbuf, entrybuf, hInf);
394     ret = SETUPX_LookupRegistryString(sectionbuf, buffer, buflen);
395     TRACE("return '%s'\n", buffer);
396     return ret;
397 }
398
399 /*
400  * Translate a logical disk identifier (LDID) into its string representation
401  */
402 static BOOL SETUPX_TranslateLDID(int ldid, LPSTR buffer, WORD buflen, HINF16 hInf)
403 {
404     BOOL handled = FALSE;
405
406     if ((ldid >= LDID_SRCPATH) && (ldid <= LDID_OLD_WIN))
407     {
408         if (LDID_Data[ldid].RegValName)
409         {
410             HKEY hKey;
411
412             if (RegOpenKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup", &hKey) == ERROR_SUCCESS)
413             {
414                 DWORD type, len = buflen;
415
416                 if ( (RegQueryValueExA(hKey, LDID_Data[ldid].RegValName,
417                         NULL, &type, buffer, &len) == ERROR_SUCCESS)
418                 &&   (type == REG_SZ) )
419                 {
420                     TRACE("found value '%s' for LDID %d\n", buffer, ldid);
421                     handled = TRUE;
422                 }
423
424                 RegCloseKey(hKey);
425             }
426         }
427     }
428     if (!handled)
429     {
430         switch(ldid)
431         {
432             case LDID_SRCPATH:
433                 FIXME("LDID_SRCPATH: what exactly do we have to do here ?\n");
434                 break;
435             case LDID_SYS:
436                 GetSystemDirectoryA(buffer, buflen);
437                 handled = TRUE;
438                 break;
439             case LDID_APPS:
440             case LDID_MACHINE:
441             case LDID_HOST_WINBOOT:
442             case LDID_BOOT:
443             case LDID_BOOT_HOST:
444                 strncpy(buffer, "C:\\", buflen);
445                 buffer[buflen-1] = '\0';
446                 handled = TRUE;
447                 break;
448             default:
449                 if ( (ldid >= LDID_NULL) && (ldid <= LDID_OLD_WIN)
450                   && (LDID_Data[ldid].StdString) )
451                 {
452                     UINT len = GetWindowsDirectoryA(buffer, buflen);
453                     if (len <= buflen-1)
454                     {
455                         buffer += len;
456                         buflen -= len;
457                         *buffer++ = '\\';
458                         buflen--;
459                         strncpy(buffer, LDID_Data[ldid].StdString, buflen);
460                         buffer[buflen-1] = '\0';
461                     }
462                     handled = TRUE;
463                 }
464                 break;
465         }
466     }
467
468     if (!handled)
469         handled = SETUPX_TranslateCustomLDID(ldid, buffer, buflen, hInf);
470
471     if (!handled)
472         FIXME("unimplemented LDID %d\n", ldid);
473
474     return handled;
475 }
476
477 /***********************************************************************
478  *              GenFormStrWithoutPlaceHolders
479  */
480 void WINAPI GenFormStrWithoutPlaceHolders16( LPSTR szDst, LPCSTR szSrc, HINF16 hInf)
481 {
482     LPCSTR pSrc = szSrc, pSrcEnd = szSrc + strlen(szSrc);
483     LPSTR pDst = szDst, p, pPHBegin;
484     int count;
485     
486     FIXME("(%p, '%s', %04x), semi stub.\n", szDst, szSrc, hInf);
487     while (pSrc < pSrcEnd)
488     {
489         p = strchr(pSrc, '%');
490         if (p)
491         {
492             count = (int)p - (int)pSrc;
493             strncpy(pDst, pSrc, count);
494             pSrc += count;
495             pDst += count;
496             pPHBegin = p+1;
497             p = strchr(pPHBegin, '%');
498             if (p)
499             {
500                 char placeholder[80]; /* that really ought to be enough ;) */
501                 int ldid;
502                 BOOL done = TRUE;
503                 count = (int)p - (int)pPHBegin;
504                 strncpy(placeholder, pPHBegin, count);
505                 placeholder[count] = '\0';
506                 ldid = atoi(placeholder);
507                 if (ldid)
508                 {
509                     done = SETUPX_TranslateLDID(ldid, pDst, 256, hInf);
510                     if (done)
511                         pDst += strlen(pDst);
512                 }
513                 else
514                 { /* hmm, string placeholder. Need to look up
515                      in the [strings] section of the hInf */
516                     DWORD ret;
517                     char buf[256]; /* long enough ? */
518                     
519                     ret = GetPrivateProfileStringA("strings", placeholder, "",
520                                         buf, 256, IP_GetFileName(hInf));
521                     if (ret)
522                     {
523                         strcpy(pDst, buf);
524                         pDst += strlen(buf);
525                     }
526                     else
527                     {
528                         ERR("placeholder string '%s' not found !\n", placeholder);
529                         done = FALSE;
530                     }
531                 }
532                 if (!done)
533                 { /* copy raw placeholder string over */
534                     count = (int)p - (int)pPHBegin + 2;
535                     strncpy(pDst, pPHBegin-1, count);
536                     pDst += count;
537                     
538                 }
539                 pSrc = p+1;
540                 continue;
541             }
542         }
543
544         /* copy the remaining source string over */
545         strncpy(pDst, pSrc, (int)pSrcEnd - (int)pSrc + 1);
546         break;
547     }
548     TRACE("ret '%s'\n", szDst);
549 }
550
551 /***********************************************************************
552  *              CtlGetLddPath
553  */
554 RETERR16 WINAPI CtlGetLddPath16(LOGDISKID16 ldid, LPSTR szPath)
555 {
556     FIXME("(%04x, %p), stub.\n", ldid, szPath);
557     strcpy(szPath, "FIXME_BogusLddPath");
558     return OK;
559 }
560
561 /***********************************************************************
562  *              CtlSetLddPath
563  */
564 RETERR16 WINAPI CtlSetLddPath16(LOGDISKID16 ldid, LPSTR szPath)
565 {
566     FIXME("(%04x, '%s'), stub.\n", ldid, szPath);
567     return OK;
568 }
569
570 /***********************************************************************
571  *              vcpOpen
572  *
573  * p2 is "\001" for Netmeeting.
574  */
575 RETERR16 WINAPI vcpOpen16(LPWORD p1, LPWORD p2)
576 {
577     FIXME("(%p, %p), stub.\n", p1, p2);
578     return OK;
579 }
580
581 /***********************************************************************
582  *              vcpClose
583  */
584 RETERR16 WINAPI vcpClose16(WORD w1, WORD w2, WORD w3)
585 {
586     FIXME("(%04x, %04x %04x), stub.\n", w1, w2, w3);
587     return OK;
588 }
589
590 /***********************************************************************
591  *              GenInstall
592  */
593 RETERR16 WINAPI GenInstall16(HINF16 hInfFile, LPCSTR szInstallSection, WORD wFlags)
594 {
595     FIXME("(%04x, '%s', %04x), stub. This doesn't install anything yet ! Use native SETUPX.DLL instead !!\n", hInfFile, szInstallSection, wFlags);
596     return OK;
597 }