NSIS/Source/exehead/util.c
2010-03-30 17:50:08 +00:00

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);
}