diff --git a/Contrib/nsDialogs/nsDialogs.nsh b/Contrib/nsDialogs/nsDialogs.nsh index 468f8f1c..17e8efa9 100644 --- a/Contrib/nsDialogs/nsDialogs.nsh +++ b/Contrib/nsDialogs/nsDialogs.nsh @@ -947,25 +947,11 @@ Exch ### Static ### !macro __NSD_LoadAndSetImage _LIHINSTMODE _IMGTYPE _LIHINSTSRC _LIFLAGS CONTROL IMAGE HANDLE - Push $0 - Push $R0 - - Push "${IMAGE}" # in case ${IMAGE} is $R0 - StrCpy $R0 ${CONTROL} # in case ${CONTROL} is $0 - !if "${_LIHINSTMODE}" == "exeresource" - !undef _LIHINSTSRC # If (internal?) _* macro params starts using $0, - !define _LIHINSTSRC r0 # _LIHINSTSRC can be changed to s - System::Call 'kernel32::GetModuleHandle(p0)p.${_LIHINSTSRC}' + LoadAndSetImage /EXERESOURCE /STRINGID "${CONTROL}" ${_IMGTYPE} ${_LIFLAGS} "${IMAGE}" ${HANDLE} + !else #if "${_LIHINSTMODE}" == "file" + LoadAndSetImage /STRINGID "${CONTROL}" ${_IMGTYPE} ${_LIFLAGS} "${IMAGE}" ${HANDLE} !endif - - System::Call 'user32::LoadImage(p ${_LIHINSTSRC}, ts, i ${_IMGTYPE}, i0, i0, i${_LIFLAGS})p.r0' - SendMessage $R0 ${STM_SETIMAGE} ${_IMGTYPE} $0 - - Pop $R0 - Exch $0 - - Pop ${HANDLE} !macroend !macro __NSD_SetIconFromExeResource CONTROL IMAGE HANDLE @@ -987,23 +973,7 @@ Exch !define NSD_SetStretchedImage `!insertmacro __NSD_SetStretchedImage ` !define NSD_SetStretchedBitmap `!insertmacro __NSD_SetStretchedImage ` !macro __NSD_SetStretchedImage CONTROL IMAGE HANDLE - Push $0 - Push $R0 - - Push "${IMAGE}" # in case ${IMAGE} is $0 or $R0 - StrCpy $R0 ${CONTROL} # in case ${CONTROL} is $0 - - System::Call 'user32::GetClientRect(pR0,@r0)' - System::Call '*$0(i,i,i.r0,i.s)' - Exch # swap so stack contains ImagePath and then ControlHeight - - System::Call 'user32::LoadImage(p0, ts, i${IMAGE_BITMAP}, ir0, is, i${LR_LOADFROMFILE}) p.r0' - SendMessage $R0 ${STM_SETIMAGE} ${IMAGE_BITMAP} $0 - - Pop $R0 - Exch $0 - - Pop ${HANDLE} + LoadAndSetImage /STRINGID /RESIZETOFIT "${CONTROL}" ${IMAGE_BITMAP} ${LR_LOADFROMFILE} "${IMAGE}" ${HANDLE} !macroend diff --git a/Source/Tests/preprocessor.nsi b/Source/Tests/preprocessor.nsi index 01abe8f4..4836d23e 100644 --- a/Source/Tests/preprocessor.nsi +++ b/Source/Tests/preprocessor.nsi @@ -189,6 +189,11 @@ ${ASSERT} '${OUT1} = 0xC0000000' !define /redef /intfmt OUT1 "0x%.3X" 42 ${ASSERT} '${OUT1} = 0x02A' +!pragma internal x OUT "0x10 | 0x40" ; NSD requires this when using LoadAndSetImage +!if "${OUT}" <> 80 + !error "math expression failed" +!endif + ; end math functions diff --git a/Source/build.cpp b/Source/build.cpp index ee4e9dc7..a34210d2 100644 --- a/Source/build.cpp +++ b/Source/build.cpp @@ -3392,6 +3392,17 @@ int CEXEBuild::parse_pragma(LineParser &line) const int rvSucc = PS_OK, rvWarn = PS_WARNING, rvErr = PS_WARNING; // rvErr is not PS_ERROR because we want !pragma parsing to be very forgiving. const TCHAR badParamMsg[] = _T("Unknown pragma"); + if (line.gettoken_enum(1, _T("internal\0")) == 0) + { + if (line.gettoken_enum(2, _T("x\0")) == 0) + { + const TCHAR *name = line.gettoken_str(3); + int succ, num = line.gettoken_intx(4, &succ);SCRIPT_MSG(_T("%#x %d\n"),num,succ); + return ((succ ? definedlist.set_si32(name, num) : definedlist.set(name, _T(""))), rvSucc); + } + return rvErr; + } + // 2.47 shipped with a corrupted CHM file (bug #1129). This minimal verification command exists because the !searchparse hack we added does not work with codepage 936! if (line.gettoken_enum(1, _T("verifychm\0")) == 0) { @@ -3847,16 +3858,16 @@ int CEXEBuild::DeclaredUserVar(const TCHAR *szVarName) int CEXEBuild::GetUnsafeUserVarIndex(LineParser &line, int token) { TCHAR *p = line.gettoken_str(token); - int idx = (*p == _T('$') && *++p) ? m_UserVarNames.get(p) : -1; + int idx = IsVarPrefix(p) ? m_UserVarNames.get(++p) : -1; if (idx >= 0 && m_UserVarNames.get_reference(idx) >= 0) m_UserVarNames.inc_reference(idx); return idx; } int CEXEBuild::GetUserVarIndex(LineParser &line, int token) { TCHAR *p = line.gettoken_str(token); - if ( *p == _T('$') && *(p+1) ) + if (IsVarPrefix(p)) { - int idxUserVar = m_UserVarNames.get((TCHAR *)p+1); + int idxUserVar = m_UserVarNames.get(p+1); if (idxUserVar >= 0 && m_UserVarNames.get_reference(idxUserVar) >= 0) { m_UserVarNames.inc_reference(idxUserVar); @@ -3864,7 +3875,7 @@ int CEXEBuild::GetUserVarIndex(LineParser &line, int token) } else { - int idxConst = m_ShellConstants.get((TCHAR *)p+1); + int idxConst = m_ShellConstants.get(p+1); if (idxConst >= 0) { ERROR_MSG(_T("Error: cannot change constants : %") NPRIs _T("\n"), p); @@ -3888,7 +3899,7 @@ void CEXEBuild::VerifyDeclaredUserVarRefs(UserVarsStringList *pVarsStringList) bool CEXEBuild::IsIntOrUserVar(const LineParser &line, int token) const { const TCHAR *p = line.gettoken_str(token); - if ( *p == _T('$') && *(p+1) ) + if (IsVarPrefix(p)) { int idxUserVar = m_UserVarNames.get(p+1); return (idxUserVar >= 0 && m_UserVarNames.get_reference(idxUserVar) >= 0); diff --git a/Source/build.h b/Source/build.h index ff86071b..54dba2f2 100644 --- a/Source/build.h +++ b/Source/build.h @@ -568,6 +568,7 @@ class CEXEBuild { int DeclaredUserVar(const TCHAR *VarName); void VerifyDeclaredUserVarRefs(UserVarsStringList *pVarsStringList); bool IsIntOrUserVar(const LineParser &line, int token) const; + static bool IsVarPrefix(const TCHAR*s) { return *s++ == _T('$') && *s > ' '; } ConstantsStringList m_ShellConstants; diff --git a/Source/lineparse.cpp b/Source/lineparse.cpp index 203dbc8c..95eeecd8 100644 --- a/Source/lineparse.cpp +++ b/Source/lineparse.cpp @@ -22,6 +22,13 @@ #include "tstring.h" #include "util.h" +#ifndef _istspace +#define _istspace my_istspace +template static int my_istspace(T c) { return c == 0x20 || (c >= 0x09 && c <= 0x0D); } +#endif +template static T skipspace(T s) { while(*s && _istspace(*s)) ++s; return s; } + + LineParser::LineParser(bool bCommentBlock) { m_incommentblock=bCommentBlock; @@ -75,58 +82,130 @@ void LineParser::eattoken() m_eat++; } -static unsigned int number_has_base_prefix(const TCHAR *str) +inline int LineParser::validate_token_index(int token, int *success/*=0*/) const { - unsigned int hasbase = false; + token += m_eat; + if (token < 0 || token >= m_nt || !m_tokens[token][0]) + { + if (success) *success = 0; + return -1; + } + return token; +} + + +static unsigned int get_base_from_prefix(const TCHAR *str) +{ + unsigned int hasbase = 0; if (_T('-') == *str || _T('+') == *str) ++str; if (_T('0') == str[0]) { - if (_T('x') == (str[1]|32)) ++hasbase; - if (_T('n') == (str[1]|32)) ++hasbase; - if (_T('b') == (str[1]|32) || _T('y') == (str[1]|32)) ++hasbase; - if (_T('o') == (str[1]|32) || _T('t') == (str[1]|32)) ++hasbase; + if (_T('x') == (str[1]|32)) hasbase = 16; + // Special support for 0n, 0y and 0t MASM style and 0b and 0o Python style radix prefix: + if (_T('n') == (str[1]|32)) hasbase = 10; + if (_T('b') == (str[1]|32) || _T('y') == (str[1]|32)) hasbase = 2; + if (_T('o') == (str[1]|32) || _T('t') == (str[1]|32)) hasbase = 8; } return hasbase; } +static unsigned int number_has_base_prefix(const TCHAR *str) { return get_base_from_prefix(str); } -int LineParser::parse_int(const TCHAR *str, int *success/*=0*/) +static int parse_int_expression(const TCHAR *str, bool expressions, int *success=0) { - const TCHAR *p=str, *parse=p; + enum { XOP_NONE = 0, XOP_BITAND, XOP_BITXOR, XOP_BITOR, XOP_LOGAND, XOP_LOGOR }; + int tot=0, xop=XOP_NONE, hasfailed=0; +nextnum: + const TCHAR *p=str, *parse=p, *xoperandpfixstart, *xoperandpfixend; TCHAR *end; int neg=0, base=0, num; + + if (expressions) + { + xoperandpfixstart=p=skipspace(p); + while (*p == '~' || *p == '!') p=skipspace(++p); + xoperandpfixend=parse=p; + } + if (_T('+') == *p) ++p; else if (_T('-') == *p) ++p, ++neg; if (_T('0') == p[0]) { - // Special support for 0n, 0y and 0t MASM style and 0b and 0o Python style radix prefix: - if (_T('n') == (p[1]|32)) parse=&p[2], base=10; - if (_T('b') == (p[1]|32) || _T('y') == (p[1]|32)) parse=&p[2], base=2; - if (_T('o') == (p[1]|32) || _T('t') == (p[1]|32)) parse=&p[2], base=8; + if ((base=get_base_from_prefix(p))) parse=&p[2]; } if (neg) { - num=_tcstol(parse,&end,base); + num=(int)(long)_tcstol(parse,&end,base); if (base) num*=-1; // Input was "-0n012" but we have only parsed "012" and need to fix the sign } else { - num=(int)_tcstoul(parse,&end,base); + num=(int)(long)_tcstoul(parse,&end,base); } - if (success) *success=! (int)(*end); - return num; + const bool parseddigits=end > parse; + if (!parseddigits) ++hasfailed; // "0x", "1 | ~" etc. + + if (expressions) + { + for (p=xoperandpfixstart; p < xoperandpfixend; p=skipspace(++p)) + switch(*p) + { + case '~': num=~num; break; + case '!': num=!num; break; + default: assert(!"op"); + } + + // Simple left-to-right parsing, no operator precedence + switch(xop) + { + case XOP_NONE: tot=num; break; + case XOP_BITAND: tot&=num; break; + case XOP_BITXOR: tot^=num; break; + case XOP_BITOR: tot|=num; break; + case XOP_LOGAND: tot=tot && num; break; + case XOP_LOGOR: tot=tot || num; break; + default: assert(!"op"); + } + + str=end=skipspace(end); + switch(*str) + { + case '&': xop=XOP_BITAND; if (*++str == '&') xop=XOP_LOGAND, ++str; goto nextnum; + case '^': xop=XOP_BITXOR, ++str; goto nextnum; + case '|': xop=XOP_BITOR; if (*++str == '|') xop=XOP_LOGOR, ++str; goto nextnum; + default: xop=XOP_NONE; // Done + } + } + else + { + tot=num; + } + + if (success) *success=!(int)(*end) && !hasfailed; + return tot; +} + +int LineParser::parse_int(const TCHAR *str, int *success/*=0*/) +{ + return parse_int_expression(str, false, success); +} + +int LineParser::parse_intx(const TCHAR *str, int *success/*=0*/) +{ + return parse_int_expression(str, true, success); } int LineParser::gettoken_int(int token, int *success/*=0*/) const { - token+=m_eat; - if (token < 0 || token >= m_nt || !m_tokens[token][0]) - { - if (success) *success=0; - return 0; - } - return parse_int(m_tokens[token], success); + if ((token = validate_token_index(token,success)) < 0) return 0; + return parse_int(m_tokens[token],success); +} + +int LineParser::gettoken_intx(int token, int *success/*=0*/) const +{ + if ((token = validate_token_index(token,success)) < 0) return 0; + return parse_intx(m_tokens[token],success); } double LineParser::parse_float(const TCHAR *str, int *success/*=0*/) @@ -149,26 +228,20 @@ double LineParser::parse_float(const TCHAR *str, int *success/*=0*/) double LineParser::gettoken_float(int token, int *success/*=0*/) const { - token+=m_eat; - if (token < 0 || token >= m_nt) - { - if (success) *success=0; - return 0.0; - } - return parse_float(m_tokens[token], success); + if ((token = validate_token_index(token,success)) < 0) return 0.0; + return parse_float(m_tokens[token],success); } double LineParser::parse_number(const TCHAR *str, int *success/*=0*/) { - const unsigned int forceint = number_has_base_prefix(str); + const unsigned int forceint=number_has_base_prefix(str); return forceint ? parse_int(str,success) : parse_float(str,success); } double LineParser::gettoken_number(int token, int *success/*=0*/) const { - const TCHAR*str=gettoken_str(token); - const unsigned int forceint = number_has_base_prefix(str); - return forceint ? gettoken_int(token,success) : gettoken_float(token,success); + if ((token = validate_token_index(token,success)) < 0) return 0.0; + return parse_number(m_tokens[token],success); } int LineParser::gettoken_binstrdata(int token, char*buffer, int bufcap) const diff --git a/Source/lineparse.h b/Source/lineparse.h index 90960946..565db7e5 100644 --- a/Source/lineparse.h +++ b/Source/lineparse.h @@ -33,6 +33,7 @@ class LineParser { int getnumtokens(); void eattoken(); int gettoken_int(int token, int *success=0) const; + int gettoken_intx(int token, int *success=0) const; // basic operator expressions parser double gettoken_float(int token, int *success=0) const; double gettoken_number(int token, int *success=0) const; int gettoken_binstrdata(int token, char*buffer, int bufcap) const; @@ -40,6 +41,7 @@ class LineParser { int gettoken_enum(int token, const TCHAR *strlist); // null separated list static int parse_int(const TCHAR *str, int *success=0); + static int parse_intx(const TCHAR *str, int *success=0); static double parse_float(const TCHAR *str, int *success=0); static double parse_number(const TCHAR *str, int *success=0); @@ -47,6 +49,7 @@ class LineParser { void freetokens(); int doline(TCHAR *line, int ignore_escaping=0); + inline int validate_token_index(int token, int *success=0) const; int m_eat; int m_nt; diff --git a/Source/script.cpp b/Source/script.cpp index dafdcb37..8df98a94 100644 --- a/Source/script.cpp +++ b/Source/script.cpp @@ -4774,7 +4774,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) ent.offsets[2]=SECTION_FIELD_SET(name_ptr); ent.offsets[4]=add_string(line.gettoken_str(2)); if (!IsIntOrUserVar(line,1)) PRINTHELP() - SCRIPT_MSG(_T("SectionSetText: %") NPRIs _T("->%") NPRIs _T("\n"),line.gettoken_str(1),line.gettoken_str(2)); + SCRIPT_MSG(_T("SectionSetText: %") NPRIs _T("=%") NPRIs _T("\n"),line.gettoken_str(1),line.gettoken_str(2)); return add_entry(&ent); case TOK_SECTIONGETTEXT: ent.which=EW_SECTIONSET; @@ -4785,14 +4785,17 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) SCRIPT_MSG(_T("SectionGetText: %") NPRIs _T("->%") NPRIs _T("\n"),line.gettoken_str(1),line.gettoken_str(2)); return add_entry(&ent); case TOK_SECTIONSETFLAGS: + { + int num, conv; ent.which=EW_SECTIONSET; ent.offsets[0]=add_string(line.gettoken_str(1)); - ent.offsets[1]=add_string(line.gettoken_str(2)); + ent.offsets[1]=(num=line.gettoken_intx(2,&conv), conv) ? add_intstring(num) : add_string(line.gettoken_str(2)); ent.offsets[2]=SECTION_FIELD_SET(flags); ent.offsets[3]=1; - if (!IsIntOrUserVar(line,1) || !IsIntOrUserVar(line,2)) PRINTHELP() - SCRIPT_MSG(_T("SectionSetFlags: %") NPRIs _T("->%") NPRIs _T("\n"),line.gettoken_str(1),line.gettoken_str(2)); - return add_entry(&ent); + if (!IsIntOrUserVar(line,1) || (!conv && !IsIntOrUserVar(line,2))) PRINTHELP() + SCRIPT_MSG(_T("SectionSetFlags: %") NPRIs _T("=%") NPRIs _T("\n"),line.gettoken_str(1),line.gettoken_str(2)); + return add_entry(&ent); + } case TOK_SECTIONGETFLAGS: ent.which=EW_SECTIONSET; ent.offsets[0]=add_string(line.gettoken_str(1)); @@ -4806,7 +4809,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) ent.offsets[0]=add_string(line.gettoken_str(1)); ent.offsets[1]=add_string(line.gettoken_str(2)); ent.offsets[2]=1; - SCRIPT_MSG(_T("InstTypeSetText: %") NPRIs _T("->%") NPRIs _T("\n"),line.gettoken_str(1),line.gettoken_str(2)); + SCRIPT_MSG(_T("InstTypeSetText: %") NPRIs _T("=%") NPRIs _T("\n"),line.gettoken_str(1),line.gettoken_str(2)); return add_entry(&ent); case TOK_INSTTYPEGETTEXT: ent.which=EW_INSTTYPESET; @@ -4817,12 +4820,16 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) SCRIPT_MSG(_T("InstTypeGetText: %") NPRIs _T("->%") NPRIs _T("\n"),line.gettoken_str(1),line.gettoken_str(2)); return add_entry(&ent); case TOK_SECTIONSETINSTTYPES: + { + int num, conv; ent.which=EW_SECTIONSET; ent.offsets[0]=add_string(line.gettoken_str(1)); - ent.offsets[1]=add_string(line.gettoken_str(2)); + ent.offsets[1]=(num=line.gettoken_intx(2,&conv), conv) ? add_intstring(num) : add_string(line.gettoken_str(2)); ent.offsets[2]=SECTION_FIELD_SET(install_types); - SCRIPT_MSG(_T("SectionSetInstTypes: %") NPRIs _T("->%") NPRIs _T("\n"),line.gettoken_str(1),line.gettoken_str(2)); - return add_entry(&ent); + if (!IsIntOrUserVar(line,1) || (!conv && !IsIntOrUserVar(line,2))) PRINTHELP() + SCRIPT_MSG(_T("SectionSetInstTypes: %") NPRIs _T("=%") NPRIs _T("\n"),line.gettoken_str(1),line.gettoken_str(2)); + return add_entry(&ent); + } case TOK_SECTIONGETINSTTYPES: ent.which=EW_SECTIONSET; ent.offsets[0]=add_string(line.gettoken_str(1)); @@ -4836,7 +4843,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) ent.offsets[0]=add_string(line.gettoken_str(1)); ent.offsets[1]=add_string(line.gettoken_str(2)); ent.offsets[2]=SECTION_FIELD_SET(size_kb); - SCRIPT_MSG(_T("SectionSetSize: %") NPRIs _T("->%") NPRIs _T("\n"),line.gettoken_str(1),line.gettoken_str(2)); + SCRIPT_MSG(_T("SectionSetSize: %") NPRIs _T("=%") NPRIs _T("\n"),line.gettoken_str(1),line.gettoken_str(2)); return add_entry(&ent); case TOK_SECTIONGETSIZE: ent.which=EW_SECTIONSET; @@ -4928,12 +4935,12 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) } ent.offsets[2] = (flags & LASIF_HWND) ? add_string(line.gettoken_str(tidx+0)) : line.gettoken_int(tidx+0, &conv); fail |= !conv; // HWND/CtrlId flags |= (line.gettoken_int(tidx+1, &conv) & LASIM_IMAGE); fail |= !conv; // IMAGE_* - flags |= ((lrflagsin = line.gettoken_int(tidx+2, &conv)) & LASIM_LR); fail |= !conv; // LR_* + flags |= ((lrflagsin = line.gettoken_intx(tidx+2, &conv)) & LASIM_LR); fail |= !conv; // LR_* ent.offsets[1] = (flags & LASIF_STRID) ? add_string(line.gettoken_str(tidx+3)) : line.gettoken_int(tidx+3, &conv); fail |= !conv; // Image path/resid ent.offsets[3] = flags; // Packed flags, IMAGE_* and LR_* ent.offsets[0] = GetUserVarIndex(line, tidx+4); // Outvar SCRIPT_MSG(_T("LoadAndSetImage %") NPRIs _T(" %#x \"%") NPRIs _T("\""), line.gettoken_str(tidx+0), flags, line.gettoken_str(tidx+3)); - if (ent.offsets[0] >= 0) SCRIPT_MSG(_T(" -> %") NPRIs _T(""), line.gettoken_str(tidx+4)); + if (ent.offsets[0] >= 0) SCRIPT_MSG(_T(" ->%") NPRIs _T(""), line.gettoken_str(tidx+4)); SCRIPT_MSG(_T("\n")); if ((lrflagsin & LASIM_LR) != lrflagsin) warning_fl(DW_PARSE_NUMBEROUTOFSPEC, _T("Out of spec: \"%") NPRIs _T("\""), line.gettoken_str(tidx+2)); if (fail) PRINTHELP(); diff --git a/Source/tokens.cpp b/Source/tokens.cpp index 5e8ce773..74df828e 100644 --- a/Source/tokens.cpp +++ b/Source/tokens.cpp @@ -196,7 +196,7 @@ static tokenType tokenlist[TOK__LAST] = {TOK_SETAUTOCLOSE,_T("SetAutoClose"),1,0,_T("(false|true)"),TP_CODE}, {TOK_SETCTLCOLORS,_T("SetCtlColors"),2,2,_T("hwnd [/BRANDING] [text_color] [transparent|bg_color]"),TP_CODE}, {TOK_SETBRANDINGIMAGE,_T("SetBrandingImage"),1,2,_T("[/IMGID=image_item_id_in_dialog] [/RESIZETOFIT] bitmap.bmp"),TP_CODE}, -{TOK_LOADANDSETIMAGE,_T("LoadAndSetImage"),4,5,_T("[/EXERESOURCE] [/STRINGID] [/RESIZETOFIT[WIDTH|HEIGHT]] ctrl imagetype lrflags imageid [$(user_var: imagehandle)]"),TP_CODE}, +{TOK_LOADANDSETIMAGE,_T("LoadAndSetImage"),4,6,_T("[/EXERESOURCE] [/STRINGID] [/RESIZETOFIT[WIDTH|HEIGHT]] ctrl imagetype lrflags imageid [$(user_var: imagehandle)]"),TP_CODE}, {TOK_SETCOMPRESS,_T("SetCompress"),1,0,_T("(off|auto|force)"),TP_ALL}, {TOK_SETCOMPRESSOR,_T("SetCompressor"),1,2,_T("[/FINAL] [/SOLID] (zlib|bzip2|lzma)"),TP_GLOBAL}, {TOK_SETCOMPRESSORDICTSIZE,_T("SetCompressorDictSize"),1,0,_T("dict_size_mb"),TP_ALL},