From 322c7ac6d241dde39db21e48dc1f914ae3f56ab2 Mon Sep 17 00:00:00 2001 From: anders_k Date: Mon, 20 Apr 2020 17:21:04 +0000 Subject: [PATCH] Added API for compiler host to choose a different output path on output error git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@7169 212acab6-be3b-0410-9dea-997c60f758d6 --- Contrib/Makensisw/makensisw.cpp | 26 +++++++++++-- Contrib/Makensisw/makensisw.h | 14 +++++-- Contrib/Makensisw/utils.h | 13 +++++++ Source/build.cpp | 68 +++++++++++++++++++++++++++++++-- Source/build.h | 24 +++++++++--- Source/growbuf.h | 1 + 6 files changed, 131 insertions(+), 15 deletions(-) diff --git a/Contrib/Makensisw/makensisw.cpp b/Contrib/Makensisw/makensisw.cpp index 2a34ab63..5f889efb 100644 --- a/Contrib/Makensisw/makensisw.cpp +++ b/Contrib/Makensisw/makensisw.cpp @@ -497,9 +497,10 @@ INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam case MakensisAPI::QUERYHOST: { if (MakensisAPI::QH_OUTPUTCHARSET == wParam) { const UINT reqcp = 1200; // We want UTF-16LE - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)(1+reqcp)); - return TRUE; + return DlgRet(hwndDlg, (LONG_PTR)(1+reqcp)); } + else if (MakensisAPI::QH_SUPPORTEDVERSION == wParam) + return DlgRet(hwndDlg, 0x03006000); return FALSE; } case WM_NOTIFY: @@ -546,7 +547,8 @@ INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam return TRUE; case WM_COPYDATA: { - PCOPYDATASTRUCT cds = PCOPYDATASTRUCT(lParam); + using namespace MakensisAPI; + COPYDATASTRUCT *cds = (COPYDATASTRUCT*) lParam, cdsret; switch (cds->dwData) { case MakensisAPI::NOTIFY_SCRIPT: MemSafeFree(g_sdata.input_script); @@ -564,6 +566,24 @@ INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam g_sdata.output_exe = (TCHAR*) MemAlloc(cds->cbData * sizeof(TCHAR)); lstrcpy(g_sdata.output_exe, (TCHAR *)cds->lpData); break; + case MakensisAPI::PROMPT_FILEPATH: + if ((((PROMPT_FILEPATH_DATA*)cds->lpData)->Platform & 7) == sizeof(TCHAR)) + { + TCHAR buf[MAX_PATH]; + lstrcpyn(buf, FSPath::FindLastComponent(((PROMPT_FILEPATH_DATA*)cds->lpData)->Path), COUNTOF(buf)); + OPENFILENAME of = { sizeof(of) }; + of.hwndOwner = hwndDlg; + of.lpstrFilter = _T("*.exe\0*.exe\0*\0*.*\0"); + of.lpstrFile = buf, of.nMaxFile = COUNTOF(buf); + of.Flags = OFN_EXPLORER|OFN_ENABLESIZING|OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT|OFN_PATHMUSTEXIST|OFN_NOCHANGEDIR; + if (GetSaveFileName(&of)) + { + cdsret.dwData = cds->dwData, cdsret.cbData = (lstrlen(buf) + 1) * sizeof(TCHAR), cdsret.lpData = buf; + SendMessage((HWND) wParam, WM_COPYDATA, (SIZE_T) hwndDlg, (SIZE_T) &cdsret); + } + return TRUE; + } + return FALSE; } return TRUE; } diff --git a/Contrib/Makensisw/makensisw.h b/Contrib/Makensisw/makensisw.h index 73e08ca7..13c27ebe 100644 --- a/Contrib/Makensisw/makensisw.h +++ b/Contrib/Makensisw/makensisw.h @@ -94,18 +94,26 @@ namespace MakensisAPI { extern const TCHAR* SigintEventNameFmt; extern const TCHAR* SigintEventNameLegacy; - enum notify_e { + enum datatransfer_e { NOTIFY_SCRIPT, NOTIFY_WARNING, NOTIFY_ERROR, - NOTIFY_OUTPUT + NOTIFY_OUTPUT, + PROMPT_FILEPATH }; enum sndmsg_e { QUERYHOST = WM_APP }; enum QUERYHOST_e { - QH_OUTPUTCHARSET = 1 + QH_OUTPUTCHARSET = 1, + QH_ENABLESTDERR, + QH_SUPPORTEDVERSION }; + typedef struct { + unsigned char Platform; + unsigned char Reserved; + TCHAR Path[1]; + } PROMPT_FILEPATH_DATA; } typedef enum { diff --git a/Contrib/Makensisw/utils.h b/Contrib/Makensisw/utils.h index ffff6609..a3604397 100644 --- a/Contrib/Makensisw/utils.h +++ b/Contrib/Makensisw/utils.h @@ -64,6 +64,7 @@ void LogMessage(HWND hwnd,const TCHAR *str); void ErrorMessage(HWND hwnd,const TCHAR *str); void CenterOnParent(HWND hwnd); void SetDialogFocus(HWND hDlg, HWND hCtl); // Use this and not SetFocus()! +#define DlgRet(hDlg, val) ( SetWindowLongPtr((hDlg), DWLP_MSGRESULT, (val)) | TRUE ) HWND GetComboEdit(HWND hCB); #define DisableItems(hwnd) EnableDisableItems(hwnd, 0) #define EnableItems(hwnd) EnableDisableItems(hwnd, 1) @@ -90,6 +91,18 @@ void BuildMRUMenus(); void LoadMRUFile(int position); void ClearMRUList(); +struct FSPath { + template static inline bool IsAgnosticSeparator(const T c) { return '\\' == c || '/' == c; } + template static T* FindLastComponent(T*p) // Note: Returns "" for "dir\" + { + for (T *sep = 0, *start = p;; ++p) + if (!*p) + return sep ? ++sep : start; + else if (IsAgnosticSeparator(*p)) + sep = p; + } +}; + bool FileExists(const TCHAR *fname); bool OpenUrlInDefaultBrowser(HWND hwnd, LPCSTR Url); diff --git a/Source/build.cpp b/Source/build.cpp index c004753e..ee4e9dc7 100644 --- a/Source/build.cpp +++ b/Source/build.cpp @@ -2646,8 +2646,8 @@ int CEXEBuild::write_output(void) RET_UNLESS_OK( uninstall_generate() ); - crc32_t crc=0; - + unsigned char limit = 0; // Limit the number of retries in case the host has some kind of bug +retry_output: { tstring full_path = get_full_path(build_output_filename), fnamebuf = get_file_name(build_output_filename); notify(MakensisAPI::NOTIFY_OUTPUT, full_path.c_str()); @@ -2667,6 +2667,7 @@ int CEXEBuild::write_output(void) if (!fp) { ERROR_MSG(_T("Can't open output file\n")); + if (++limit && prompt_for_output_path(build_output_filename, COUNTOF(CEXEBuild::build_output_filename))) goto retry_output; return PS_ERROR; } @@ -2677,6 +2678,7 @@ int CEXEBuild::write_output(void) return PS_ERROR; } + crc32_t crc=0; #ifdef NSIS_CONFIG_CRC_SUPPORT #ifdef NSIS_CONFIG_CRC_ANAL crc=CRC32(crc,m_exehead,(DWORD)m_exehead_size); @@ -3479,7 +3481,7 @@ void CEXEBuild::warninghelper(DIAGCODE dc, bool fl, const TCHAR *fmt, va_list ar m_warnings.add(msg,0); // Add to list of warnings to display at the end - MakensisAPI::notify_e hostevent = MakensisAPI::NOTIFY_WARNING; + MakensisAPI::datatransfer_e hostevent = MakensisAPI::NOTIFY_WARNING; if (aserror) hostevent = MakensisAPI::NOTIFY_ERROR, display_warnings = display_errors; @@ -3571,7 +3573,7 @@ void CEXEBuild::print_warnings() FlushOutputAndResetPrintColor(); } -void CEXEBuild::notify(MakensisAPI::notify_e code, const TCHAR *data) const +void CEXEBuild::notify(MakensisAPI::datatransfer_e code, const TCHAR *data) const { #ifdef _WIN32 if (notify_hwnd) @@ -3595,6 +3597,64 @@ void CEXEBuild::notify(MakensisAPI::notify_e code, const TCHAR *data) const #endif } +bool CEXEBuild::hostapi_request_data(MakensisAPI::datatransfer_e operation, UINT minver, HOSTAPIREQUESTDATAPROC proc, void*cookie, const void* input, UINT inputsize) const +{ + using namespace MakensisAPI; +#ifdef _WIN32 + struct helper { + static INT_PTR CALLBACK Proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) + { + size_t *data = (size_t*) GetWindowLongPtr(hWnd, DWLP_USER); + if (Msg == WM_CLOSE) + { + if (lParam) SendMessage((HWND) wParam, WM_COPYDATA, (SIZE_T) hWnd, lParam); + return DestroyWindow(hWnd) | PostMessage(NULL, WM_QUIT, 0, 0); + } + return data && ((HOSTAPIREQUESTDATAPROC)data[0])((void*) data[1], hWnd, Msg, wParam, lParam); // We don't set DWLP_MSGRESULT nor care about the return value + } + }; + if (!notify_hwnd || (minver && SendMessage(notify_hwnd, QUERYHOST, QH_SUPPORTEDVERSION, 0) < minver)) return false; + size_t data[] = { (size_t) proc, (size_t) cookie }; + COPYDATASTRUCT cds = { (DWORD) operation, inputsize, (void*) input }; + HWND hWnd = CreateWindowEx(WS_EX_TOOLWINDOW, WC_DIALOG, NULL, WS_POPUP|WS_DISABLED, 0, 0, 0, 0, NULL, NULL, NULL, NULL); + SetWindowLongPtr(hWnd, DWLP_USER, (LONG_PTR) data); + SetWindowLongPtr(hWnd, DWLP_DLGPROC, (LONG_PTR) helper::Proc); + SendMessage(hWnd, WM_CLOSE, (SIZE_T) notify_hwnd, (SIZE_T) &cds); + for (MSG msg; (int) GetMessage(&msg, NULL, 0, 0) > 0;) DispatchMessage(&msg); + return true; +#else + return false; +#endif +} + +bool CEXEBuild::prompt_for_output_path(TCHAR*path, UINT pathcap) const +{ + using namespace MakensisAPI; +#ifdef _WIN32 + struct handler { + static bool CALLBACK proc(void*cookie, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) + { + size_t *io = (size_t*) cookie; + COPYDATASTRUCT*pCDS = (COPYDATASTRUCT*) lParam; + if (Msg == WM_COPYDATA && pCDS->cbData > sizeof(TCHAR) && pCDS->cbData <= io[2] * sizeof(TCHAR)) + { + _tcscpy((TCHAR*) io[1], (TCHAR*) ((COPYDATASTRUCT*)lParam)->lpData); + return (io[0] = pCDS->dwData == PROMPT_FILEPATH); + } + return false; + } + }; + size_t io[] = { false, (size_t) path, pathcap }, cb; + TinyGrowBuf inputbuf((cb = FIELD_OFFSET(PROMPT_FILEPATH_DATA, Path[pathcap]))); + PROMPT_FILEPATH_DATA *p = (PROMPT_FILEPATH_DATA*) inputbuf.get(); + p->Platform = (sizeof(void*) * 8) | sizeof(TCHAR), p->Reserved = 0; + _tcscpy(p->Path, path); + return hostapi_request_data(PROMPT_FILEPATH, 0x03006000, handler::proc, io, p, cb) && io[0]; +#else + return false; +#endif +} + #ifdef NSIS_CONFIG_PLUGIN_SUPPORT int CEXEBuild::initialize_default_plugins(bool newtargetarc) { diff --git a/Source/build.h b/Source/build.h index 87cf64c1..d08a8f1c 100644 --- a/Source/build.h +++ b/Source/build.h @@ -143,11 +143,12 @@ namespace MakensisAPI { extern const TCHAR* SigintEventNameFmt; extern const TCHAR* SigintEventNameLegacy; - enum notify_e { - NOTIFY_SCRIPT, // main nsi file(s) + enum datatransfer_e { + NOTIFY_SCRIPT, // Compiler -> Host: main nsi file(s) NOTIFY_WARNING, NOTIFY_ERROR, - NOTIFY_OUTPUT // generated .exe file + NOTIFY_OUTPUT, // Compiler -> Host: Generated .exe file + PROMPT_FILEPATH // [0x03006000] Compiler -> Host -> Compiler }; #ifdef _WIN32 enum sndmsg_e { @@ -156,8 +157,14 @@ namespace MakensisAPI { #endif enum QUERYHOST_e { QH_OUTPUTCHARSET = 1, // [0x03000000] return (wincodepage+1) or 0 for default (This encoding is used by stdout, stderr and the notify messages) - QH_ENABLESTDERR // [0x03001000] return 1 to output error messages to stderr or 0 to output error messages to stdout + QH_ENABLESTDERR, // [0x03001000] return 1 to output error messages to stderr or 0 to output error messages to stdout + QH_SUPPORTEDVERSION, // [0x03006000] The host must return new highest makensis compiler version it supports }; + typedef struct { + unsigned char Platform; // Bitness OR'ed with sizeof(TCHAR) of the compiler + unsigned char Reserved; + TCHAR Path[1]; + } PROMPT_FILEPATH_DATA; } #define PAGE_CUSTOM 0 @@ -273,7 +280,14 @@ class CEXEBuild { NStreamLineReader* curlinereader; HWND notify_hwnd; - void notify(MakensisAPI::notify_e code, const TCHAR *data) const; + void notify(MakensisAPI::datatransfer_e code, const TCHAR *data) const; + #ifdef _WIN32 + typedef bool (CALLBACK*HOSTAPIREQUESTDATAPROC)(void*cookie, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); + #else + typedef bool (*HOSTAPIREQUESTDATAPROC)(void*cookie); + #endif + bool hostapi_request_data(MakensisAPI::datatransfer_e operation, UINT minver, HOSTAPIREQUESTDATAPROC proc, void*cookie, const void* input, UINT inputsize) const; + bool prompt_for_output_path(TCHAR*path, UINT pathcap) const; private: int check_write_output_errors() const; diff --git a/Source/growbuf.h b/Source/growbuf.h index 67b0dfd0..93c67708 100644 --- a/Source/growbuf.h +++ b/Source/growbuf.h @@ -122,6 +122,7 @@ class GrowBuf : public IGrowBuf class TinyGrowBuf : public GrowBuf { public: TinyGrowBuf() : GrowBuf() { m_bs=1024; } + TinyGrowBuf(size_type cb) : GrowBuf() { m_bs=1024; resize(cb); } }; #endif