Added !uninstfinalize (patch 280)

git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@7294 212acab6-be3b-0410-9dea-997c60f758d6
This commit is contained in:
anders_k 2021-08-18 13:53:50 +00:00
parent 1768555f2b
commit 41bb557cbc
11 changed files with 166 additions and 79 deletions

View file

@ -120,9 +120,17 @@ This option makes the compiler use an external EXE packer (such as \W{http://www
\c command [compare comparevalue]
This option will execute 'command' using a call to system() after the output EXE has been generated. You can typically use it to sign (Authenticode) your installer. If 'command' contains a '%1' it will be replaced by the executables filename.
This option will execute 'command' using a call to system() after the installer EXE has been generated. You can typically use it to sign (Authenticode) your installer. If 'command' contains a '%1' it will be replaced by the executables filename.
\c !finalize 'sign.bat "%1" "Product Installer" http://example.com'
\c !finalize 'sign.bat "%1" "MyProduct Installer" http://example.com'
\S1{uninstfinalize} !uninstfinalize
\c command [compare comparevalue]
This option will execute 'command' using a call to system() after the uninstaller EXE has been generated. You can typically use it to sign (Authenticode) your uninstaller. If 'command' contains a '%1' it will be replaced by the executables filename.
\c !uninstfinalize 'sign.bat "%1" "MyProduct Installer" http://example.com'
\S1{system} !system

View file

@ -10,6 +10,10 @@ Released on ???? ??th, 20??
\S1{v3.08-cl} Changelog
\S2{} Major Changes
\b Added \R{uninstfinalize}{!uninstfinalize} (\W{http://sf.net/p/nsis/patches/280}{patch #280}, \W{http://sf.net/p/nsis/bugs/1241}{bug #1241})
\S2{} Minor Changes
\b Disallow start maximized mode

View file

@ -100,8 +100,8 @@ CEXEBuild::~CEXEBuild()
for (int i = 0; i < nlt; i++)
DeleteLangTable(nla+i);
if (postbuild_cmds)
postbuild_cmds->delete_all();
if (postbuild_cmds) postbuild_cmds->delete_all();
if (postubuild_cmds) postubuild_cmds->delete_all();
}
CEXEBuild::CEXEBuild(signed char pponly, bool warnaserror) :
@ -191,7 +191,7 @@ CEXEBuild::CEXEBuild(signed char pponly, bool warnaserror) :
build_cursection=NULL;
// init public data.
build_packname[0]=build_packcmd[0]=build_output_filename[0]=0;
postbuild_cmds=NULL;
postbuild_cmds=postubuild_cmds=NULL;
// Added by ramon 23 May 2003
build_allowskipfiles=1;
@ -1731,21 +1731,21 @@ int CEXEBuild::add_page(int type)
#ifndef NSIS_CONFIG_LICENSEPAGE
if (type == PAGE_LICENSE)
{
ERROR_MSG(_T("Error: can't add license page, NSIS_CONFIG_LICENSEPAGE not defined.\n"));
ERROR_MSG(_T("Error: can't add %") NPRIns _T(" page, %") NPRIns _T(" not defined.\n"), "license", "NSIS_CONFIG_LICENSEPAGE");
return PS_ERROR;
}
#endif
#ifndef NSIS_CONFIG_COMPONENTPAGE
if (type == PAGE_COMPONENTS)
{
ERROR_MSG(_T("Error: can't add components page, NSIS_CONFIG_COMPONENTPAGE not defined.\n"));
ERROR_MSG(_T("Error: can't add %") NPRIns _T(" page, %") NPRIns _T(" not defined.\n"), "components", "NSIS_CONFIG_COMPONENTPAGE");
return PS_ERROR;
}
#endif
#ifndef NSIS_CONFIG_UNINSTALL_SUPPORT
if (type == PAGE_COMPONENTS)
{
ERROR_MSG(_T("Error: can't add uninstConfirm page, NSIS_CONFIG_UNINSTALL_SUPPORT not defined.\n"));
ERROR_MSG(_T("Error: can't add %") NPRIns _T(" page, %") NPRIns _T(" not defined.\n"), "uninstConfirm", "NSIS_CONFIG_UNINSTALL_SUPPORT");
return PS_ERROR;
}
#endif
@ -2539,14 +2539,11 @@ int CEXEBuild::pack_exe_header()
// write out exe header, pack, read back in, and
// update the header info
FILE *tmpfile=FOPEN(build_packname,("wb"));
if (!tmpfile)
if (m_exehead_size != write_octets_to_file(build_packname, m_exehead, m_exehead_size))
{
ERROR_MSG(_T("Error: writing temporary file \"%") NPRIs _T("\" for pack\n"),build_packname);
return PS_ERROR;
}
fwrite(m_exehead,1,m_exehead_size,tmpfile);
fclose(tmpfile);
int ec = sane_system(build_packcmd);
if (ec == -1)
{
@ -3064,23 +3061,33 @@ int CEXEBuild::uninstall_generate()
{
SCRIPT_MSG(_T("Generating uninstaller... "));
const int start_offset = postubuild_cmds ? truncate_cast(int, m_exehead_size) : 0;
entry *ent = (entry *) build_entries.get();
if (!ent) return PS_ERROR; // Check this early
MMapBuf udata;
firstheader fh={0,};
GrowBuf uhd;
{
GrowBuf udata;
GrowBuf udata_exehead;
set_uninstall_mode(1);
PrepareHeaders(&udata);
PrepareHeaders(&udata_exehead);
fh.length_of_header=udata.getlen();
int err=add_data((char*)udata.get(),udata.getlen(),&uhd);
fh.length_of_header=udata_exehead.getlen();
int err=add_data((char*)udata_exehead.get(),udata_exehead.getlen(),&uhd);
set_uninstall_mode(0);
if (err < 0) return PS_ERROR;
}
crc32_t crc=0;
#ifdef NSIS_CONFIG_CRC_SUPPORT
bool calc_crc = true;
#else
bool calc_crc = false;
#endif
// Get offsets of icons to replace for uninstall
// Also makes sure that the icons are there and in the right size.
@ -3091,41 +3098,34 @@ int CEXEBuild::uninstall_generate()
return PS_ERROR;
}
entry *ent = (entry *) build_entries.get();
if (!ent)
{
delete [] unicon_data;
return PS_ERROR;
}
int ents = build_header.blocks[NB_ENTRIES].num;
int uns = uninstaller_writes_used;
int uninstdata_offset = build_datablock.getlen();
while (ents--)
for (int ents = build_header.blocks[NB_ENTRIES].num; ents--; ent++)
{
if (ent->which == EW_WRITEUNINSTALLER)
{
ent->offsets[1] = uninstdata_offset;
ent->offsets[2] = (int) m_unicon_size;
uns--;
if (!uns)
ent->offsets[2] = start_offset ? 0 : (int) m_unicon_size;
if (!--uns)
break;
}
ent++;
}
if (add_db_data((char *)unicon_data,m_unicon_size) < 0)
if (!start_offset && add_db_data((char *)unicon_data,m_unicon_size) < 0)
{
delete [] unicon_data;
return PS_ERROR;
}
#ifdef NSIS_CONFIG_CRC_SUPPORT
if (start_offset || calc_crc)
{
// "create" the uninstaller
// create the uninstaller
LPBYTE uninst_header = (LPBYTE) malloc(m_exehead_size);
if (!uninst_header)
{
delete [] unicon_data;
return PS_ERROR;
}
memcpy(uninst_header, m_exehead, m_exehead_size);
@ -3140,17 +3140,25 @@ int CEXEBuild::uninstall_generate()
seeker += dwSize;
}
delete [] unicon_data;
if (calc_crc)
{
#ifdef NSIS_CONFIG_CRC_SUPPORT
#ifdef NSIS_CONFIG_CRC_ANAL
crc=CRC32(crc, uninst_header, (DWORD)m_exehead_size);
crc=CRC32(crc, uninst_header, (DWORD)m_exehead_size);
#else
crc=CRC32(crc, uninst_header + 512, (DWORD)m_exehead_size - 512);
crc=CRC32(crc, uninst_header + 512, (DWORD)m_exehead_size - 512);
#endif
#endif
}
// write the exehead
if (start_offset)
udata.add((char *)uninst_header, truncate_cast(int, m_exehead_size));
free(uninst_header);
}
#endif
delete [] unicon_data;
fh.nsinst[0]=FH_INT1;
fh.nsinst[1]=FH_INT2;
fh.nsinst[2]=FH_INT3;
@ -3165,8 +3173,6 @@ int CEXEBuild::uninstall_generate()
fh.length_of_all_following_data=
uhd.getlen()+ubuild_datablock.getlen()+(int)sizeof(firstheader)+(build_crcchk?sizeof(crc32_t):0);
MMapBuf udata;
{
growbuf_writer_sink sink(&udata, mk_writer_target_info());
firstheader_writer w(&sink);
@ -3231,8 +3237,8 @@ int CEXEBuild::uninstall_generate()
compressor->End();
}
firstheader *_fh=(firstheader *)udata.get(0, sizeof(firstheader));
_fh->length_of_all_following_data=FIX_ENDIAN_INT32(udata.getlen()+(build_crcchk?sizeof(crc32_t):0));
firstheader *ufh=(firstheader *)udata.get(start_offset, sizeof(firstheader));
ufh->length_of_all_following_data=FIX_ENDIAN_INT32((udata.getlen()-start_offset)+(build_crcchk?sizeof(crc32_t):0));
udata.release();
}
else
@ -3264,11 +3270,11 @@ int CEXEBuild::uninstall_generate()
if (build_crcchk)
{
int pos = 0;
int left = udata.getlen();
int left = udata.getlen() - start_offset;
while (left > 0)
{
int l = min(build_filebuflen, left);
crc = CRC32(crc, (unsigned char *) udata.get(pos, l), l);
crc = CRC32(crc, (unsigned char *) udata.get(pos + start_offset, l), l);
udata.release();
pos += l;
left -= l;
@ -3280,14 +3286,57 @@ int CEXEBuild::uninstall_generate()
}
#endif
if (add_db_data(&udata) < 0)
return PS_ERROR;
if (start_offset)
{
TCHAR* fpath;
unsigned long in_len;
if (!(fpath = create_tempfile_path()))
{
ERROR_MSG(_T("Error: can't get temporary path\n"));
return PS_ERROR;
}
size_t ret_size = write_octets_to_file(fpath, (unsigned char*)udata.get(0, udata.getlen()), udata.getlen());
udata.release();
if ((size_t)udata.getlen() != ret_size)
{
ERROR_MSG(_T("Error: can't write %d bytes to output\n"), udata.getlen());
free(fpath);
return PS_ERROR;
}
if (PS_OK != run_postbuild_cmds(postubuild_cmds, fpath, _T("UninstFinalize")))
{
free(fpath);
return PS_ERROR;
}
BYTE* in_buf = alloc_and_read_file(fpath, in_len);
_tremove(fpath);
free(fpath);
if (!in_buf)
{
ERROR_MSG(_T("Error: can't read %d bytes from input\n"), in_len);
return PS_ERROR;
}
MMapBuf udata_in;
udata_in.add(in_buf, in_len);
free(in_buf);
if (add_db_data(&udata_in) < 0)
return PS_ERROR;
uninstall_size_full = udata_in.getlen();
udata_in.clear();
}
else
{
if (add_db_data(&udata) < 0)
return PS_ERROR;
//uninstall_size_full=fh.length_of_all_following_data + sizeof(int) + unicondata_size - 32 + sizeof(int);
uninstall_size_full = fh.length_of_all_following_data+(int)m_unicon_size;
}
udata.clear();
//uninstall_size_full=fh.length_of_all_following_data + sizeof(int) + unicondata_size - 32 + sizeof(int);
uninstall_size_full=fh.length_of_all_following_data+(int)m_unicon_size;
// compressed size
uninstall_size=build_datablock.getlen()-uninstdata_offset;

View file

@ -390,7 +390,7 @@ class CEXEBuild {
int pp_define(LineParser&line);
int pp_undef(LineParser&line);
int pp_packhdr(LineParser&line);
int pp_finalize(LineParser&line);
int pp_finalize(int which_token, LineParser&line);
int pp_execute(int which_token, LineParser&line);
int pp_addincludedir(LineParser&line);
int pp_include(LineParser&line);
@ -606,7 +606,7 @@ class CEXEBuild {
TCHAR cmd[1];
void delete_all();
static postbuild_cmd* make(const TCHAR *cmdstr, int cmpop, int cmpval);
} *postbuild_cmds;
} *postbuild_cmds, *postubuild_cmds;
int run_postbuild_cmds(const postbuild_cmd *cmds, const TCHAR *templatearg_pc1, const TCHAR* commandname);
int check_external_exitcode(int exitcode, int op, int val);

View file

@ -1563,36 +1563,41 @@ static int NSISCALL ExecuteEntry(entry *entry_)
hFile=myOpenFile(buf1,GENERIC_WRITE,CREATE_ALWAYS);
if (hFile != INVALID_HANDLE_VALUE)
{
unsigned char *filebuf;
int filehdrsize = g_filehdrsize;
filebuf=(unsigned char *)GlobalAlloc(GPTR,filehdrsize);
if (filebuf)
int dboffset = parm1;
if (parm2)
{
SetSelfFilePointer(0);
ReadSelfFile((char*)filebuf,filehdrsize);
unsigned char *filebuf;
int filehdrsize = g_filehdrsize;
filebuf=(unsigned char *)GlobalAlloc(GPTR,filehdrsize);
if (filebuf)
{
// parm1 = uninstdata_offset
// parm2 = m_unicon_size
unsigned char* seeker;
unsigned char* unicon_data = seeker = (unsigned char*)GlobalAlloc(GPTR,parm2);
if (unicon_data) {
GetCompressedDataFromDataBlockToMemory(parm1,unicon_data,parm2);
while (*seeker) {
struct icondata {
DWORD dwSize;
DWORD dwOffset;
} id = *(struct icondata *) seeker;
seeker += sizeof(struct icondata);
mini_memcpy(filebuf+id.dwOffset, seeker, id.dwSize);
seeker += id.dwSize;
SetSelfFilePointer(0);
ReadSelfFile((char*)filebuf,filehdrsize);
{
// parm1 = uninstdata_offset
// parm2 = m_unicon_size
unsigned char* seeker;
unsigned char* unicon_data = seeker = (unsigned char*)GlobalAlloc(GPTR,parm2);
if (unicon_data) {
GetCompressedDataFromDataBlockToMemory(parm1,unicon_data,parm2);
while (*seeker) {
struct icondata {
DWORD dwSize;
DWORD dwOffset;
} id = *(struct icondata *) seeker;
seeker += sizeof(struct icondata);
mini_memcpy(filebuf+id.dwOffset, seeker, id.dwSize);
seeker += id.dwSize;
}
GlobalFree(unicon_data);
}
GlobalFree(unicon_data);
}
myWriteFile(hFile,(char*)filebuf,filehdrsize);
GlobalFree(filebuf);
dboffset = -1;
}
myWriteFile(hFile,(char*)filebuf,filehdrsize);
GlobalFree(filebuf);
ret=GetCompressedDataFromDataBlock(-1,hFile);
}
ret=GetCompressedDataFromDataBlock(dboffset, hFile);
CloseHandle(hFile);
}
log_printf3(_T("created uninstaller: %d, \"%s\""),ret,buf1);

View file

@ -530,7 +530,7 @@ parse_again:
extern FILE *g_output;
bool pptok = is_pp_token(tkid), docmd = pptok;
bool both = TOK_P_VERBOSE == tkid || TOK_P_WARNING == tkid || TOK_P_ECHO == tkid;
if (TOK_P_FINALIZE == tkid || TOK_P_PACKEXEHEADER == tkid) docmd = false;
if (TOK_P_FINALIZE == tkid || TOK_P_UNINSTFINALIZE == tkid || TOK_P_PACKEXEHEADER == tkid) docmd = false;
if (docmd && is_unsafe_pp_token(tkid) && preprocessonly > 0) docmd = false;
if (!docmd || both) _ftprintf(g_output,(_T("%") NPRIs _T("\n")),ppoline.get());
if (!docmd && !both) return PS_OK;
@ -2553,7 +2553,8 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
case TOK_P_PACKEXEHEADER:
return pp_packhdr(line);
case TOK_P_FINALIZE:
return pp_finalize(line);
case TOK_P_UNINSTFINALIZE:
return pp_finalize(which_token, line);
case TOK_P_SYSTEMEXEC:
case TOK_P_EXECUTE:
case TOK_P_MAKENSIS:

View file

@ -1058,8 +1058,15 @@ template<class T> void slist_append(T&list, T&item)
(prev ? prev->next : list) = item;
}
int CEXEBuild::pp_finalize(LineParser&line)
int CEXEBuild::pp_finalize(int which_token, LineParser&line)
{
#ifndef NSIS_CONFIG_UNINSTALL_SUPPORT
if (which_token == TOK_P_UNINSTFINALIZE)
{
ERROR_MSG(_T("Error: %") NPRIs _T(" specified, %") NPRIns _T(" not defined.\n"), line.gettoken_str(0), "NSIS_CONFIG_UNINSTALL_SUPPORT");
return PS_ERROR;
}
#endif
TCHAR* cmdstr = line.gettoken_str(1);
int validparams = false;
postbuild_cmd *newcmd = postbuild_cmd::make(cmdstr, line.gettoken_enum(2, _T("<\0>\0<>\0=\0ignore\0")), line.gettoken_int(3, &validparams));
@ -1067,8 +1074,8 @@ int CEXEBuild::pp_finalize(LineParser&line)
newcmd->cmpop = 4, validparams = true; // Just a command, ignore the exit code
if (newcmd->cmpop == -1 || !validparams)
PRINTHELP();
slist_append(postbuild_cmds, newcmd);
SCRIPT_MSG(_T("!finalize: \"%") NPRIs _T("\"\n"), cmdstr);
slist_append(which_token == TOK_P_UNINSTFINALIZE ? postubuild_cmds : postbuild_cmds, newcmd);
SCRIPT_MSG(_T("!%") NPRIns _T("finalize: \"%") NPRIs _T("\"\n"), which_token == TOK_P_UNINSTFINALIZE ? "uninst" : "", cmdstr);
return PS_OK;
}

View file

@ -273,6 +273,7 @@ static tokenType tokenlist[TOK__LAST] =
{TOK_MANIFEST_GDISCALING,_T("ManifestGdiScaling"),1,0,_T("notset|true"),TP_GLOBAL},
{TOK_P_PACKEXEHEADER,_T("!packhdr"),2,0,_T("temp_file_name command_line_to_compress_that_temp_file"),TP_ALL},
{TOK_P_FINALIZE,_T("!finalize"),1,2,_T("command_with_%1 [<OP retval>]"),TP_ALL},
{TOK_P_UNINSTFINALIZE,_T("!uninstfinalize"),1,2,_T("command_with_%1 [<OP retval>]"),TP_ALL},
{TOK_P_SYSTEMEXEC,_T("!system"),1,2,_T("command [<OP retval> | <retvalsymbol>]\n OP=(< > <> =)"),TP_ALL},
{TOK_P_EXECUTE,_T("!execute"),1,2,_T("command [<OP retval> | <retvalsymbol>]\n OP=(< > <> =)"),TP_ALL},
{TOK_P_MAKENSIS,_T("!makensis"),1,2,_T("parameters [<OP retval> | <retvalsymbol>]"),TP_ALL},

View file

@ -117,6 +117,7 @@ enum
TOK_P_UNDEF,
TOK_P_PACKEXEHEADER,
TOK_P_FINALIZE,
TOK_P_UNINSTFINALIZE,
TOK_P_SYSTEMEXEC,
TOK_P_EXECUTE,
TOK_P_MAKENSIS,

View file

@ -714,6 +714,15 @@ char* create_file_view_readonly(const TCHAR *filepath, FILEVIEW&mmfv)
#endif
}
size_t write_octets_to_file(const TCHAR *filename, const void *data, size_t cb)
{
FILE *hfile = FOPEN(filename, ("wb"));
if (!hfile) return 0;
size_t ret = fwrite(data, 1, cb, hfile);
fclose(hfile);
return ret;
}
TCHAR* create_tempfile_path()
{
TCHAR *tfpath = NULL;

View file

@ -287,6 +287,8 @@ typedef struct { char*base; size_t internal; } FILEVIEW;
void close_file_view(FILEVIEW&mmfv);
char* create_file_view_readonly(const TCHAR *filepath, FILEVIEW&mmfv);
size_t write_octets_to_file(const TCHAR *filename, const void *data, size_t cb);
// round a value up to be a multiple of 512
// assumption: T is an int type
template <class T> inline T align_to_512(const T x) { return (x+511) & ~511; }