/* * 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 #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); }