| /* |
| * SETUPX library |
| * |
| * Copyright 1998,2000 Andreas Mohr |
| * |
| * FIXME: Rather non-functional functions for now. |
| * |
| * See: |
| * http://www.geocities.com/SiliconValley/Network/5317/drivers.html |
| * http://willemer.de/informatik/windows/inf_info.htm (German) |
| * http://www.microsoft.com/ddk/ddkdocs/win98ddk/devinst_12uw.htm |
| * DDK: setupx.h |
| * http://mmatrix.tripod.com/customsystemfolder/infsysntaxfull.html |
| * http://www.rdrop.com/~cary/html/inf_faq.html |
| * |
| * Stuff tested with: |
| * - rs405deu.exe (German Acroread 4.05 setup) |
| * - ie5setup.exe |
| * - Netmeeting |
| * |
| * FIXME: |
| * - check buflen |
| */ |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include "winreg.h" |
| #include "wine/winuser16.h" |
| #include "setupx16.h" |
| #include "winerror.h" |
| #include "debugtools.h" |
| |
| DEFAULT_DEBUG_CHANNEL(setupx); |
| |
| /*********************************************************************** |
| * SURegOpenKey |
| */ |
| DWORD WINAPI SURegOpenKey( HKEY hkey, LPCSTR lpszSubKey, LPHKEY retkey ) |
| { |
| FIXME("(%x,%s,%p), semi-stub.\n",hkey,debugstr_a(lpszSubKey),retkey); |
| return RegOpenKeyA( hkey, lpszSubKey, retkey ); |
| } |
| |
| /*********************************************************************** |
| * SURegQueryValueEx |
| */ |
| DWORD WINAPI SURegQueryValueEx( HKEY hkey, LPSTR lpszValueName, |
| LPDWORD lpdwReserved, LPDWORD lpdwType, |
| LPBYTE lpbData, LPDWORD lpcbData ) |
| { |
| FIXME("(%x,%s,%p,%p,%p,%ld), semi-stub.\n",hkey,debugstr_a(lpszValueName), |
| lpdwReserved,lpdwType,lpbData,lpcbData?*lpcbData:0); |
| return RegQueryValueExA( hkey, lpszValueName, lpdwReserved, lpdwType, |
| lpbData, lpcbData ); |
| } |
| |
| /*********************************************************************** |
| * InstallHinfSection |
| * |
| * hwnd = parent window |
| * hinst = instance of SETUPX.DLL |
| * lpszCmdLine = e.g. "DefaultInstall 132 C:\MYINSTALL\MYDEV.INF" |
| * Here "DefaultInstall" is the .inf file section to be installed (optional). |
| * 132 is the standard parameter, it seems. |
| * 133 means don't prompt user for reboot. |
| * |
| * nCmdShow = nCmdShow of CreateProcess |
| * FIXME: is the return type correct ? |
| */ |
| DWORD WINAPI InstallHinfSection16( HWND16 hwnd, HINSTANCE16 hinst, LPCSTR lpszCmdLine, INT16 nCmdShow) |
| { |
| FIXME("(%04x, %04x, %s, %d), stub.\n", hwnd, hinst, lpszCmdLine, nCmdShow); |
| return 0; |
| } |
| |
| typedef struct |
| { |
| LPCSTR RegValName; |
| LPCSTR StdString; /* fallback string; sub dir of windows directory */ |
| } LDID_DATA; |
| |
| static const LDID_DATA LDID_Data[34] = |
| { |
| { /* 0 (LDID_NULL) -- not defined */ |
| NULL, |
| NULL |
| }, |
| { /* 1 (LDID_SRCPATH) = source of installation. hmm, what to do here ? */ |
| "SourcePath", /* hmm, does SETUPX have to care about updating it ?? */ |
| NULL |
| }, |
| { /* 2 (LDID_SETUPTEMP) = setup temp dir */ |
| "SetupTempDir", |
| NULL |
| }, |
| { /* 3 (LDID_UNINSTALL) = uninstall backup dir */ |
| "UninstallDir", |
| NULL |
| }, |
| { /* 4 (LDID_BACKUP) = backup dir */ |
| "BackupDir", |
| NULL |
| }, |
| { /* 5 (LDID_SETUPSCRATCH) = setup scratch dir */ |
| "SetupScratchDir", |
| NULL |
| }, |
| { /* 6 -- not defined */ |
| NULL, |
| NULL |
| }, |
| { /* 7 -- not defined */ |
| NULL, |
| NULL |
| }, |
| { /* 8 -- not defined */ |
| NULL, |
| NULL |
| }, |
| { /* 9 -- not defined */ |
| NULL, |
| NULL |
| }, |
| { /* 10 (LDID_WIN) = windows dir */ |
| "WinDir", |
| "" |
| }, |
| { /* 11 (LDID_SYS) = system dir */ |
| "SysDir", |
| NULL /* call GetSystemDirectory() instead */ |
| }, |
| { /* 12 (LDID_IOS) = IOSubSys dir */ |
| NULL, /* FIXME: registry string ? */ |
| "SYSTEM\\IOSUBSYS" |
| }, |
| { /* 13 (LDID_CMD) = COMMAND dir */ |
| NULL, /* FIXME: registry string ? */ |
| "COMMAND" |
| }, |
| { /* 14 (LDID_CPL) = control panel dir */ |
| NULL, |
| "" |
| }, |
| { /* 15 (LDID_PRINT) = windows printer dir */ |
| NULL, |
| "SYSTEM" /* correct ?? */ |
| }, |
| { /* 16 (LDID_MAIL) = destination mail dir */ |
| NULL, |
| "" |
| }, |
| { /* 17 (LDID_INF) = INF dir */ |
| "SetupScratchDir", /* correct ? */ |
| "INF" |
| }, |
| { /* 18 (LDID_HELP) = HELP dir */ |
| NULL, /* ??? */ |
| "HELP" |
| }, |
| { /* 19 (LDID_WINADMIN) = Admin dir */ |
| "WinAdminDir", |
| "" |
| }, |
| { /* 20 (LDID_FONTS) = Fonts dir */ |
| NULL, /* ??? */ |
| "FONTS" |
| }, |
| { /* 21 (LDID_VIEWERS) = Viewers */ |
| NULL, /* ??? */ |
| "SYSTEM\\VIEWERS" |
| }, |
| { /* 22 (LDID_VMM32) = VMM32 dir */ |
| NULL, /* ??? */ |
| "SYSTEM\\VMM32" |
| }, |
| { /* 23 (LDID_COLOR) = ICM dir */ |
| "ICMPath", |
| "SYSTEM\\COLOR" |
| }, |
| { /* 24 (LDID_APPS) = root of boot drive ? */ |
| "AppsDir", |
| "C:\\" |
| }, |
| { /* 25 (LDID_SHARED) = shared dir */ |
| "SharedDir", |
| "" |
| }, |
| { /* 26 (LDID_WINBOOT) = Windows boot dir */ |
| "WinBootDir", |
| "" |
| }, |
| { /* 27 (LDID_MACHINE) = machine specific files */ |
| "MachineDir", |
| NULL |
| }, |
| { /* 28 (LDID_HOST_WINBOOT) = Host Windows boot dir */ |
| "HostWinBootDir", |
| NULL |
| }, |
| { /* 29 -- not defined */ |
| NULL, |
| NULL |
| }, |
| { /* 30 (LDID_BOOT) = Root of boot drive */ |
| "BootDir", |
| NULL |
| }, |
| { /* 31 (LDID_BOOT_HOST) = Root of boot drive host */ |
| "BootHost", |
| NULL |
| }, |
| { /* 32 (LDID_OLD_WINBOOT) = subdir of root */ |
| "OldWinBootDir", |
| NULL |
| }, |
| { /* 33 (LDID_OLD_WIN) = old win dir */ |
| "OldWinDir", |
| NULL |
| } |
| /* the rest (34-38) isn't too interesting, so I'll forget about it */ |
| }; |
| |
| static void SETUPX_IsolateSubString(LPSTR *begin, LPSTR *end) |
| { |
| LPSTR p, q; |
| |
| p = *begin; |
| q = *end; |
| |
| while ((p < q) && ((*p == ' ') || (*p == '\t'))) p++; |
| while ((p < q) && (*p == '"')) p++; |
| |
| while ((q-1 >= p) && ((*(q-1) == ' ') || (*(q-1) == '\t'))) q--; |
| while ((q-1 >= p) && (*(q-1) == '"')) q--; |
| |
| *begin = p; |
| *end = q; |
| } |
| |
| /* |
| * Example: HKLM,"Software\Microsoft\Windows\CurrentVersion","ProgramFilesDir",,"C:\" |
| */ |
| static BOOL SETUPX_LookupRegistryString(LPSTR regstr, LPSTR buffer, DWORD buflen) |
| { |
| HANDLE heap = GetProcessHeap(); |
| LPSTR items[5]; |
| LPSTR p, q, next; |
| int len, n; |
| HKEY hkey, hsubkey; |
| DWORD dwType; |
| |
| TRACE("retrieving '%s'\n", regstr); |
| |
| p = regstr; |
| |
| /* isolate root key, subkey, value, flag, defval */ |
| for (n=0; n < 5; n++) |
| { |
| q = strchr(p, ','); |
| if (!q) |
| { |
| if (n == 4) |
| q = p+strlen(p); |
| else |
| return FALSE; |
| } |
| next = q+1; |
| if (q < regstr) |
| return FALSE; |
| SETUPX_IsolateSubString(&p, &q); |
| len = (int)q - (int)p; |
| items[n] = HeapAlloc(heap, 0, len+1); |
| strncpy(items[n], p, len); |
| items[n][len] = '\0'; |
| p = next; |
| } |
| TRACE("got '%s','%s','%s','%s','%s'\n", |
| items[0], items[1], items[2], items[3], items[4]); |
| |
| /* check root key */ |
| if (!strcasecmp(items[0], "HKCR")) |
| hkey = HKEY_CLASSES_ROOT; |
| else |
| if (!strcasecmp(items[0], "HKCU")) |
| hkey = HKEY_CURRENT_USER; |
| else |
| if (!strcasecmp(items[0], "HKLM")) |
| hkey = HKEY_LOCAL_MACHINE; |
| else |
| if (!strcasecmp(items[0], "HKU")) |
| hkey = HKEY_USERS; |
| else |
| { /* HKR ? -> relative to key passed to GenInstallEx */ |
| FIXME("unsupported regkey '%s'\n", items[0]); |
| goto regfailed; |
| } |
| |
| if (RegOpenKeyA(hkey, items[1], &hsubkey) != ERROR_SUCCESS) |
| goto regfailed; |
| |
| if (RegQueryValueExA(hsubkey, items[2], 0, &dwType, buffer, &buflen) |
| != ERROR_SUCCESS) |
| goto regfailed; |
| goto done; |
| |
| regfailed: |
| strcpy(buffer, items[4]); /* I don't care about buflen */ |
| done: |
| for (n=0; n < 5; n++) |
| HeapFree(heap, 0, items[n]); |
| TRACE("return '%s'\n", buffer); |
| return TRUE; |
| } |
| |
| /* |
| * Find the value of a custom LDID in a .inf file |
| * e.g. for 49301: |
| * 49300,49301=ProgramFilesDir,5 |
| * -- profile section lookup --> |
| * [ProgramFilesDir] |
| * HKLM,"Software\Microsoft\Windows\CurrentVersion","ProgramFilesDir",,"%24%" |
| * -- GenFormStrWithoutPlaceHolders16 --> |
| * HKLM,"Software\Microsoft\Windows\CurrentVersion","ProgramFilesDir",,"C:\" |
| * -- registry lookup --> |
| * C:\Program Files (or C:\ if not found in registry) |
| * |
| * FIXME: |
| * - maybe we ought to add a caching array for speed ? - I don't care :) |
| * - not sure whether the processing is correct - sometimes there are equal |
| * LDIDs for both install and removal sections. |
| */ |
| static BOOL SETUPX_TranslateCustomLDID(int ldid, LPSTR buffer, WORD buflen, INT16 hInf) |
| { |
| char ldidstr[6], sectionbuf[0xffff], entrybuf[0xffff], section[256]; |
| LPCSTR filename; |
| LPSTR pSec, pEnt, pEqual, p, pEnd; |
| BOOL ret = FALSE; |
| |
| sprintf(ldidstr, "%d", ldid); |
| filename = IP_GetFileName(hInf); |
| if (!GetPrivateProfileStringA(NULL, NULL, NULL, |
| sectionbuf, sizeof(sectionbuf), filename)) |
| { |
| ERR("section buffer too small ?\n"); |
| return FALSE; |
| } |
| for (pSec=sectionbuf; *pSec; pSec += strlen(pSec)+1) |
| { |
| if (!GetPrivateProfileSectionA(pSec, |
| entrybuf, sizeof(entrybuf), filename)) |
| { |
| ERR("entry buffer too small ?\n"); |
| return FALSE; |
| } |
| for (pEnt=entrybuf; *pEnt; pEnt += strlen(pEnt)+1) |
| { |
| if (strstr(pEnt, ldidstr)) |
| { |
| pEqual = strchr(pEnt, '='); |
| if (!pEqual) /* crippled entry ?? */ |
| continue; |
| |
| /* make sure we found the LDID on left side of the equation */ |
| if (pEnt+strlen(ldidstr) <= pEqual) |
| { /* found */ |
| |
| /* but we don't want entries in the strings section */ |
| if (!strcasecmp(pSec, "Strings")) |
| goto next_section; |
| p = pEqual+1; |
| while ((*p == ' ') || (*p == '\t')) p++; |
| goto found; |
| } |
| } |
| } |
| next_section: |
| } |
| return FALSE; |
| found: |
| TRACE("found entry '%s'\n", p); |
| /* strip off any flags we get |
| * FIXME: what are these flags used for ?? */ |
| pEnd = strchr(p, ','); |
| strncpy(section, p, (int)pEnd - (int)p); |
| section[(int)pEnd - (int)p] = '\0'; |
| |
| /* get the location of the registry key from that section */ |
| if (!GetPrivateProfileSectionA(section, entrybuf, sizeof(entrybuf), filename)) |
| { |
| ERR("entrybuf too small ?\n"); |
| return FALSE; |
| } |
| GenFormStrWithoutPlaceHolders16(sectionbuf, entrybuf, hInf); |
| ret = SETUPX_LookupRegistryString(sectionbuf, buffer, buflen); |
| TRACE("return '%s'\n", buffer); |
| return ret; |
| } |
| |
| /* |
| * Translate a logical disk identifier (LDID) into its string representation |
| */ |
| static BOOL SETUPX_TranslateLDID(int ldid, LPSTR buffer, WORD buflen, HINF16 hInf) |
| { |
| BOOL handled = FALSE; |
| |
| if ((ldid >= LDID_SRCPATH) && (ldid <= LDID_OLD_WIN)) |
| { |
| if (LDID_Data[ldid].RegValName) |
| { |
| HKEY hKey; |
| |
| if (RegOpenKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup", &hKey) == ERROR_SUCCESS) |
| { |
| DWORD type, len = buflen; |
| |
| if ( (RegQueryValueExA(hKey, LDID_Data[ldid].RegValName, |
| NULL, &type, buffer, &len) == ERROR_SUCCESS) |
| && (type == REG_SZ) ) |
| { |
| TRACE("found value '%s' for LDID %d\n", buffer, ldid); |
| handled = TRUE; |
| } |
| |
| RegCloseKey(hKey); |
| } |
| } |
| } |
| if (!handled) |
| { |
| switch(ldid) |
| { |
| case LDID_SRCPATH: |
| FIXME("LDID_SRCPATH: what exactly do we have to do here ?\n"); |
| break; |
| case LDID_SYS: |
| GetSystemDirectoryA(buffer, buflen); |
| handled = TRUE; |
| break; |
| case LDID_APPS: |
| case LDID_MACHINE: |
| case LDID_HOST_WINBOOT: |
| case LDID_BOOT: |
| case LDID_BOOT_HOST: |
| strncpy(buffer, "C:\\", buflen); |
| buffer[buflen-1] = '\0'; |
| handled = TRUE; |
| break; |
| default: |
| if ( (ldid >= LDID_NULL) && (ldid <= LDID_OLD_WIN) |
| && (LDID_Data[ldid].StdString) ) |
| { |
| UINT len = GetWindowsDirectoryA(buffer, buflen); |
| if (len <= buflen-1) |
| { |
| buffer += len; |
| buflen -= len; |
| *buffer++ = '\\'; |
| buflen--; |
| strncpy(buffer, LDID_Data[ldid].StdString, buflen); |
| buffer[buflen-1] = '\0'; |
| } |
| handled = TRUE; |
| } |
| break; |
| } |
| } |
| |
| if (!handled) |
| handled = SETUPX_TranslateCustomLDID(ldid, buffer, buflen, hInf); |
| |
| if (!handled) |
| FIXME("unimplemented LDID %d\n", ldid); |
| |
| return handled; |
| } |
| |
| /*********************************************************************** |
| * GenFormStrWithoutPlaceHolders |
| */ |
| void WINAPI GenFormStrWithoutPlaceHolders16( LPSTR szDst, LPCSTR szSrc, HINF16 hInf) |
| { |
| LPCSTR pSrc = szSrc, pSrcEnd = szSrc + strlen(szSrc); |
| LPSTR pDst = szDst, p, pPHBegin; |
| int count; |
| |
| FIXME("(%p, '%s', %04x), semi stub.\n", szDst, szSrc, hInf); |
| while (pSrc < pSrcEnd) |
| { |
| p = strchr(pSrc, '%'); |
| if (p) |
| { |
| count = (int)p - (int)pSrc; |
| strncpy(pDst, pSrc, count); |
| pSrc += count; |
| pDst += count; |
| pPHBegin = p+1; |
| p = strchr(pPHBegin, '%'); |
| if (p) |
| { |
| char placeholder[80]; /* that really ought to be enough ;) */ |
| int ldid; |
| BOOL done = TRUE; |
| count = (int)p - (int)pPHBegin; |
| strncpy(placeholder, pPHBegin, count); |
| placeholder[count] = '\0'; |
| ldid = atoi(placeholder); |
| if (ldid) |
| { |
| done = SETUPX_TranslateLDID(ldid, pDst, 256, hInf); |
| if (done) |
| pDst += strlen(pDst); |
| } |
| else |
| { /* hmm, string placeholder. Need to look up |
| in the [strings] section of the hInf */ |
| DWORD ret; |
| char buf[256]; /* long enough ? */ |
| |
| ret = GetPrivateProfileStringA("strings", placeholder, "", |
| buf, 256, IP_GetFileName(hInf)); |
| if (ret) |
| { |
| strcpy(pDst, buf); |
| pDst += strlen(buf); |
| } |
| else |
| { |
| ERR("placeholder string '%s' not found !\n", placeholder); |
| done = FALSE; |
| } |
| } |
| if (!done) |
| { /* copy raw placeholder string over */ |
| count = (int)p - (int)pPHBegin + 2; |
| strncpy(pDst, pPHBegin-1, count); |
| pDst += count; |
| |
| } |
| pSrc = p+1; |
| continue; |
| } |
| } |
| |
| /* copy the remaining source string over */ |
| strncpy(pDst, pSrc, (int)pSrcEnd - (int)pSrc + 1); |
| break; |
| } |
| TRACE("ret '%s'\n", szDst); |
| } |
| |
| /*********************************************************************** |
| * CtlGetLddPath |
| */ |
| RETERR16 WINAPI CtlGetLddPath16(LOGDISKID16 ldid, LPSTR szPath) |
| { |
| FIXME("(%04x, %p), stub.\n", ldid, szPath); |
| strcpy(szPath, "FIXME_BogusLddPath"); |
| return OK; |
| } |
| |
| /*********************************************************************** |
| * CtlSetLddPath |
| */ |
| RETERR16 WINAPI CtlSetLddPath16(LOGDISKID16 ldid, LPSTR szPath) |
| { |
| FIXME("(%04x, '%s'), stub.\n", ldid, szPath); |
| return OK; |
| } |
| |
| /*********************************************************************** |
| * vcpOpen |
| * |
| * p2 is "\001" for Netmeeting. |
| */ |
| RETERR16 WINAPI vcpOpen16(LPWORD p1, LPWORD p2) |
| { |
| FIXME("(%p, %p), stub.\n", p1, p2); |
| return OK; |
| } |
| |
| /*********************************************************************** |
| * vcpClose |
| */ |
| RETERR16 WINAPI vcpClose16(WORD w1, WORD w2, WORD w3) |
| { |
| FIXME("(%04x, %04x %04x), stub.\n", w1, w2, w3); |
| return OK; |
| } |
| |
| /*********************************************************************** |
| * GenInstall |
| */ |
| RETERR16 WINAPI GenInstall16(HINF16 hInfFile, LPCSTR szInstallSection, WORD wFlags) |
| { |
| FIXME("(%04x, '%s', %04x), stub. This doesn't install anything yet ! Use native SETUPX.DLL instead !!\n", hInfFile, szInstallSection, wFlags); |
| return OK; |
| } |