push 8edcbf8579c1d48dd3fb4c679acf4b3012a9efac
[wine/hacks.git] / dlls / version / install.c
blobd3170e33939e83b898a2af4b4759220fa99e5dae
1 /*
2 * Implementation of VERSION.DLL - File Installer routines
4 * Copyright 1996,1997 Marcus Meissner
5 * Copyright 1997 David Cuthbert
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * TODO
22 * o Check the installation functions.
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <string.h>
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winver.h"
33 #include "winnls.h"
34 #include "wine/unicode.h"
35 #include "winerror.h"
36 #include "lzexpand.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(ver);
42 /******************************************************************************
43 * testFileExistenceA
45 * Tests whether a given path/file combination exists. If the file does
46 * not exist, the return value is zero. If it does exist, the return
47 * value is non-zero.
49 * Revision history
50 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
51 * Original implementation
54 static int testFileExistenceA( char const * path, char const * file, BOOL excl )
56 char filename[1024];
57 int filenamelen;
58 OFSTRUCT fileinfo;
60 fileinfo.cBytes = sizeof(OFSTRUCT);
62 strcpy(filename, path);
63 filenamelen = strlen(filename);
65 /* Add a trailing \ if necessary */
66 if(filenamelen) {
67 if(filename[filenamelen - 1] != '\\')
68 strcat(filename, "\\");
70 else /* specify the current directory */
71 strcpy(filename, ".\\");
73 /* Create the full pathname */
74 strcat(filename, file);
76 return (OpenFile(filename, &fileinfo,
77 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
80 /******************************************************************************
81 * testFileExistenceW
83 static int testFileExistenceW( const WCHAR *path, const WCHAR *file, BOOL excl )
85 char *filename;
86 DWORD pathlen, filelen;
87 int ret;
88 OFSTRUCT fileinfo;
90 fileinfo.cBytes = sizeof(OFSTRUCT);
92 pathlen = WideCharToMultiByte( CP_ACP, 0, path, -1, NULL, 0, NULL, NULL );
93 filelen = WideCharToMultiByte( CP_ACP, 0, file, -1, NULL, 0, NULL, NULL );
94 filename = HeapAlloc( GetProcessHeap(), 0, pathlen+filelen+2 );
96 WideCharToMultiByte( CP_ACP, 0, path, -1, filename, pathlen, NULL, NULL );
97 /* Add a trailing \ if necessary */
98 if (pathlen > 1)
100 if (filename[pathlen-2] != '\\') strcpy( &filename[pathlen-1], "\\" );
102 else /* specify the current directory */
103 strcpy(filename, ".\\");
105 WideCharToMultiByte( CP_ACP, 0, file, -1, filename+strlen(filename), filelen, NULL, NULL );
107 ret = (OpenFile(filename, &fileinfo,
108 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
109 HeapFree( GetProcessHeap(), 0, filename );
110 return ret;
113 /*****************************************************************************
114 * VerFindFileA [VERSION.@]
116 * Determines where to install a file based on whether it locates another
117 * version of the file in the system. The values VerFindFile returns are
118 * used in a subsequent call to the VerInstallFile function.
120 * Revision history:
121 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
122 * Reimplementation of VerFindFile from original stub.
124 DWORD WINAPI VerFindFileA(
125 DWORD flags,
126 LPCSTR lpszFilename,
127 LPCSTR lpszWinDir,
128 LPCSTR lpszAppDir,
129 LPSTR lpszCurDir,
130 PUINT lpuCurDirLen,
131 LPSTR lpszDestDir,
132 PUINT lpuDestDirLen )
134 DWORD retval = 0;
135 const char *curDir;
136 const char *destDir;
137 unsigned int curDirSizeReq;
138 unsigned int destDirSizeReq;
139 char systemDir[MAX_PATH];
141 /* Print out debugging information */
142 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
143 flags, debugstr_a(lpszFilename), debugstr_a(lpszWinDir), debugstr_a(lpszAppDir),
144 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
145 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
147 /* Figure out where the file should go; shared files default to the
148 system directory */
150 GetSystemDirectoryA(systemDir, sizeof(systemDir));
151 curDir = "";
152 destDir = "";
154 if(flags & VFFF_ISSHAREDFILE)
156 destDir = systemDir;
157 /* Were we given a filename? If so, try to find the file. */
158 if(lpszFilename)
160 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
161 else if(lpszAppDir && testFileExistenceA(lpszAppDir, lpszFilename, FALSE))
163 curDir = lpszAppDir;
164 retval |= VFF_CURNEDEST;
168 else /* not a shared file */
170 if(lpszAppDir)
172 destDir = lpszAppDir;
173 if(lpszFilename)
175 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
176 else if(testFileExistenceA(systemDir, lpszFilename, FALSE))
178 curDir = systemDir;
179 retval |= VFF_CURNEDEST;
185 /* Check to see if the file exists and is inuse by another application */
186 if (lpszFilename && testFileExistenceA(curDir, lpszFilename, FALSE)) {
187 if (lpszFilename && !testFileExistenceA(curDir, lpszFilename, TRUE))
188 retval |= VFF_FILEINUSE;
191 curDirSizeReq = strlen(curDir) + 1;
192 destDirSizeReq = strlen(destDir) + 1;
194 /* Make sure that the pointers to the size of the buffers are
195 valid; if not, do NOTHING with that buffer. If that pointer
196 is valid, then make sure that the buffer pointer is valid, too! */
198 if(lpuDestDirLen && lpszDestDir)
200 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
201 lstrcpynA(lpszDestDir, destDir, *lpuDestDirLen);
202 *lpuDestDirLen = destDirSizeReq;
204 if(lpuCurDirLen && lpszCurDir)
206 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
207 lstrcpynA(lpszCurDir, curDir, *lpuCurDirLen);
208 *lpuCurDirLen = curDirSizeReq;
211 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
212 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
213 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
214 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
215 debugstr_a(lpszCurDir), debugstr_a(lpszDestDir));
217 return retval;
220 /*****************************************************************************
221 * VerFindFileW [VERSION.@]
223 DWORD WINAPI VerFindFileW( DWORD flags,LPCWSTR lpszFilename,LPCWSTR lpszWinDir,
224 LPCWSTR lpszAppDir, LPWSTR lpszCurDir,PUINT lpuCurDirLen,
225 LPWSTR lpszDestDir,PUINT lpuDestDirLen )
227 static const WCHAR emptyW;
228 DWORD retval = 0;
229 const WCHAR *curDir;
230 const WCHAR *destDir;
231 unsigned int curDirSizeReq;
232 unsigned int destDirSizeReq;
233 WCHAR systemDir[MAX_PATH];
235 /* Print out debugging information */
236 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
237 flags, debugstr_w(lpszFilename), debugstr_w(lpszWinDir), debugstr_w(lpszAppDir),
238 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
239 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
241 /* Figure out where the file should go; shared files default to the
242 system directory */
244 GetSystemDirectoryW(systemDir, sizeof(systemDir)/sizeof(WCHAR));
245 curDir = &emptyW;
246 destDir = &emptyW;
248 if(flags & VFFF_ISSHAREDFILE)
250 destDir = systemDir;
251 /* Were we given a filename? If so, try to find the file. */
252 if(lpszFilename)
254 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
255 else if(lpszAppDir && testFileExistenceW(lpszAppDir, lpszFilename, FALSE))
257 curDir = lpszAppDir;
258 retval |= VFF_CURNEDEST;
262 else /* not a shared file */
264 if(lpszAppDir)
266 destDir = lpszAppDir;
267 if(lpszFilename)
269 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
270 else if(testFileExistenceW(systemDir, lpszFilename, FALSE))
272 curDir = systemDir;
273 retval |= VFF_CURNEDEST;
279 if (lpszFilename && !testFileExistenceW(curDir, lpszFilename, TRUE))
280 retval |= VFF_FILEINUSE;
282 curDirSizeReq = strlenW(curDir) + 1;
283 destDirSizeReq = strlenW(destDir) + 1;
285 /* Make sure that the pointers to the size of the buffers are
286 valid; if not, do NOTHING with that buffer. If that pointer
287 is valid, then make sure that the buffer pointer is valid, too! */
289 if(lpuDestDirLen && lpszDestDir)
291 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
292 lstrcpynW(lpszDestDir, destDir, *lpuDestDirLen);
293 *lpuDestDirLen = destDirSizeReq;
295 if(lpuCurDirLen && lpszCurDir)
297 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
298 lstrcpynW(lpszCurDir, curDir, *lpuCurDirLen);
299 *lpuCurDirLen = curDirSizeReq;
302 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
303 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
304 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
305 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
306 debugstr_w(lpszCurDir), debugstr_w(lpszDestDir));
307 return retval;
310 static LPBYTE
311 _fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO **vffi) {
312 DWORD alloclen;
313 LPBYTE buf;
314 DWORD ret;
316 alloclen = 1000;
317 buf=HeapAlloc(GetProcessHeap(), 0, alloclen);
318 if(buf == NULL) {
319 WARN("Memory exausted while fetching version info!\n");
320 return NULL;
322 while (1) {
323 ret = GetFileVersionInfoA(fn,0,alloclen,buf);
324 if (!ret) {
325 HeapFree(GetProcessHeap(), 0, buf);
326 return NULL;
328 if (alloclen<*(WORD*)buf) {
329 alloclen = *(WORD*)buf;
330 HeapFree(GetProcessHeap(), 0, buf);
331 buf = HeapAlloc(GetProcessHeap(), 0, alloclen);
332 if(buf == NULL) {
333 WARN("Memory exausted while fetching version info!\n");
334 return NULL;
336 } else {
337 *vffi = (VS_FIXEDFILEINFO*)(buf+0x14);
338 if ((*vffi)->dwSignature == 0x004f0049) /* hack to detect unicode */
339 *vffi = (VS_FIXEDFILEINFO*)(buf+0x28);
340 if ((*vffi)->dwSignature != VS_FFI_SIGNATURE)
341 WARN("Bad VS_FIXEDFILEINFO signature 0x%08x\n",(*vffi)->dwSignature);
342 return buf;
347 static DWORD
348 _error2vif(DWORD error) {
349 switch (error) {
350 case ERROR_ACCESS_DENIED:
351 return VIF_ACCESSVIOLATION;
352 case ERROR_SHARING_VIOLATION:
353 return VIF_SHARINGVIOLATION;
354 default:
355 return 0;
360 /******************************************************************************
361 * VerInstallFileA [VERSION.@]
363 DWORD WINAPI VerInstallFileA(
364 DWORD flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir,
365 LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,PUINT tmpfilelen )
367 LPCSTR pdest;
368 char destfn[260],tmpfn[260],srcfn[260];
369 HFILE hfsrc,hfdst;
370 DWORD attr,xret,tmplast;
371 LONG ret;
372 LPBYTE buf1,buf2;
373 OFSTRUCT ofs;
375 TRACE("(%x,%s,%s,%s,%s,%s,%p,%d)\n",
376 flags,srcfilename,destfilename,srcdir,destdir,curdir,tmpfile,*tmpfilelen
378 xret = 0;
379 sprintf(srcfn,"%s\\%s",srcdir,srcfilename);
380 if (!destdir || !*destdir) pdest = srcdir;
381 else pdest = destdir;
382 sprintf(destfn,"%s\\%s",pdest,destfilename);
383 hfsrc=LZOpenFileA(srcfn,&ofs,OF_READ);
384 if (hfsrc < 0)
385 return VIF_CANNOTREADSRC;
386 sprintf(tmpfn,"%s\\%s",pdest,destfilename);
387 tmplast=strlen(pdest)+1;
388 attr = GetFileAttributesA(tmpfn);
389 if (attr != INVALID_FILE_ATTRIBUTES) {
390 if (attr & FILE_ATTRIBUTE_READONLY) {
391 LZClose(hfsrc);
392 return VIF_WRITEPROT;
394 /* FIXME: check if file currently in use and return VIF_FILEINUSE */
396 attr = INVALID_FILE_ATTRIBUTES;
397 if (flags & VIFF_FORCEINSTALL) {
398 if (tmpfile[0]) {
399 sprintf(tmpfn,"%s\\%s",pdest,tmpfile);
400 tmplast = strlen(pdest)+1;
401 attr = GetFileAttributesA(tmpfn);
402 /* if it exists, it has been copied by the call before.
403 * we jump over the copy part...
407 if (attr == INVALID_FILE_ATTRIBUTES) {
408 char *s;
410 GetTempFileNameA(pdest,"ver",0,tmpfn); /* should not fail ... */
411 s=strrchr(tmpfn,'\\');
412 if (s)
413 tmplast = s-tmpfn;
414 else
415 tmplast = 0;
416 hfdst = OpenFile(tmpfn,&ofs,OF_CREATE);
417 if (hfdst == HFILE_ERROR) {
418 LZClose(hfsrc);
419 return VIF_CANNOTCREATE; /* | translated dos error */
421 ret = LZCopy(hfsrc,hfdst);
422 _lclose(hfdst);
423 if (ret < 0) {
424 /* translate LZ errors into VIF_xxx */
425 switch (ret) {
426 case LZERROR_BADINHANDLE:
427 case LZERROR_READ:
428 case LZERROR_BADVALUE:
429 case LZERROR_UNKNOWNALG:
430 xret = VIF_CANNOTREADSRC;
431 break;
432 case LZERROR_BADOUTHANDLE:
433 case LZERROR_WRITE:
434 xret = VIF_OUTOFSPACE;
435 break;
436 case LZERROR_GLOBALLOC:
437 case LZERROR_GLOBLOCK:
438 xret = VIF_OUTOFMEMORY;
439 break;
440 default: /* unknown error, should not happen */
441 FIXME("Unknown LZCopy error %d, ignoring.\n", ret);
442 xret = 0;
443 break;
445 if (xret) {
446 LZClose(hfsrc);
447 return xret;
451 xret = 0;
452 if (!(flags & VIFF_FORCEINSTALL)) {
453 VS_FIXEDFILEINFO *destvffi,*tmpvffi;
454 buf1 = _fetch_versioninfo(destfn,&destvffi);
455 if (buf1) {
456 buf2 = _fetch_versioninfo(tmpfn,&tmpvffi);
457 if (buf2) {
458 char *tbuf1,*tbuf2;
459 static const CHAR trans_array[] = "\\VarFileInfo\\Translation";
460 UINT len1,len2;
462 len1=len2=40;
464 /* compare file versions */
465 if ((destvffi->dwFileVersionMS > tmpvffi->dwFileVersionMS)||
466 ((destvffi->dwFileVersionMS==tmpvffi->dwFileVersionMS)&&
467 (destvffi->dwFileVersionLS > tmpvffi->dwFileVersionLS)
470 xret |= VIF_MISMATCH|VIF_SRCOLD;
471 /* compare filetypes and filesubtypes */
472 if ((destvffi->dwFileType!=tmpvffi->dwFileType) ||
473 (destvffi->dwFileSubtype!=tmpvffi->dwFileSubtype)
475 xret |= VIF_MISMATCH|VIF_DIFFTYPE;
476 if (VerQueryValueA(buf1,trans_array,(LPVOID*)&tbuf1,&len1) &&
477 VerQueryValueA(buf2,trans_array,(LPVOID*)&tbuf2,&len2)
479 /* Do something with tbuf1 and tbuf2
480 * generates DIFFLANG|MISMATCH
483 HeapFree(GetProcessHeap(), 0, buf2);
484 } else
485 xret=VIF_MISMATCH|VIF_SRCOLD;
486 HeapFree(GetProcessHeap(), 0, buf1);
489 if (xret) {
490 if (*tmpfilelen<strlen(tmpfn+tmplast)) {
491 xret|=VIF_BUFFTOOSMALL;
492 DeleteFileA(tmpfn);
493 } else {
494 strcpy(tmpfile,tmpfn+tmplast);
495 *tmpfilelen = strlen(tmpfn+tmplast)+1;
496 xret|=VIF_TEMPFILE;
498 } else {
499 if (INVALID_FILE_ATTRIBUTES!=GetFileAttributesA(destfn))
500 if (!DeleteFileA(destfn)) {
501 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETE;
502 DeleteFileA(tmpfn);
503 LZClose(hfsrc);
504 return xret;
506 if ((!(flags & VIFF_DONTDELETEOLD)) &&
507 curdir &&
508 *curdir &&
509 lstrcmpiA(curdir,pdest)
511 char curfn[260];
513 sprintf(curfn,"%s\\%s",curdir,destfilename);
514 if (INVALID_FILE_ATTRIBUTES != GetFileAttributesA(curfn)) {
515 /* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */
516 if (!DeleteFileA(curfn))
517 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR;
520 if (!MoveFileA(tmpfn,destfn)) {
521 xret|=_error2vif(GetLastError())|VIF_CANNOTRENAME;
522 DeleteFileA(tmpfn);
525 LZClose(hfsrc);
526 return xret;
530 /******************************************************************************
531 * VerInstallFileW [VERSION.@]
533 DWORD WINAPI VerInstallFileW(
534 DWORD flags,LPCWSTR srcfilename,LPCWSTR destfilename,LPCWSTR srcdir,
535 LPCWSTR destdir,LPCWSTR curdir,LPWSTR tmpfile,PUINT tmpfilelen )
537 LPSTR wsrcf = NULL, wsrcd = NULL, wdestf = NULL, wdestd = NULL, wtmpf = NULL, wcurd = NULL;
538 DWORD ret;
539 UINT len;
541 if (srcfilename)
543 len = WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, NULL, 0, NULL, NULL );
544 if ((wsrcf = HeapAlloc( GetProcessHeap(), 0, len )))
545 WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, wsrcf, len, NULL, NULL );
547 if (srcdir)
549 len = WideCharToMultiByte( CP_ACP, 0, srcdir, -1, NULL, 0, NULL, NULL );
550 if ((wsrcd = HeapAlloc( GetProcessHeap(), 0, len )))
551 WideCharToMultiByte( CP_ACP, 0, srcdir, -1, wsrcd, len, NULL, NULL );
553 if (destfilename)
555 len = WideCharToMultiByte( CP_ACP, 0, destfilename, -1, NULL, 0, NULL, NULL );
556 if ((wdestf = HeapAlloc( GetProcessHeap(), 0, len )))
557 WideCharToMultiByte( CP_ACP, 0, destfilename, -1, wdestf, len, NULL, NULL );
559 if (destdir)
561 len = WideCharToMultiByte( CP_ACP, 0, destdir, -1, NULL, 0, NULL, NULL );
562 if ((wdestd = HeapAlloc( GetProcessHeap(), 0, len )))
563 WideCharToMultiByte( CP_ACP, 0, destdir, -1, wdestd, len, NULL, NULL );
565 if (curdir)
567 len = WideCharToMultiByte( CP_ACP, 0, curdir, -1, NULL, 0, NULL, NULL );
568 if ((wcurd = HeapAlloc( GetProcessHeap(), 0, len )))
569 WideCharToMultiByte( CP_ACP, 0, curdir, -1, wcurd, len, NULL, NULL );
571 len = *tmpfilelen * sizeof(WCHAR);
572 wtmpf = HeapAlloc( GetProcessHeap(), 0, len );
573 ret = VerInstallFileA(flags,wsrcf,wdestf,wsrcd,wdestd,wcurd,wtmpf,&len);
574 if (!ret)
575 *tmpfilelen = MultiByteToWideChar( CP_ACP, 0, wtmpf, -1, tmpfile, *tmpfilelen );
576 else if (ret & VIF_BUFFTOOSMALL)
577 *tmpfilelen = len; /* FIXME: not correct */
579 HeapFree( GetProcessHeap(), 0, wsrcf );
580 HeapFree( GetProcessHeap(), 0, wsrcd );
581 HeapFree( GetProcessHeap(), 0, wdestf );
582 HeapFree( GetProcessHeap(), 0, wdestd );
583 HeapFree( GetProcessHeap(), 0, wtmpf );
584 HeapFree( GetProcessHeap(), 0, wcurd );
585 return ret;