
git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@6045 212acab6-be3b-0410-9dea-997c60f758d6
1033 lines
26 KiB
C
1033 lines
26 KiB
C
/*
|
|
* util.c
|
|
*
|
|
* This file is a part of NSIS.
|
|
*
|
|
* Copyright (C) 1999-2009 Nullsoft and Contributors
|
|
*
|
|
* Licensed under the zlib/libpng license (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
*
|
|
* Licence details can be found in the file COPYING.
|
|
*
|
|
* This software is provided 'as-is', without any express or implied
|
|
* warranty.
|
|
*
|
|
* Unicode support by Jim Park -- 08/11/2007
|
|
*/
|
|
|
|
#include "../Platform.h"
|
|
#include <shellapi.h>
|
|
#include "util.h"
|
|
#include "state.h"
|
|
#include "config.h"
|
|
#include "lang.h"
|
|
#include "fileform.h"
|
|
#include "exec.h"
|
|
#include "ui.h"
|
|
#include "resource.h"
|
|
#include "../tchar.h"
|
|
|
|
#ifdef NSIS_CONFIG_LOG
|
|
#if !defined(NSIS_CONFIG_LOG_ODS) && !defined(NSIS_CONFIG_LOG_STDOUT)
|
|
TCHAR g_log_file[1024];
|
|
#endif
|
|
#endif
|
|
|
|
// *** DO NOT DECLARE MORE VARIABLES INSIDE THIS PRAGMAS ***
|
|
// This will produce a special section called ".ndata" (stands for nsis data)
|
|
// this way makensis during build time, can search for this section by name
|
|
// and change the virtual size of this section
|
|
// which result in extra memory for extra variables without code to do allocation :)
|
|
// nsis then removes the "DISCARDABLE" style from section (for safe)
|
|
#ifdef _MSC_VER
|
|
# pragma bss_seg(NSIS_VARS_SECTION)
|
|
NSIS_STRING g_usrvars[1];
|
|
# pragma bss_seg()
|
|
# pragma comment(linker, "/section:" NSIS_VARS_SECTION ",rwd")
|
|
#else
|
|
# ifdef __GNUC__
|
|
NSIS_STRING g_usrvars[1] __attribute__((section (NSIS_VARS_SECTION)));
|
|
# else
|
|
# error Unknown compiler. You must implement the seperate PE section yourself.
|
|
# endif
|
|
#endif
|
|
|
|
HANDLE NSISCALL myCreateProcess(TCHAR *cmd)
|
|
{
|
|
PROCESS_INFORMATION ProcInfo;
|
|
static STARTUPINFO StartUp;
|
|
StartUp.cb=sizeof(StartUp);
|
|
if (!CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &StartUp, &ProcInfo))
|
|
return NULL;
|
|
CloseHandle(ProcInfo.hThread);
|
|
return ProcInfo.hProcess;
|
|
}
|
|
|
|
/*BOOL NSISCALL my_SetWindowText(HWND hWnd, const TCHAR *val)
|
|
{
|
|
return SendMessage(hWnd,WM_SETTEXT,0,(LPARAM)val);
|
|
}*/
|
|
|
|
BOOL NSISCALL my_SetDialogItemText(HWND dlg, UINT idx, const TCHAR *val)
|
|
{
|
|
return SetDlgItemText(dlg,idx,val);
|
|
// return my_SetWindowText(GetDlgItem(dlg, idx), val);
|
|
}
|
|
|
|
int NSISCALL my_GetDialogItemText(UINT idx, TCHAR *val)
|
|
{
|
|
extern HWND m_curwnd;
|
|
return GetDlgItemText(m_curwnd, idx, val, NSIS_MAX_STRLEN);
|
|
// return my_GetWindowText(GetDlgItem(m_curwnd, idx), val, NSIS_MAX_STRLEN);
|
|
}
|
|
|
|
int NSISCALL my_MessageBox(const TCHAR *text, UINT type) {
|
|
int _type = type & 0x001FFFFF;
|
|
static MSGBOXPARAMS mbp = {
|
|
sizeof(MSGBOXPARAMS),
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
MAKEINTRESOURCE(IDI_ICON2),
|
|
0,
|
|
0,
|
|
0
|
|
};
|
|
|
|
#ifdef NSIS_CONFIG_SILENT_SUPPORT
|
|
// default for silent installers
|
|
if (g_exec_flags.silent && type >> 21)
|
|
return type >> 21;
|
|
#endif
|
|
// no silent or no default, just show
|
|
if (g_exec_flags.rtl)
|
|
_type ^= MB_RIGHT | MB_RTLREADING;
|
|
|
|
mbp.hwndOwner = g_hwnd;
|
|
mbp.hInstance = g_hInstance;
|
|
mbp.lpszText = text;
|
|
mbp.lpszCaption = g_caption;
|
|
mbp.dwStyle = _type;
|
|
|
|
return MessageBoxIndirect(&mbp);
|
|
}
|
|
|
|
void NSISCALL myDelete(TCHAR *buf, int flags)
|
|
{
|
|
static TCHAR lbuf[NSIS_MAX_STRLEN];
|
|
|
|
HANDLE h;
|
|
WIN32_FIND_DATA fd;
|
|
TCHAR *fn;
|
|
int valid_dir=is_valid_instpath(buf);
|
|
|
|
if ((flags & DEL_SIMPLE))
|
|
{
|
|
g_exec_flags.exec_error += !DeleteFile(buf);
|
|
return;
|
|
}
|
|
|
|
#ifdef NSIS_SUPPORT_RMDIR
|
|
if (!(flags & DEL_DIR) || (valid_dir && (flags & DEL_RECURSE)))
|
|
#endif//NSIS_SUPPORT_RMDIR
|
|
{
|
|
mystrcpy(lbuf,buf);
|
|
#ifdef NSIS_SUPPORT_RMDIR
|
|
if (flags & DEL_DIR)
|
|
mystrcat(lbuf,_T("\\*.*"));
|
|
else
|
|
#endif//NSIS_SUPPORT_RMDIR
|
|
trimslashtoend(buf);
|
|
|
|
// only append backslash if the path isn't relative to the working directory [bug #1851273]
|
|
if (*buf || *lbuf == _T('\\'))
|
|
mystrcat(buf,_T("\\"));
|
|
|
|
fn=buf+mystrlen(buf);
|
|
|
|
h = FindFirstFile(lbuf,&fd);
|
|
if (h != INVALID_HANDLE_VALUE)
|
|
{
|
|
do
|
|
{
|
|
TCHAR *fdfn = fd.cFileName;
|
|
if (*findchar(fdfn, _T('?')) && *fd.cAlternateFileName)
|
|
// name contains unicode, use short name
|
|
fdfn = fd.cAlternateFileName;
|
|
|
|
#ifdef NSIS_SUPPORT_RMDIR
|
|
if (fdfn[0] == _T('.') && !fdfn[1]) continue;
|
|
if (fdfn[0] == _T('.') && fdfn[1] == _T('.') && !fdfn[2]) continue;
|
|
#endif//NSIS_SUPPORT_RMDIR
|
|
{
|
|
mystrcpy(fn,fdfn);
|
|
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
#ifdef NSIS_SUPPORT_RMDIR
|
|
if ((flags & (DEL_DIR | DEL_RECURSE)) == (DEL_DIR | DEL_RECURSE))
|
|
{
|
|
myDelete(buf,flags);
|
|
}
|
|
#endif//NSIS_SUPPORT_RMDIR
|
|
}
|
|
else
|
|
{
|
|
log_printf2(_T("Delete: DeleteFile(\"%s\")"),buf);
|
|
remove_ro_attr(buf);
|
|
if (!DeleteFile(buf))
|
|
{
|
|
#ifdef NSIS_SUPPORT_MOVEONREBOOT
|
|
if (flags & DEL_REBOOT)
|
|
{
|
|
log_printf2(_T("Delete: DeleteFile on Reboot(\"%s\")"),buf);
|
|
update_status_text(LANG_DELETEONREBOOT,buf);
|
|
MoveFileOnReboot(buf,NULL);
|
|
}
|
|
else
|
|
#endif//NSIS_SUPPORT_MOVEONREBOOT
|
|
{
|
|
log_printf2(_T("Delete: DeleteFile failed(\"%s\")"),buf);
|
|
g_exec_flags.exec_error++;
|
|
}
|
|
}
|
|
else
|
|
update_status_text(LANG_DELETEFILE,buf);
|
|
}
|
|
}
|
|
} while (FindNextFile(h,&fd));
|
|
FindClose(h);
|
|
}
|
|
|
|
#ifdef NSIS_SUPPORT_RMDIR
|
|
if (flags & DEL_DIR)
|
|
fn[-1]=0;
|
|
#endif//NSIS_SUPPORT_RMDIR
|
|
}
|
|
|
|
#ifdef NSIS_SUPPORT_RMDIR
|
|
if ((flags & DEL_DIR))
|
|
{
|
|
if (!valid_dir)
|
|
{
|
|
log_printf2(_T("RMDir: RemoveDirectory invalid input(\"%s\")"),buf);
|
|
g_exec_flags.exec_error++;
|
|
}
|
|
else if (file_exists(buf))
|
|
{
|
|
addtrailingslash(buf);
|
|
log_printf2(_T("RMDir: RemoveDirectory(\"%s\")"),buf);
|
|
remove_ro_attr(buf);
|
|
if (!RemoveDirectory(buf))
|
|
{
|
|
#ifdef NSIS_SUPPORT_MOVEONREBOOT
|
|
if (flags & DEL_REBOOT)
|
|
{
|
|
log_printf2(_T("RMDir: RemoveDirectory on Reboot(\"%s\")"),buf);
|
|
update_status_text(LANG_DELETEONREBOOT,buf);
|
|
MoveFileOnReboot(buf,NULL);
|
|
}
|
|
else
|
|
#endif//NSIS_SUPPORT_MOVEONREBOOT
|
|
{
|
|
log_printf2(_T("RMDir: RemoveDirectory failed(\"%s\")"),buf);
|
|
g_exec_flags.exec_error++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
update_status_text(LANG_REMOVEDIR,buf);
|
|
}
|
|
}
|
|
}
|
|
#endif//NSIS_SUPPORT_RMDIR
|
|
}
|
|
|
|
TCHAR *NSISCALL addtrailingslash(TCHAR *str)
|
|
{
|
|
if (lastchar(str)!=_T('\\')) mystrcat(str,_T("\\"));
|
|
return str;
|
|
}
|
|
|
|
/*char NSISCALL lastchar(const char *str)
|
|
{
|
|
return *CharPrev(str,str+mystrlen(str));
|
|
}*/
|
|
|
|
TCHAR * NSISCALL findchar(TCHAR *str, TCHAR c)
|
|
{
|
|
while (*str && *str != c)
|
|
{
|
|
str = CharNext(str);
|
|
}
|
|
return str;
|
|
}
|
|
|
|
// Separates a full path to the directory portion and file name portion
|
|
// and returns the pointer to the filename portion.
|
|
TCHAR * NSISCALL trimslashtoend(TCHAR *buf)
|
|
{
|
|
TCHAR *p = buf + mystrlen(buf);
|
|
do
|
|
{
|
|
if (*p == _T('\\'))
|
|
break;
|
|
p = CharPrev(buf, p);
|
|
} while (p > buf);
|
|
|
|
*p = 0;
|
|
|
|
return p + 1;
|
|
}
|
|
|
|
int NSISCALL validpathspec(TCHAR *ubuf)
|
|
{
|
|
TCHAR dl = ubuf[0] | 0x20; // convert alleged drive letter to lower case
|
|
return ((ubuf[0] == _T('\\') && ubuf[1] == _T('\\')) ||
|
|
(dl >= _T('a') && dl <= _T('z') && ubuf[1] == _T(':')));
|
|
}
|
|
|
|
TCHAR * NSISCALL skip_root(TCHAR *path)
|
|
{
|
|
TCHAR *p = CharNext(path);
|
|
TCHAR *p2 = CharNext(p);
|
|
|
|
if (*path && p[0] == _T(':') && p[1] == _T('\\'))
|
|
{
|
|
return CharNext(p2);
|
|
}
|
|
else if (path[0] == _T('\\') && path[1] == _T('\\'))
|
|
{
|
|
// skip host and share name
|
|
int x = 2;
|
|
while (x--)
|
|
{
|
|
p2 = findchar(p2, _T('\\'));
|
|
if (!*p2)
|
|
return NULL;
|
|
p2++; // skip backslash
|
|
}
|
|
|
|
return p2;
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
int NSISCALL is_valid_instpath(TCHAR *s)
|
|
{
|
|
static TCHAR tmp[NSIS_MAX_STRLEN];
|
|
TCHAR *root;
|
|
|
|
mystrcpy(tmp, s);
|
|
|
|
root = skip_root(tmp);
|
|
|
|
if (!root)
|
|
return 0;
|
|
|
|
// must be called after skip_root or AllowRootDirInstall won't work.
|
|
// validate_filename removes trailing blackslashes and so converts
|
|
// "C:\" to "C:" which is not a valid directory. skip_root returns
|
|
// NULL for "C:" so the above test returns 0.
|
|
// validate_filename is called so directories such as "C:\ " will
|
|
// not pass as a valid non-root directory.
|
|
validate_filename(root);
|
|
|
|
if ((g_flags & CH_FLAGS_NO_ROOT_DIR) && (!*root || *root == _T('\\')))
|
|
return 0;
|
|
|
|
while (mystrlen(tmp) > root - tmp)
|
|
{
|
|
WIN32_FIND_DATA *fd = file_exists(tmp);
|
|
// if the directory bit not set then it's a file, which is not a valid inst dir...
|
|
// GetFileAttributes is not used because it doesn't work with certain files (error 32)
|
|
// as for concerns of the user using * or ?, that's invalid anyway...
|
|
if (fd && !(fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
return 0;
|
|
trimslashtoend(tmp);
|
|
}
|
|
|
|
// if the root drive exists
|
|
addtrailingslash(tmp); // don't check the current directory, check the root directory
|
|
if (GetFileAttributes(tmp) == INVALID_FILE_ATTRIBUTES)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Used strictly for the wininit.ini file which is an ASCII file.
|
|
char * NSISCALL mystrstriA(char *a, const char *b)
|
|
{
|
|
int l = lstrlenA(b);
|
|
while (lstrlenA(a) >= l)
|
|
{
|
|
char c = a[l];
|
|
a[l] = 0;
|
|
if (!lstrcmpiA(a, b))
|
|
{
|
|
a[l] = c;
|
|
return a;
|
|
}
|
|
a[l] = c;
|
|
a = CharNextA(a);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// mini_memcpy takes the number of bytes to copy.
|
|
void NSISCALL mini_memcpy(void *out, const void *in, int len)
|
|
{
|
|
char *c_out=(char*)out;
|
|
char *c_in=(char *)in;
|
|
while (len-- > 0)
|
|
{
|
|
*c_out++=*c_in++;
|
|
}
|
|
}
|
|
|
|
void NSISCALL remove_ro_attr(TCHAR *file)
|
|
{
|
|
int attr = GetFileAttributes(file);
|
|
if (attr != INVALID_FILE_ATTRIBUTES)
|
|
SetFileAttributes(file,attr&(~FILE_ATTRIBUTE_READONLY));
|
|
}
|
|
|
|
HANDLE NSISCALL myOpenFile(const TCHAR *fn, DWORD da, DWORD cd)
|
|
{
|
|
int attr = GetFileAttributes(fn);
|
|
return CreateFile(
|
|
fn,
|
|
da,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
cd,
|
|
attr == INVALID_FILE_ATTRIBUTES ? 0 : attr,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
TCHAR * NSISCALL my_GetTempFileName(TCHAR *buf, const TCHAR *dir)
|
|
{
|
|
int n = 100;
|
|
while (n--)
|
|
{
|
|
TCHAR prefix[4] = _T("nsa");
|
|
prefix[2] += (TCHAR)(GetTickCount() % 26);
|
|
if (GetTempFileName(dir, prefix, 0, buf))
|
|
return buf;
|
|
}
|
|
*buf = 0;
|
|
return 0;
|
|
}
|
|
|
|
#ifdef NSIS_SUPPORT_MOVEONREBOOT
|
|
/** Modifies the wininit.ini file to rename / delete a file.
|
|
*
|
|
* @param prevName The previous / current name of the file.
|
|
* @param newName The new name to move the file to. If NULL, the current file
|
|
* will be deleted.
|
|
*/
|
|
void RenameViaWininit(const TCHAR* prevName, const TCHAR* newName)
|
|
{
|
|
static char szRenameLine[1024];
|
|
static TCHAR wininit[1024];
|
|
static TCHAR tmpbuf[1024];
|
|
|
|
int cchRenameLine;
|
|
LPCSTR szRenameSec = "[Rename]\r\n"; // rename section marker
|
|
HANDLE hfile;
|
|
DWORD dwFileSize;
|
|
DWORD dwBytes;
|
|
DWORD dwRenameLinePos;
|
|
char *pszWinInit; // Contains the file contents of wininit.ini
|
|
|
|
int spn; // length of the short path name in TCHARs.
|
|
|
|
lstrcpy(tmpbuf, _T("NUL"));
|
|
|
|
if (newName) {
|
|
// create the file if it's not already there to prevent GetShortPathName from failing
|
|
CloseHandle(myOpenFile(newName,0,CREATE_NEW));
|
|
spn = GetShortPathName(newName,tmpbuf,1024);
|
|
if (!spn || spn > 1024)
|
|
return;
|
|
}
|
|
// wininit is used as a temporary here
|
|
spn = GetShortPathName(prevName,wininit,1024);
|
|
if (!spn || spn > 1024)
|
|
return;
|
|
cchRenameLine = wsprintfA(szRenameLine, "%s=%s\r\n", tmpbuf, wininit);
|
|
// Get the path to the wininit.ini file.
|
|
GetNSISString(wininit, g_header->str_wininit);
|
|
|
|
hfile = myOpenFile(wininit, GENERIC_READ | GENERIC_WRITE, OPEN_ALWAYS);
|
|
|
|
if (hfile != INVALID_HANDLE_VALUE)
|
|
{
|
|
// We are now working on the Windows wininit file
|
|
dwFileSize = GetFileSize(hfile, NULL);
|
|
pszWinInit = (char*) GlobalAlloc(GPTR, dwFileSize + cchRenameLine + 10);
|
|
|
|
if (pszWinInit != NULL)
|
|
{
|
|
if (ReadFile(hfile, pszWinInit, dwFileSize, &dwBytes, NULL) && dwFileSize == dwBytes)
|
|
{
|
|
// Look for the rename section in the current file.
|
|
LPSTR pszRenameSecInFile = mystrstriA(pszWinInit, szRenameSec);
|
|
if (pszRenameSecInFile == NULL)
|
|
{
|
|
// No rename section. So we add it to the end of file.
|
|
lstrcpyA(pszWinInit+dwFileSize, szRenameSec);
|
|
dwFileSize += 10;
|
|
dwRenameLinePos = dwFileSize;
|
|
}
|
|
else
|
|
{
|
|
// There is a rename section, but is there another section after it?
|
|
char *pszFirstRenameLine = pszRenameSecInFile+10;
|
|
char *pszNextSec = mystrstriA(pszFirstRenameLine,"\n[");
|
|
if (pszNextSec)
|
|
{
|
|
TCHAR *p = ++pszNextSec;
|
|
while (p < pszWinInit + dwFileSize) {
|
|
p[cchRenameLine] = *p;
|
|
p++;
|
|
}
|
|
|
|
dwRenameLinePos = pszNextSec - pszWinInit;
|
|
}
|
|
// rename section is last, stick item at end of file
|
|
else dwRenameLinePos = dwFileSize;
|
|
}
|
|
|
|
mini_memcpy(&pszWinInit[dwRenameLinePos], szRenameLine, cchRenameLine);
|
|
dwFileSize += cchRenameLine;
|
|
|
|
SetFilePointer(hfile, 0, NULL, FILE_BEGIN);
|
|
WriteFile(hfile, pszWinInit, dwFileSize, &dwBytes, NULL);
|
|
|
|
GlobalFree(pszWinInit);
|
|
}
|
|
}
|
|
|
|
CloseHandle(hfile);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* MoveFileOnReboot tries to move a file by the name of pszExisting to the
|
|
* name pszNew.
|
|
*
|
|
* @param pszExisting The old name of the file.
|
|
* @param pszNew The new name of the file.
|
|
*/
|
|
void NSISCALL MoveFileOnReboot(LPCTSTR pszExisting, LPCTSTR pszNew)
|
|
{
|
|
BOOL fOk = 0;
|
|
typedef BOOL (WINAPI *mfea_t)(LPCTSTR lpExistingFileName,LPCTSTR lpNewFileName,DWORD dwFlags);
|
|
mfea_t mfea;
|
|
mfea=(mfea_t) myGetProcAddress(MGA_MoveFileEx);
|
|
if (mfea)
|
|
{
|
|
fOk=mfea(pszExisting, pszNew, MOVEFILE_DELAY_UNTIL_REBOOT|MOVEFILE_REPLACE_EXISTING);
|
|
}
|
|
|
|
if (!fOk)
|
|
{
|
|
RenameViaWininit(pszExisting, pszNew);
|
|
}
|
|
|
|
#ifdef NSIS_SUPPORT_REBOOT
|
|
g_exec_flags.exec_reboot++;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
// The value of registry->sub->name is stored in out. If failure, then out becomes
|
|
// an empty string "".
|
|
void NSISCALL myRegGetStr(HKEY root, const TCHAR *sub, const TCHAR *name, TCHAR *out, int x64)
|
|
{
|
|
HKEY hKey;
|
|
*out=0;
|
|
if (RegOpenKeyEx(root,sub,0,KEY_READ|(x64?KEY_WOW64_64KEY:0),&hKey) == ERROR_SUCCESS)
|
|
{
|
|
DWORD l = NSIS_MAX_STRLEN*sizeof(TCHAR);
|
|
DWORD t;
|
|
// Note that RegQueryValueEx returns Unicode strings if _UNICODE is defined for the
|
|
// REG_SZ type.
|
|
if (RegQueryValueEx(hKey,name,NULL,&t,(LPBYTE)out,&l ) != ERROR_SUCCESS || (t != REG_SZ && t != REG_EXPAND_SZ)) *out=0;
|
|
out[NSIS_MAX_STRLEN-1]=0;
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
|
|
void NSISCALL myitoa(TCHAR *s, int d)
|
|
{
|
|
static const TCHAR c[] = _T("%d");
|
|
wsprintf(s,c,d);
|
|
}
|
|
|
|
int NSISCALL myatoi(TCHAR *s)
|
|
{
|
|
unsigned int v=0;
|
|
int sign=1; // sign of positive
|
|
TCHAR m=10; // base of 10
|
|
TCHAR t=_T('9'); // cap top of numbers at 9
|
|
|
|
if (*s == _T('-'))
|
|
{
|
|
s++; //skip over -
|
|
sign=-1; // sign flip
|
|
}
|
|
|
|
if (*s == _T('0'))
|
|
{
|
|
s++; // skip over 0
|
|
if (s[0] >= _T('0') && s[0] <= _T('7'))
|
|
{
|
|
m=8; // base of 8
|
|
t=_T('7'); // cap top at 7
|
|
}
|
|
if ((s[0] & ~0x20) == _T('X'))
|
|
{
|
|
m=16; // base of 16
|
|
s++; // advance over 'x'
|
|
}
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
int c=*s++;
|
|
if (c >= _T('0') && c <= t) c-=_T('0');
|
|
// clever little trick to do both upper and lowercase A-F.
|
|
else if (m==16 && (c & ~0x20) >= _T('A') && (c & ~0x20) <= _T('F')) c = (c & 7) + 9;
|
|
else break;
|
|
v*=m;
|
|
v+=c;
|
|
}
|
|
return ((int)v)*sign;
|
|
}
|
|
|
|
// Straight copies of selected shell functions. Calling local functions
|
|
// requires less code than DLL functions. For the savings to outweigh the cost
|
|
// of a new function there should be about a couple of dozen or so calls.
|
|
TCHAR * NSISCALL mystrcpy(TCHAR *out, const TCHAR *in)
|
|
{
|
|
return lstrcpyn(out, in, NSIS_MAX_STRLEN);
|
|
}
|
|
|
|
int NSISCALL mystrlen(const TCHAR *in)
|
|
{
|
|
return lstrlen(in);
|
|
}
|
|
|
|
TCHAR * NSISCALL mystrcat(TCHAR *out, const TCHAR *concat)
|
|
{
|
|
return lstrcat(out, concat);
|
|
}
|
|
|
|
TCHAR ps_tmpbuf[NSIS_MAX_STRLEN*2];
|
|
|
|
const TCHAR SYSREGKEY[] = _T("Software\\Microsoft\\Windows\\CurrentVersion");
|
|
const TCHAR QUICKLAUNCH[] = _T("\\Microsoft\\Internet Explorer\\Quick Launch");
|
|
|
|
typedef HRESULT (__stdcall * PFNSHGETFOLDERPATH)(HWND, int, HANDLE, DWORD, LPTSTR);
|
|
extern void *g_SHGetFolderPath;
|
|
|
|
// Based on Dave Laundon's simplified process_string
|
|
// The string actually has a lot of different data encoded into it. This
|
|
// function extracts the special data out and puts it into outbuf.
|
|
TCHAR * NSISCALL GetNSISString(TCHAR *outbuf, int strtab)
|
|
{
|
|
// This looks at the g_block (copied from header->blocks) and
|
|
// indexes into the language
|
|
TCHAR *in = (TCHAR*)GetNSISStringNP(GetNSISTab(strtab));
|
|
TCHAR *out = ps_tmpbuf;
|
|
|
|
// Still working within ps_tmpbuf, so set out to the
|
|
// current position that is passed in.
|
|
if (outbuf >= ps_tmpbuf &&
|
|
(size_t) (outbuf - ps_tmpbuf) < COUNTOF(ps_tmpbuf))
|
|
{
|
|
out = outbuf;
|
|
outbuf = 0;
|
|
}
|
|
|
|
while (*in && out - ps_tmpbuf < NSIS_MAX_STRLEN)
|
|
{
|
|
_TUCHAR nVarIdx = (_TUCHAR)*in++;
|
|
int nData;
|
|
int fldrs[4];
|
|
if (nVarIdx > NS_CODES_START)
|
|
{
|
|
nData = ((in[1] & 0x7F) << 7) | (in[0] & 0x7F);
|
|
fldrs[0] = in[0] | CSIDL_FLAG_CREATE; // current user
|
|
fldrs[1] = in[0];
|
|
fldrs[2] = in[1] | CSIDL_FLAG_CREATE; // all users
|
|
fldrs[3] = in[1];
|
|
in += sizeof(SHORT)/sizeof(TCHAR);
|
|
|
|
if (nVarIdx == NS_SHELL_CODE)
|
|
{
|
|
LPITEMIDLIST idl;
|
|
|
|
int x = 2;
|
|
DWORD ver = GetVersion();
|
|
|
|
/*
|
|
|
|
SHGetFolderPath as provided by shfolder.dll is used to get special folders
|
|
unless the installer is running on Windows 95/98. For 95/98 shfolder.dll is
|
|
only used for the Application Data and Documents folder (if the DLL exists).
|
|
Otherwise, the old SHGetSpecialFolderLocation API is called.
|
|
|
|
The reason for not using shfolder.dll for all folders on 95/98 is that some
|
|
unsupported folders (such as the Start Menu folder for all users) are
|
|
simulated instead of returning an error so we can fall back on the current
|
|
user folder.
|
|
|
|
SHGetFolderPath in shell32.dll could be called directly for Windows versions
|
|
later than 95/98 but there is no need to do so, because shfolder.dll is still
|
|
provided and calls shell32.dll.
|
|
|
|
*/
|
|
|
|
BOOL use_shfolder =
|
|
// Use shfolder if not on 95/98
|
|
!((ver & 0x80000000) && (LOWORD(ver) != 0x5A04)) ||
|
|
|
|
// Unless the Application Data or Documents folder is requested
|
|
(
|
|
(fldrs[3] == CSIDL_COMMON_APPDATA) ||
|
|
(fldrs[3] == CSIDL_COMMON_DOCUMENTS)
|
|
);
|
|
|
|
/* Carry on... shfolder stuff is over. */
|
|
|
|
if (g_exec_flags.all_user_var)
|
|
{
|
|
x = 4;
|
|
}
|
|
|
|
if (fldrs[1] & 0x80)
|
|
{
|
|
myRegGetStr(HKEY_LOCAL_MACHINE, SYSREGKEY, GetNSISStringNP(fldrs[1] & 0x3F), out, fldrs[1] & 0x40);
|
|
if (!*out)
|
|
GetNSISString(out, fldrs[3]);
|
|
x = 0;
|
|
}
|
|
else if (fldrs[1] == CSIDL_SYSTEM)
|
|
{
|
|
GetSystemDirectory(out, NSIS_MAX_STRLEN);
|
|
x = 0;
|
|
}
|
|
else if (fldrs[1] == CSIDL_WINDOWS)
|
|
{
|
|
GetWindowsDirectory(out, NSIS_MAX_STRLEN);
|
|
x = 0;
|
|
}
|
|
|
|
while (x--)
|
|
{
|
|
if (g_SHGetFolderPath && use_shfolder)
|
|
{
|
|
PFNSHGETFOLDERPATH SHGetFolderPathFunc = (PFNSHGETFOLDERPATH) g_SHGetFolderPath;
|
|
if (!SHGetFolderPathFunc(g_hwnd, fldrs[x], NULL, SHGFP_TYPE_CURRENT, out))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!SHGetSpecialFolderLocation(g_hwnd, fldrs[x], &idl))
|
|
{
|
|
BOOL res = SHGetPathFromIDList(idl, out);
|
|
CoTaskMemFree(idl);
|
|
if (res) break;
|
|
}
|
|
|
|
*out=0;
|
|
}
|
|
|
|
if (*out)
|
|
{
|
|
// all users' version is CSIDL_APPDATA only for $QUICKLAUNCH
|
|
// for normal $APPDATA, it'd be CSIDL_APPDATA_COMMON
|
|
if (fldrs[3] == CSIDL_APPDATA)
|
|
{
|
|
mystrcat(out, QUICKLAUNCH);
|
|
}
|
|
}
|
|
validate_filename(out);
|
|
}
|
|
else if (nVarIdx == NS_VAR_CODE)
|
|
{
|
|
if (nData == 29) // $HWNDPARENT
|
|
myitoa(out, (unsigned int) g_hwnd);
|
|
else
|
|
mystrcpy(out, g_usrvars[nData]);
|
|
// validate the directory name
|
|
if ((unsigned int)(nData - 21) < 7) {
|
|
// validate paths for $INSTDIR, $OUTDIR, $EXEDIR, $LANGUAGE, $TEMP, $PLUGINSDIR and $EXEPATH
|
|
// $LANGUAGE is just a number anyway...
|
|
validate_filename(out);
|
|
}
|
|
} // == VAR_CODES_START
|
|
else if (nVarIdx == NS_LANG_CODE)
|
|
{
|
|
GetNSISString(out, -nData-1);
|
|
}
|
|
out += mystrlen(out);
|
|
}
|
|
else if (nVarIdx == NS_SKIP_CODE)
|
|
{
|
|
*out++ = *in++;
|
|
}
|
|
else // Normal char
|
|
{
|
|
*out++ = nVarIdx;
|
|
}
|
|
} // while
|
|
*out = 0;
|
|
if (outbuf)
|
|
return mystrcpy(outbuf, ps_tmpbuf);
|
|
return ps_tmpbuf;
|
|
}
|
|
|
|
void NSISCALL validate_filename(TCHAR *in) {
|
|
TCHAR *nono = _T("*?|<>/\":");
|
|
TCHAR *out;
|
|
TCHAR *out_save;
|
|
|
|
// ignoring spaces is wrong, _T(" C:\blah") is invalid
|
|
//while (*in == _T(' ')) in = CharNext(in);
|
|
|
|
if (in[0] == _T('\\') && in[1] == _T('\\') && in[2] == _T('?') && in[3] == _T('\\'))
|
|
{
|
|
// at least four bytes
|
|
in += 4;
|
|
}
|
|
if (*in)
|
|
{
|
|
// at least two bytes
|
|
if (validpathspec(in)) in += 2;
|
|
}
|
|
out = out_save = in;
|
|
while (*in)
|
|
{
|
|
if ((_TUCHAR)*in > 31 && !*findchar(nono, *in))
|
|
{
|
|
mini_memcpy(out, in, CharNext(in) - in);
|
|
out = CharNext(out);
|
|
}
|
|
in = CharNext(in);
|
|
}
|
|
*out = 0;
|
|
do
|
|
{
|
|
out = CharPrev(out_save, out);
|
|
if (*out == _T(' ') || *out == _T('\\'))
|
|
*out = 0;
|
|
else
|
|
break;
|
|
} while (out_save < out);
|
|
}
|
|
|
|
#ifdef NSIS_CONFIG_LOG
|
|
int log_dolog;
|
|
TCHAR log_text[2048]; // 1024 for each wsprintf
|
|
|
|
#if !defined(NSIS_CONFIG_LOG_ODS) && !defined(NSIS_CONFIG_LOG_STDOUT)
|
|
void NSISCALL log_write(int close)
|
|
{
|
|
static HANDLE fp=INVALID_HANDLE_VALUE;
|
|
if (close)
|
|
{
|
|
if (fp!=INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(fp);
|
|
}
|
|
fp=INVALID_HANDLE_VALUE;
|
|
return;
|
|
}
|
|
if (log_dolog)
|
|
{
|
|
if (g_log_file[0] && fp==INVALID_HANDLE_VALUE)
|
|
{
|
|
fp = myOpenFile(g_log_file,GENERIC_WRITE,OPEN_ALWAYS);
|
|
if (fp!=INVALID_HANDLE_VALUE)
|
|
SetFilePointer(fp,0,NULL,FILE_END);
|
|
}
|
|
if (fp!=INVALID_HANDLE_VALUE)
|
|
{
|
|
DWORD d;
|
|
mystrcat(log_text,_T("\r\n"));
|
|
WriteFile(fp,log_text,mystrlen(log_text)*sizeof(TCHAR),&d,NULL);
|
|
}
|
|
}
|
|
}
|
|
#endif//!NSIS_CONFIG_LOG_ODS && !NSIS_CONFIG_LOG_STDOUT
|
|
|
|
const TCHAR * _RegKeyHandleToName(HKEY hKey)
|
|
{
|
|
if (hKey == HKEY_CLASSES_ROOT)
|
|
return _T("HKEY_CLASSES_ROOT");
|
|
else if (hKey == HKEY_CURRENT_USER)
|
|
return _T("HKEY_CURRENT_USER");
|
|
else if (hKey == HKEY_LOCAL_MACHINE)
|
|
return _T("HKEY_LOCAL_MACHINE");
|
|
else if (hKey == HKEY_USERS)
|
|
return _T("HKEY_USERS");
|
|
else if (hKey == HKEY_PERFORMANCE_DATA)
|
|
return _T("HKEY_PERFORMANCE_DATA");
|
|
else if (hKey == HKEY_CURRENT_CONFIG)
|
|
return _T("HKEY_CURRENT_CONFIG");
|
|
else if (hKey == HKEY_DYN_DATA)
|
|
return _T("HKEY_DYN_DATA");
|
|
else
|
|
return _T("invalid registry key");
|
|
}
|
|
|
|
void _LogData2Hex(TCHAR *buf, size_t buflen, BYTE *data, size_t datalen)
|
|
{
|
|
TCHAR *p = buf;
|
|
|
|
size_t i;
|
|
|
|
int dots = 0;
|
|
size_t bufbytes = buflen / 3; // 2 hex digits, one space/null
|
|
|
|
if (datalen > bufbytes)
|
|
{
|
|
bufbytes--;
|
|
dots = 1;
|
|
}
|
|
else
|
|
bufbytes = datalen;
|
|
|
|
for (i = 0; i < bufbytes; i++)
|
|
{
|
|
wsprintf(p, _T("%02x%c"), data[i], (i == bufbytes - 1) ? _T('\0') : _T(' '));
|
|
p += 3;
|
|
}
|
|
|
|
if (dots)
|
|
mystrcat(buf, _T("..."));
|
|
}
|
|
|
|
#ifdef NSIS_CONFIG_LOG_TIMESTAMP
|
|
void log_timestamp(TCHAR *buf)
|
|
{
|
|
SYSTEMTIME st;
|
|
GetLocalTime(&st);
|
|
wsprintf(buf,_T("[%04hu/%02hu/%02hu %02hu:%02hu:%02hu] "), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
|
|
}
|
|
#else
|
|
# define log_timestamp(x)
|
|
#endif//NSIS_CONFIG_LOG_TIMESTAMP
|
|
|
|
void log_printf(TCHAR *format, ...)
|
|
{
|
|
va_list val;
|
|
va_start(val,format);
|
|
|
|
log_text[0] = _T('\0');
|
|
log_timestamp(log_text);
|
|
wvsprintf(log_text+mystrlen(log_text),format,val);
|
|
|
|
va_end(val);
|
|
#ifdef NSIS_CONFIG_LOG_ODS
|
|
if (log_dolog)
|
|
OutputDebugString(log_text);
|
|
#endif
|
|
#ifdef NSIS_CONFIG_LOG_STDOUT
|
|
if (log_dolog && GetStdHandle(STD_OUTPUT_HANDLE) != INVALID_HANDLE_VALUE)
|
|
{
|
|
DWORD dwBytes;
|
|
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), log_text, lstrlen(log_text), &dwBytes, NULL);
|
|
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), _T("\n"), 1, &dwBytes, NULL);
|
|
}
|
|
#endif
|
|
#if !defined(NSIS_CONFIG_LOG_ODS) && !defined(NSIS_CONFIG_LOG_STDOUT)
|
|
log_write(0);
|
|
#endif
|
|
}
|
|
#endif//NSIS_CONFIG_LOG
|
|
|
|
// Jim Park: This function is non-reentrant because of the static.
|
|
WIN32_FIND_DATA * NSISCALL file_exists(TCHAR *buf)
|
|
{
|
|
HANDLE h;
|
|
static WIN32_FIND_DATA fd;
|
|
h = FindFirstFile(buf,&fd);
|
|
if (h != INVALID_HANDLE_VALUE)
|
|
{
|
|
FindClose(h);
|
|
return &fd;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// Jim Park: Keep these as chars since there's only ANSI version of
|
|
// GetProcAddress.
|
|
struct MGA_FUNC
|
|
{
|
|
const char *dll;
|
|
const char *func;
|
|
};
|
|
|
|
#ifdef _UNICODE
|
|
struct MGA_FUNC MGA_FUNCS[] = {
|
|
{"KERNEL32", "GetDiskFreeSpaceExW"},
|
|
{"KERNEL32", "MoveFileExW"},
|
|
{"ADVAPI32", "RegDeleteKeyExW"},
|
|
{"ADVAPI32", "OpenProcessToken"},
|
|
{"ADVAPI32", "LookupPrivilegeValueW"},
|
|
{"ADVAPI32", "AdjustTokenPrivileges"},
|
|
{"KERNEL32", "GetUserDefaultUILanguage"},
|
|
{"SHLWAPI", "SHAutoComplete"},
|
|
{"SHFOLDER", "SHGetFolderPathW"}
|
|
};
|
|
#else
|
|
struct MGA_FUNC MGA_FUNCS[] = {
|
|
{"KERNEL32", "GetDiskFreeSpaceExA"},
|
|
{"KERNEL32", "MoveFileExA"},
|
|
{"ADVAPI32", "RegDeleteKeyExA"},
|
|
{"ADVAPI32", "OpenProcessToken"},
|
|
{"ADVAPI32", "LookupPrivilegeValueA"},
|
|
{"ADVAPI32", "AdjustTokenPrivileges"},
|
|
{"KERNEL32", "GetUserDefaultUILanguage"},
|
|
{"SHLWAPI", "SHAutoComplete"},
|
|
{"SHFOLDER", "SHGetFolderPathA"}
|
|
};
|
|
#endif
|
|
|
|
/**
|
|
* Given a function enum, it will load the appropriate DLL and get the
|
|
* process address of the function and return the pointer. It's up to
|
|
* the caller to know how to call that function, however.
|
|
*
|
|
* @param func Enum value that indexes the MGA_FUNCS array.
|
|
* @return Pointer to the function identified by the enum value.
|
|
*/
|
|
void * NSISCALL myGetProcAddress(const enum myGetProcAddressFunctions func)
|
|
{
|
|
const char *dll = MGA_FUNCS[func].dll;
|
|
HMODULE hModule = GetModuleHandleA(dll);
|
|
if (!hModule)
|
|
hModule = LoadLibraryA(dll);
|
|
if (!hModule)
|
|
return NULL;
|
|
|
|
return GetProcAddress(hModule, MGA_FUNCS[func].func);
|
|
}
|
|
|
|
void NSISCALL MessageLoop(UINT uCheckedMsg)
|
|
{
|
|
MSG msg;
|
|
while (PeekMessage(&msg, NULL, uCheckedMsg, uCheckedMsg, PM_REMOVE))
|
|
DispatchMessage(&msg);
|
|
}
|