2 * Advpack file functions
4 * Copyright 2006 James Hawkins
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(advpack);
35 /***********************************************************************
36 * AddDelBackupEntryA (ADVPACK.@)
38 * Either appends the files in the file list to the backup section of
39 * the specified INI, or deletes the entries from the INI file.
42 * lpcszFileList [I] NULL-separated list of filenames.
43 * lpcszBackupDir [I] Path of the backup directory.
44 * lpcszBaseName [I] Basename of the INI file.
45 * dwFlags [I] AADBE_ADD_ENTRY adds the entries in the file list
46 * to the INI file, while AADBE_DEL_ENTRY removes
47 * the entries from the INI file.
53 * If the INI file does not exist before adding entries to it, the file
56 * If lpcszBackupDir is NULL, the INI file is assumed to exist in
57 * c:\windows or created there if it does not exist.
59 HRESULT WINAPI AddDelBackupEntryA(LPCSTR lpcszFileList, LPCSTR lpcszBackupDir,
60 LPCSTR lpcszBaseName, DWORD dwFlags)
62 CHAR szIniPath[MAX_PATH];
63 LPSTR szString = NULL;
65 const char szBackupEntry[] = "-1,0,0,0,0,0,-1";
67 TRACE("(%p, %p, %p, %ld)\n", lpcszFileList, lpcszBackupDir,
68 lpcszBaseName, dwFlags);
70 if (!lpcszFileList || !*lpcszFileList)
74 lstrcpyA(szIniPath, lpcszBackupDir);
76 GetWindowsDirectoryA(szIniPath, MAX_PATH);
78 lstrcatA(szIniPath, "\\");
79 lstrcatA(szIniPath, lpcszBaseName);
80 lstrcatA(szIniPath, ".ini");
82 SetFileAttributesA(szIniPath, FILE_ATTRIBUTE_NORMAL);
84 if (dwFlags & AADBE_ADD_ENTRY)
85 szString = (LPSTR)szBackupEntry;
86 else if (dwFlags & AADBE_DEL_ENTRY)
89 /* add or delete the INI entries */
90 while (*lpcszFileList)
92 WritePrivateProfileStringA("backup", lpcszFileList, szString, szIniPath);
93 lpcszFileList += lstrlenA(lpcszFileList) + 1;
96 /* hide the INI file */
97 SetFileAttributesA(szIniPath, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN);
102 /* FIXME: this is only for the local case, X:\ */
103 #define ROOT_LENGTH 3
105 UINT CALLBACK pQuietQueueCallback(PVOID Context, UINT Notification,
106 UINT_PTR Param1, UINT_PTR Param2)
111 UINT CALLBACK pQueueCallback(PVOID Context, UINT Notification,
112 UINT_PTR Param1, UINT_PTR Param2)
114 /* only be verbose for error notifications */
116 Notification == SPFILENOTIFY_RENAMEERROR ||
117 Notification == SPFILENOTIFY_DELETEERROR ||
118 Notification == SPFILENOTIFY_COPYERROR)
120 return SetupDefaultQueueCallbackA(Context, Notification,
127 /***********************************************************************
128 * AdvInstallFileA (ADVPACK.@)
130 * Copies a file from the source to a destination.
133 * hwnd [I] Handle to the window used for messages.
134 * lpszSourceDir [I] Source directory.
135 * lpszSourceFile [I] Source filename.
136 * lpszDestDir [I] Destination directory.
137 * lpszDestFile [I] Optional destination filename.
138 * dwFlags [I] See advpub.h.
139 * dwReserved [I] Reserved. Must be 0.
146 * If lpszDestFile is NULL, the destination filename is the same as
149 HRESULT WINAPI AdvInstallFileA(HWND hwnd, LPCSTR lpszSourceDir, LPCSTR lpszSourceFile,
150 LPCSTR lpszDestDir, LPCSTR lpszDestFile,
151 DWORD dwFlags, DWORD dwReserved)
153 PSP_FILE_CALLBACK_A pFileCallback;
154 LPSTR szPath, szDestFilename;
155 char szRootPath[ROOT_LENGTH];
156 DWORD dwLen, dwLastError;
160 TRACE("(%p,%p,%p,%p,%p,%ld,%ld)\n", hwnd, debugstr_a(lpszSourceDir),
161 debugstr_a(lpszSourceFile), debugstr_a(lpszDestDir),
162 debugstr_a(lpszDestFile), dwFlags, dwReserved);
164 if (!lpszSourceDir || !lpszSourceFile || !lpszDestDir)
167 fileQueue = SetupOpenFileQueue();
168 if (fileQueue == INVALID_HANDLE_VALUE)
169 return HRESULT_FROM_WIN32(GetLastError());
172 dwLastError = ERROR_SUCCESS;
174 lstrcpynA(szRootPath, lpszSourceDir, ROOT_LENGTH);
175 szPath = (LPSTR)lpszSourceDir + ROOT_LENGTH;
177 /* use lpszSourceFile as destination filename if lpszDestFile is NULL */
180 dwLen = lstrlenA(lpszDestFile);
181 szDestFilename = HeapAlloc(GetProcessHeap(), 0, dwLen);
182 lstrcpyA(szDestFilename, lpszDestFile);
186 dwLen = lstrlenA(lpszSourceFile);
187 szDestFilename = HeapAlloc(GetProcessHeap(), 0, dwLen);
188 lstrcpyA(szDestFilename, lpszSourceFile);
191 /* add the file copy operation to the setup queue */
192 if (!SetupQueueCopyA(fileQueue, szRootPath, szPath, lpszSourceFile, NULL,
193 NULL, lpszDestDir, szDestFilename, dwFlags))
195 dwLastError = GetLastError();
199 pContext = SetupInitDefaultQueueCallbackEx(hwnd, INVALID_HANDLE_VALUE,
203 dwLastError = GetLastError();
207 /* don't output anything for AIF_QUIET */
208 if (dwFlags & AIF_QUIET)
209 pFileCallback = pQuietQueueCallback;
211 pFileCallback = pQueueCallback;
213 /* perform the file copy */
214 if (!SetupCommitFileQueueA(hwnd, fileQueue, pFileCallback, pContext))
216 dwLastError = GetLastError();
221 SetupTermDefaultQueueCallback(pContext);
222 SetupCloseFileQueue(fileQueue);
224 HeapFree(GetProcessHeap(), 0, szDestFilename);
226 return HRESULT_FROM_WIN32(dwLastError);
229 static HRESULT DELNODE_recurse_dirtree(LPSTR fname, DWORD flags)
231 DWORD fattrs = GetFileAttributesA(fname);
232 HRESULT ret = E_FAIL;
234 if (fattrs & FILE_ATTRIBUTE_DIRECTORY)
237 WIN32_FIND_DATAA w32fd;
239 int fname_len = lstrlenA(fname);
241 /* Generate a path with wildcard suitable for iterating */
242 if (CharPrevA(fname, fname + fname_len) != "\\")
244 lstrcpyA(fname + fname_len, "\\");
247 lstrcpyA(fname + fname_len, "*");
249 if ((hFindFile = FindFirstFileA(fname, &w32fd)) != INVALID_HANDLE_VALUE)
251 /* Iterate through the files in the directory */
252 for (done = FALSE; !done; done = !FindNextFileA(hFindFile, &w32fd))
254 TRACE("%s\n", w32fd.cFileName);
255 if (lstrcmpA(".", w32fd.cFileName) != 0 &&
256 lstrcmpA("..", w32fd.cFileName) != 0)
258 lstrcpyA(fname + fname_len, w32fd.cFileName);
259 if (DELNODE_recurse_dirtree(fname, flags) != S_OK)
265 FindClose(hFindFile);
268 /* We're done with this directory, so restore the old path without wildcard */
269 *(fname + fname_len) = '\0';
273 TRACE("%s: directory\n", fname);
274 if (SetFileAttributesA(fname, FILE_ATTRIBUTE_NORMAL) && RemoveDirectoryA(fname))
282 TRACE("%s: file\n", fname);
283 if (SetFileAttributesA(fname, FILE_ATTRIBUTE_NORMAL) && DeleteFileA(fname))
292 /***********************************************************************
293 * DelNodeA (ADVPACK.@)
295 * Deletes a file or directory
298 * pszFileOrDirName [I] Name of file or directory to delete
299 * dwFlags [I] Flags; see include/advpub.h
307 * - Native version apparently does a lot of checking to make sure
308 * we're not trying to delete a system directory etc.
310 HRESULT WINAPI DelNodeA( LPCSTR pszFileOrDirName, DWORD dwFlags )
312 CHAR fname[MAX_PATH];
313 HRESULT ret = E_FAIL;
315 TRACE("(%s, 0x%08lx)\n", debugstr_a(pszFileOrDirName), dwFlags);
318 FIXME("Flags ignored!\n");
320 if (pszFileOrDirName && *pszFileOrDirName)
322 lstrcpyA(fname, pszFileOrDirName);
324 /* TODO: Should check for system directory deletion etc. here */
326 ret = DELNODE_recurse_dirtree(fname, dwFlags);
332 /* returns the parameter at dwIndex in a list of parameters
333 * separated by the cSeparator character
335 static LPSTR get_parameter(LPSTR szParameters, CHAR cSeparator, DWORD dwIndex)
337 LPSTR szParam = NULL;
340 while (*szParameters && i < dwIndex)
342 if (*szParameters == cSeparator)
351 szParam = HeapAlloc(GetProcessHeap(), 0, lstrlenA(szParameters));
352 lstrcpyA(szParam, szParameters);
357 /***********************************************************************
358 * DelNodeRunDLL32A (ADVPACK.@)
360 * Deletes a file or directory, WinMain style.
363 * hWnd [I] Handle to the window used for the display.
364 * hInst [I] Instance of the process.
365 * cmdline [I] Contains parameters in the order FileOrDirName,Flags.
366 * show [I] How the window should be shown.
372 HRESULT WINAPI DelNodeRunDLL32A( HWND hWnd, HINSTANCE hInst, LPSTR cmdline, INT show )
374 LPSTR szFilename, szFlags;
378 TRACE("(%s)\n", debugstr_a(cmdline));
380 /* get the parameters at indexes 0 and 1 respectively */
381 szFilename = get_parameter(cmdline, ',', 0);
382 szFlags = get_parameter(cmdline, ',', 1);
384 dwFlags = atol(szFlags);
386 res = DelNodeA(szFilename, dwFlags);
388 HeapFree(GetProcessHeap(), 0, szFilename);
389 HeapFree(GetProcessHeap(), 0, szFlags);
394 /* The following defintions were copied from dlls/cabinet/cabinet.h */
396 /* EXTRACTdest flags */
397 #define EXTRACT_FILLFILELIST 0x00000001
398 #define EXTRACT_EXTRACTFILES 0x00000002
400 struct ExtractFileList {
402 struct ExtractFileList *next;
403 BOOL unknown; /* always 1L */
406 /* the first parameter of the function Extract */
408 long result1; /* 0x000 */
409 long unknown1[3]; /* 0x004 */
410 struct ExtractFileList *filelist; /* 0x010 */
411 long filecount; /* 0x014 */
412 DWORD flags; /* 0x018 */
413 char directory[0x104]; /* 0x01c */
414 char lastfile[0x20c]; /* 0x120 */
417 static HRESULT (WINAPI *pExtract)(EXTRACTdest*, LPCSTR);
419 /* removes legal characters before and after file list, and
420 * converts the file list to a NULL-separated list
422 static LPSTR convert_file_list(LPCSTR FileList, DWORD *dwNumFiles)
425 char *first = (char *)FileList;
426 char *last = (char *)FileList + strlen(FileList) - 1;
427 LPSTR szConvertedList, temp;
429 /* any number of these chars before the list is OK */
430 while (first < last && (*first == ' ' || *first == '\t' || *first == ':'))
433 /* any number of these chars after the list is OK */
434 while (last > first && (*last == ' ' || *last == '\t' || *last == ':'))
440 dwLen = last - first + 3; /* room for double-null termination */
441 szConvertedList = HeapAlloc(GetProcessHeap(), 0, dwLen);
442 lstrcpynA(szConvertedList, first, dwLen - 1);
444 szConvertedList[dwLen - 1] = '\0';
445 szConvertedList[dwLen] = '\0';
448 if (!lstrlenA(szConvertedList))
453 /* convert the colons to double-null termination */
454 temp = szConvertedList;
466 return szConvertedList;
469 static void free_file_node(struct ExtractFileList *pNode)
471 HeapFree(GetProcessHeap(), 0, pNode->filename);
472 HeapFree(GetProcessHeap(), 0, pNode);
475 /* determines whether szFile is in the NULL-separated szFileList */
476 static BOOL file_in_list(LPSTR szFile, LPSTR szFileList)
478 DWORD dwLen = lstrlenA(szFile);
483 dwTestLen = lstrlenA(szFileList);
485 if (dwTestLen == dwLen)
487 if (!lstrcmpiA(szFile, szFileList))
491 szFileList += dwTestLen + 1;
497 /* removes nodes from the linked list that aren't specified in szFileList
498 * returns the number of files that are in both the linked list and szFileList
500 static DWORD fill_file_list(EXTRACTdest *extractDest, LPCSTR szCabName, LPSTR szFileList)
502 DWORD dwNumFound = 0;
503 struct ExtractFileList *pNode;
504 struct ExtractFileList *prev = NULL;
506 extractDest->flags |= EXTRACT_FILLFILELIST;
507 if (pExtract(extractDest, szCabName))
509 extractDest->flags &= ~EXTRACT_FILLFILELIST;
513 pNode = extractDest->filelist;
516 if (file_in_list(pNode->filename, szFileList))
524 prev->next = pNode->next;
525 free_file_node(pNode);
530 extractDest->filelist = pNode->next;
531 free_file_node(pNode);
532 pNode = extractDest->filelist;
536 extractDest->flags &= ~EXTRACT_FILLFILELIST;
540 /***********************************************************************
541 * ExtractFilesA (ADVPACK.@)
543 * Extracts the specified files from a cab archive into
544 * a destination directory.
547 * CabName [I] Filename of the cab archive.
548 * ExpandDir [I] Destination directory for the extracted files.
549 * Flags [I] Reserved.
550 * FileList [I] Optional list of files to extract. See NOTES.
551 * LReserved [I] Reserved. Must be NULL.
552 * Reserved [I] Reserved. Must be 0.
559 * FileList is a colon-separated list of filenames. If FileList is
560 * non-NULL, only the files in the list will be extracted from the
561 * cab file, otherwise all files will be extracted. Any number of
562 * spaces, tabs, or colons can be before or after the list, but
563 * the list itself must only be separated by colons.
565 HRESULT WINAPI ExtractFilesA( LPCSTR CabName, LPCSTR ExpandDir, DWORD Flags,
566 LPCSTR FileList, LPVOID LReserved, DWORD Reserved)
568 EXTRACTdest extractDest;
571 DWORD dwFileCount = 0;
572 DWORD dwFilesFound = 0;
573 LPSTR szConvertedList = NULL;
575 TRACE("(%p %p %ld %p %p %ld)\n", CabName, ExpandDir, Flags,
576 FileList, LReserved, Reserved);
578 if (!CabName || !ExpandDir)
581 if (GetFileAttributesA(ExpandDir) == INVALID_FILE_ATTRIBUTES)
582 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
584 hCabinet = LoadLibraryA("cabinet.dll");
588 pExtract = (void *)GetProcAddress(hCabinet, "Extract");
595 ZeroMemory(&extractDest, sizeof(EXTRACTdest));
596 lstrcpyA(extractDest.directory, ExpandDir);
600 szConvertedList = convert_file_list(FileList, &dwFileCount);
601 if (!szConvertedList || dwFileCount == -1)
607 dwFilesFound = fill_file_list(&extractDest, CabName, szConvertedList);
608 if (dwFilesFound != dwFileCount)
615 extractDest.flags |= EXTRACT_FILLFILELIST;
617 extractDest.flags |= EXTRACT_EXTRACTFILES;
618 res = pExtract(&extractDest, CabName);
621 FreeLibrary(hCabinet);
622 HeapFree(GetProcessHeap(), 0, szConvertedList);
627 /***********************************************************************
628 * FileSaveMarkNotExistA (ADVPACK.@)
630 * Marks the files in the file list as not existing so they won't be
631 * backed up during a save.
634 * pszFileList [I] NULL-separated list of filenames.
635 * pszDir [I] Path of the backup directory.
636 * pszBaseName [I] Basename of the INI file.
642 HRESULT WINAPI FileSaveMarkNotExistA(LPSTR pszFileList, LPSTR pszDir, LPSTR pszBaseName)
644 TRACE("(%p, %p, %p)\n", pszFileList, pszDir, pszBaseName);
646 return AddDelBackupEntryA(pszFileList, pszDir, pszBaseName, AADBE_DEL_ENTRY);
649 /***********************************************************************
650 * FileSaveRestoreA (ADVPACK.@)
652 * Saves or restores the files in the specified file list.
655 * hDlg [I] Handle to the dialog used for the display.
656 * pszFileList [I] NULL-separated list of filenames.
657 * pszDir [I] Path of the backup directory.
658 * pszBaseName [I] Basename of the backup files.
659 * dwFlags [I] See advpub.h.
666 * If pszFileList is NULL on restore, all files will be restored.
671 HRESULT WINAPI FileSaveRestoreA(HWND hDlg, LPSTR pszFileList, LPSTR pszDir,
672 LPSTR pszBaseName, DWORD dwFlags)
674 FIXME("(%p, %p, %p, %p, %ld) stub\n", hDlg, pszFileList, pszDir,
675 pszBaseName, dwFlags);
680 /***********************************************************************
681 * FileSaveRestoreOnINFA (ADVPACK.@)
685 * hWnd [I] Handle to the window used for the display.
686 * pszTitle [I] Title of the window.
687 * pszINF [I] Fully-qualified INF filename.
688 * pszSection [I] GenInstall INF section name.
689 * pszBackupDir [I] Directory to store the backup file.
690 * pszBaseBackupFile [I] Basename of the backup files.
691 * dwFlags [I] See advpub.h
698 * If pszSection is NULL, the default section will be used.
703 HRESULT WINAPI FileSaveRestoreOnINFA(HWND hWnd, LPCSTR pszTitle, LPCSTR pszINF,
704 LPCSTR pszSection, LPCSTR pszBackupDir,
705 LPCSTR pszBaseBackupFile, DWORD dwFlags)
707 FIXME("(%p, %p, %p, %p, %p, %p, %ld) stub\n", hWnd, pszTitle, pszINF,
708 pszSection, pszBackupDir, pszBaseBackupFile, dwFlags);
713 /***********************************************************************
714 * GetVersionFromFileA (ADVPACK.@)
716 * See GetVersionFromFileEx.
718 HRESULT WINAPI GetVersionFromFileA(LPCSTR Filename, LPDWORD MajorVer,
719 LPDWORD MinorVer, BOOL Version )
721 TRACE("(%s, %p, %p, %d)\n", Filename, MajorVer, MinorVer, Version);
722 return GetVersionFromFileExA(Filename, MajorVer, MinorVer, Version);
725 /* data for GetVersionFromFileEx */
726 typedef struct tagLANGANDCODEPAGE
732 /***********************************************************************
733 * GetVersionFromFileExA (ADVPACK.@)
735 * Gets the files version or language information.
738 * lpszFilename [I] The file to get the info from.
739 * pdwMSVer [O] Major version.
740 * pdwLSVer [O] Minor version.
741 * bVersion [I] Whether to retrieve version or language info.
744 * Always returns S_OK.
747 * If bVersion is TRUE, version information is retrieved, else
748 * pdwMSVer gets the language ID and pdwLSVer gets the codepage ID.
750 HRESULT WINAPI GetVersionFromFileExA(LPCSTR lpszFilename, LPDWORD pdwMSVer,
751 LPDWORD pdwLSVer, BOOL bVersion )
753 VS_FIXEDFILEINFO *pFixedVersionInfo;
754 LANGANDCODEPAGE *pLangAndCodePage;
755 DWORD dwHandle, dwInfoSize;
756 CHAR szWinDir[MAX_PATH];
757 CHAR szFile[MAX_PATH];
758 LPVOID pVersionInfo = NULL;
759 BOOL bFileCopied = FALSE;
762 TRACE("(%s, %p, %p, %d)\n", lpszFilename, pdwMSVer, pdwLSVer, bVersion);
767 lstrcpynA(szFile, lpszFilename, MAX_PATH);
769 dwInfoSize = GetFileVersionInfoSizeA(szFile, &dwHandle);
772 /* check that the file exists */
773 if (GetFileAttributesA(szFile) == INVALID_FILE_ATTRIBUTES)
776 /* file exists, but won't be found by GetFileVersionInfoSize,
777 * so copy it to the temp dir where it will be found.
779 GetWindowsDirectoryA(szWinDir, MAX_PATH);
780 GetTempFileNameA(szWinDir, NULL, 0, szFile);
781 CopyFileA(lpszFilename, szFile, FALSE);
784 dwInfoSize = GetFileVersionInfoSizeA(szFile, &dwHandle);
789 pVersionInfo = HeapAlloc(GetProcessHeap(), 0, dwInfoSize);
793 if (!GetFileVersionInfoA(szFile, dwHandle, dwInfoSize, pVersionInfo))
798 if (!VerQueryValueA(pVersionInfo, "\\",
799 (LPVOID *)&pFixedVersionInfo, &uValueLen))
805 *pdwMSVer = pFixedVersionInfo->dwFileVersionMS;
806 *pdwLSVer = pFixedVersionInfo->dwFileVersionLS;
810 if (!VerQueryValueA(pVersionInfo, "\\VarFileInfo\\Translation",
811 (LPVOID *)&pLangAndCodePage, &uValueLen))
817 *pdwMSVer = pLangAndCodePage->wLanguage;
818 *pdwLSVer = pLangAndCodePage->wCodePage;
822 HeapFree(GetProcessHeap(), 0, pVersionInfo);