From 0bffaecea36dc24983f44a14749daabb815ff755 Mon Sep 17 00:00:00 2001 From: anders_k Date: Thu, 19 Jun 2014 19:06:49 +0000 Subject: [PATCH] Added preprocess only mode (/[SAFE]PPO switch) git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@6495 212acab6-be3b-0410-9dea-997c60f758d6 --- Docs/src/history.but | 2 ++ Docs/src/usage.but | 2 ++ Source/build.cpp | 11 ++++++----- Source/build.h | 7 +++++-- Source/growbuf.cpp | 9 +++++++++ Source/growbuf.h | 2 ++ Source/makenssi.cpp | 46 +++++++++++++++++++++++++++----------------- Source/script.cpp | 24 +++++++++++++++++++++-- Source/tokens.cpp | 34 +++++++++++++++++++++----------- 9 files changed, 99 insertions(+), 38 deletions(-) diff --git a/Docs/src/history.but b/Docs/src/history.but index 793b7bab..319f2628 100644 --- a/Docs/src/history.but +++ b/Docs/src/history.but @@ -8,6 +8,8 @@ Released on ?, 2014 \S2{} Major Changes +\b Added PPO and SafePPO preprocess-only compiler switches + \b MakeNSIS WM_COPYDATA messages now use the QH_OUTPUTCHARSET encoding with CP_ACP as the default for compatibility with old IDEs. \S2{} Minor Changes diff --git a/Docs/src/usage.but b/Docs/src/usage.but index eca32f3c..ba59b65b 100644 --- a/Docs/src/usage.but +++ b/Docs/src/usage.but @@ -33,6 +33,8 @@ If you want to use MakeNSIS on the command line, the syntax of the makensis comm \b /OUTPUTCHARSET allows you to specify the codepage used by stdout when the output is redirected. (\NsisOutputCharset) +\b /PPO or /SAFEPPO will only run the preprocessor and print the result to stdout. The safe version will not execute instructions like !appendfile or !system. !packhdr and !finalize are never executed. + \b Using the /D switch one or more times will add to symbols to the globally defined list (See !define). \b Using the /X switch one or more times will execute the code you specify following it. Example: "/XAutoCloseWindow false" diff --git a/Source/build.cpp b/Source/build.cpp index 6ad1f103..f8f6e5c4 100644 --- a/Source/build.cpp +++ b/Source/build.cpp @@ -105,15 +105,15 @@ CEXEBuild::~CEXEBuild() } } -CEXEBuild::CEXEBuild() : - m_exehead(0), - m_exehead_size(0) +CEXEBuild::CEXEBuild(signed char pponly) : + m_exehead(0), + m_exehead_size(0), + preprocessonly(pponly) { set_verbosity(3); curlinereader=0; - curfilename=0; - linecnt=0; + curfilename=0, linecnt=0; cur_ifblock=NULL; last_line_had_slash=0; inside_comment=false; @@ -3366,6 +3366,7 @@ int CEXEBuild::get_verbosity() const void CEXEBuild::set_verbosity(int lvl) { + if (preprocessonly) lvl = STD_MIN(lvl, 1); display_errors = lvl > 0; display_warnings = lvl > 1; display_info = lvl > 2; diff --git a/Source/build.h b/Source/build.h index 4ebcf511..228766e0 100644 --- a/Source/build.h +++ b/Source/build.h @@ -102,7 +102,7 @@ namespace MakensisAPI { class CEXEBuild { public: - CEXEBuild(); + CEXEBuild(signed char pponly); void initialize(const TCHAR *makensis_path); ~CEXEBuild(); @@ -135,7 +135,7 @@ class CEXEBuild { // process a script (you can process as many scripts as you want, // it is as if they are concatenated) int process_script(NIStream&Strm, const TCHAR *filename); - int process_oneline(TCHAR *line, const TCHAR *curfilename, int lineptr); + int process_oneline(const TCHAR *line, const TCHAR *curfilename, int lineptr); // you only get to call write_output once, so use it wisely. int write_output(void); @@ -145,6 +145,7 @@ class CEXEBuild { DefineList definedlist; // List of identifiers marked as "defined" like // C++ macro definitions such as _UNICODE. void define(const TCHAR *p, const TCHAR *v=_T("")); // to add a defined thing. + signed char preprocessonly; // > 0 = safe, < 0 = unsafe int get_verbosity() const; void set_verbosity(int lvl); @@ -173,6 +174,8 @@ class CEXEBuild { // tokens.cpp bool is_ppbranch_token(TCHAR *s); + bool is_pp_token(int tkid); + bool is_unsafe_pp_token(int tkid); int get_commandtoken(TCHAR *s, int *np, int *op, int *pos); const TCHAR* get_commandtoken_name(int tok); diff --git a/Source/growbuf.cpp b/Source/growbuf.cpp index ea41119c..61d63148 100644 --- a/Source/growbuf.cpp +++ b/Source/growbuf.cpp @@ -98,3 +98,12 @@ void GrowBuf::resize(int newlen) int GrowBuf::getlen() const { return m_used; } void *GrowBuf::get() const { return m_s; } + +void GrowBuf::swap(GrowBuf&other) +{ + std::swap(m_s, other.m_s); + std::swap(m_alloc, other.m_alloc); + std::swap(m_used, other.m_used); + std::swap(m_zero, other.m_zero); + std::swap(m_bs, other.m_bs); +} diff --git a/Source/growbuf.h b/Source/growbuf.h index 836df03c..0797a59f 100644 --- a/Source/growbuf.h +++ b/Source/growbuf.h @@ -103,6 +103,8 @@ class GrowBuf : public IGrowBuf */ void *get() const; + void swap(GrowBuf&other); + private: void *m_s; /* the storage buffer */ int m_alloc; /* allocated bytes */ diff --git a/Source/makenssi.cpp b/Source/makenssi.cpp index 62fa7a11..c8279971 100644 --- a/Source/makenssi.cpp +++ b/Source/makenssi.cpp @@ -176,6 +176,7 @@ static void print_usage() #ifdef _WIN32 _T(" ") OPT_STR _T("OUTPUTCHARSET <") TSTR_OUTPUTCHARSET _T(">\n") #endif + _T(" ") OPT_STR _T("[SAFE]PPO preprocess to stdout/file\n") _T(" ") OPT_STR _T("Ddefine[=value] defines the symbol \"define\" for the script [to value]\n") _T(" ") OPT_STR _T("Xscriptcmd executes scriptcmd in script (i.e. \"") OPT_STR _T("XOutFile inst.exe\")\n") _T(" ") _T(" parameters are processed by order (") OPT_STR _T("Ddef ins.nsi != ins.nsi ") OPT_STR _T("Ddef)\n") @@ -298,6 +299,7 @@ static inline int makensismain(int argc, TCHAR **argv) bool no_logo=true; bool initialparsefail=false; bool noconfig=false; + signed char pponly=0; #ifdef _WIN32 signed char outputbom=1; @@ -352,6 +354,10 @@ static inline int makensismain(int argc, TCHAR **argv) outputenc.SetCodepage(NStreamEncoding::UTF16LE); } #endif + else if (!_tcsicmp(swname,_T("PPO")) || !_tcsicmp(swname,_T("SafePPO"))) + { + pponly = S7IsChEqualI('s',swname[0]) ? 1 : -1; + } else if (S7IsChEqualI('v',swname[0]) && swname[1] && !swname[2]) { no_logo=swname[1] >= _T('0') && swname[1] <= _T('2'); @@ -394,7 +400,7 @@ static inline int makensismain(int argc, TCHAR **argv) unsigned int files_processed=0; unsigned int cmds_processed=0; - CEXEBuild build; + CEXEBuild build(pponly); try { build.initialize(argv[0]); @@ -421,7 +427,7 @@ static inline int makensismain(int argc, TCHAR **argv) fflush(g_output); return 0; } - if (!no_logo) print_logo(); + if (!no_logo && !pponly) print_logo(); argpos=initialparsefail ? argc : 1; @@ -431,15 +437,17 @@ static inline int makensismain(int argc, TCHAR **argv) in_files=1; else if (IS_OPT(argv[argpos]) && _tcscmp(argv[argpos], _T("-")) && !in_files) { - if (!_tcsicmp(&argv[argpos][1],_T("NOCD"))) do_cd=false; - else if (!_tcsicmp(&argv[argpos][1],_T("NOCONFIG"))) noconfig=true; - else if (!_tcsicmp(&argv[argpos][1],_T("PAUSE"))) g_dopause=true; - else if (!_tcsicmp(&argv[argpos][1],_T("LICENSE"))) + const TCHAR* const swname = &argv[argpos][1]; + if (!_tcsicmp(swname,_T("PPO")) || !_tcsicmp(swname,_T("SafePPO"))) {} // Already parsed + else if (!_tcsicmp(swname,_T("NOCD"))) do_cd=false; + else if (!_tcsicmp(swname,_T("NOCONFIG"))) noconfig=true; + else if (!_tcsicmp(swname,_T("PAUSE"))) g_dopause=true; + else if (!_tcsicmp(swname,_T("LICENSE"))) { if (build.display_info) print_license(); nousage++; } - else if (!_tcsicmp(&argv[argpos][1],_T("CMDHELP"))) + else if (!_tcsicmp(swname,_T("CMDHELP"))) { if (argpos < argc-1) build.print_help(argv[++argpos]); @@ -447,12 +455,12 @@ static inline int makensismain(int argc, TCHAR **argv) build.print_help(NULL); nousage++; } - else if (!_tcsicmp(&argv[argpos][1],_T("HDRINFO"))) + else if (!_tcsicmp(swname,_T("HDRINFO"))) { print_stub_info(build); nousage++; } - else if (!_tcsicmp(&argv[argpos][1],_T("INPUTCHARSET")) || !_tcsicmp(&argv[argpos][1],_T("ICS"))) + else if (!_tcsicmp(swname,_T("INPUTCHARSET")) || !_tcsicmp(swname,_T("ICS"))) { if (!HasReqParam(argv, ++argpos, argc)) break; WORD cp = GetEncodingFromString(argv[argpos]); @@ -464,13 +472,13 @@ static inline int makensismain(int argc, TCHAR **argv) } inputenc.SafeSetCodepage(cp); } - else if (S7IsChEqualI('v',argv[argpos][1]) && + else if (S7IsChEqualI('v',*swname) && argv[argpos][2] >= _T('0') && argv[argpos][2] <= _T('4') && !argv[argpos][3]) { int v=argv[argpos][2]-_T('0'); build.set_verbosity(v); } - else if (S7IsChEqualI('p',argv[argpos][1]) && + else if (S7IsChEqualI('p',*swname) && argv[argpos][2] >= _T('0') && argv[argpos][2] <= _T('5') && !argv[argpos][3]) { #ifdef _WIN32 @@ -496,10 +504,10 @@ static inline int makensismain(int argc, TCHAR **argv) #endif } // Already parsed these (must adjust argpos) - else if (!_tcsicmp(&argv[argpos][1],_T("NOTIFYHWND"))) ++argpos; - else if (!_tcsicmp(&argv[argpos][1],_T("OUTPUTCHARSET")) || !_tcsicmp(&argv[argpos][1],_T("OCS"))) ++argpos; + else if (!_tcsicmp(swname,_T("NOTIFYHWND"))) ++argpos; + else if (!_tcsicmp(swname,_T("OUTPUTCHARSET")) || !_tcsicmp(swname,_T("OCS"))) ++argpos; // These must be parsed last because they will eat other switches - else if (S7IsChEqualI('d',argv[argpos][1]) && argv[argpos][2]) + else if (S7IsChEqualI('d',swname[0]) && swname[1]) { TCHAR *p=argv[argpos]+2; TCHAR *s=_tcsdup(p),*v; @@ -509,9 +517,9 @@ static inline int makensismain(int argc, TCHAR **argv) build.define(s,v?v:_T("")); free(s); } - else if (S7IsChEqualI('x',argv[argpos][1]) && argv[argpos][2]) + else if (S7IsChEqualI('x',swname[0]) && swname[1]) { - if (build.process_oneline(argv[argpos]+2,_T(""),argpos+1) != PS_OK) + if (build.process_oneline(swname+1,_T(""),argpos+1) != PS_OK) { return 1; } @@ -519,9 +527,9 @@ static inline int makensismain(int argc, TCHAR **argv) } // Already parsed these ("VERSION" never gets this far) #ifdef _WIN32 - else if (!_tcsicmp(&argv[argpos][1],_T("RAW"))) {} + else if (!_tcsicmp(swname,_T("RAW"))) {} #endif - else if (S7IsChEqualI('o',argv[argpos][1]) && argv[argpos][2]) {} + else if (S7IsChEqualI('o',swname[0]) && swname[1]) {} else break; } @@ -615,6 +623,8 @@ static inline int makensismain(int argc, TCHAR **argv) return 1; } + if (build.preprocessonly) return 0; + if (build.display_info) { _ftprintf(g_output,_T("\nProcessed ")); diff --git a/Source/script.cpp b/Source/script.cpp index e98eee62..500531cd 100644 --- a/Source/script.cpp +++ b/Source/script.cpp @@ -286,6 +286,8 @@ int CEXEBuild::process_script(NIStream&Strm, const TCHAR *filename) } #define PRINTHELP() { print_help(line.gettoken_str(0)); return PS_ERROR; } +static void PREPROCESSONLY_BEGINCOMMENT() { extern FILE *g_output; _ftprintf(g_output,_T("!if 0 /*\n")); } +static void PREPROCESSONLY_ENDCOMMENT() { extern FILE *g_output; _ftprintf(g_output,_T("*/\n!endif\n")); } void CEXEBuild::start_ifblock() { @@ -369,6 +371,8 @@ int CEXEBuild::doParse(const TCHAR *str) } } + GrowBuf ppoline; + if (preprocessonly) m_linebuild.swap(ppoline); // LineParser strips quotes and we need to display them m_linebuild.resize(0); if (res) @@ -393,7 +397,11 @@ parse_again: ERROR_MSG(_T("Invalid label: %") NPRIs _T(" (labels cannot begin with !, $, -, +, or 0-9)\n"),line.gettoken_str(0)); return PS_ERROR; } - if (add_label(line.gettoken_str(0))) return PS_ERROR; + extern FILE *g_output; + if (preprocessonly) + _ftprintf(g_output,_T("%") NPRIs _T("\n"),line.gettoken_str(0)); + else + if (add_label(line.gettoken_str(0))) return PS_ERROR; line.eattoken(); goto parse_again; } @@ -616,6 +624,16 @@ parse_again: } if (!cur_ifblock || (!cur_ifblock->ignore && !cur_ifblock->inherited_ignore)) { + if (preprocessonly) + { + 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 (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; + } return doCommand(tkid,line); } @@ -1006,7 +1024,7 @@ l_errwcconv: return PS_OK; } -int CEXEBuild::process_oneline(TCHAR *line, const TCHAR *filename, int linenum) +int CEXEBuild::process_oneline(const TCHAR *line, const TCHAR *filename, int linenum) { const TCHAR *last_filename = curfilename; int last_linecnt = linecnt; @@ -3129,6 +3147,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) } if (!validparams || comp == -1) PRINTHELP() SCRIPT_MSG(_T("%") NPRIs _T(": \"%") NPRIs _T("\"\n"),cmdname,exec); + PREPROCESSONLY_BEGINCOMMENT(); #ifdef _WIN32 if (TOK_P_EXECUTE == which_token) { @@ -3164,6 +3183,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) ERROR_MSG(_T("%") NPRIs _T(": returned %d, aborting\n"),cmdname,ret); return PS_ERROR; } + PREPROCESSONLY_ENDCOMMENT(); SCRIPT_MSG(_T("%") NPRIs _T(": returned %d\n"),cmdname,ret); } return PS_OK; diff --git a/Source/tokens.cpp b/Source/tokens.cpp index 845509ad..d329c1c8 100644 --- a/Source/tokens.cpp +++ b/Source/tokens.cpp @@ -335,10 +335,29 @@ bool CEXEBuild::is_ppbranch_token(TCHAR *s) case TOK_P_IFDEF: case TOK_P_IFNDEF: case TOK_P_IFMACRODEF: case TOK_P_IFMACRONDEF: return true; - default: return false; + default: + return false; } } +bool CEXEBuild::is_pp_token(int tkid) +{ + // NOTE: This assumes that all TOK_P_* in tokens.h are grouped together. + return (tkid >= TOK_P_IF && tkid <= TOK_P_SEARCHREPLACESTRING); +} + +bool CEXEBuild::is_unsafe_pp_token(int tkid) +{ + switch(tkid) + { + case TOK_P_TEMPFILE: case TOK_P_APPENDFILE: case TOK_P_DELFILE: + case TOK_P_SYSTEMEXEC: case TOK_P_EXECUTE: case TOK_P_FINALIZE: + case TOK_P_PACKEXEHEADER: + return true; + } + return false; +} + int CEXEBuild::get_commandtoken(TCHAR *s, int *np, int *op, int *pos) { for (int x = 0; x < TOK__LAST; x ++) @@ -355,16 +374,7 @@ int CEXEBuild::get_commandtoken(TCHAR *s, int *np, int *op, int *pos) int CEXEBuild::GetCurrentTokenPlace() { if (build_cursection) - { - if (build_cursection_isfunc) - { - return TP_FUNC; - } - else - { - return TP_SEC; - } - } + return build_cursection_isfunc ? TP_FUNC : TP_SEC; if (cur_page) return TP_PAGEEX; @@ -374,6 +384,8 @@ int CEXEBuild::GetCurrentTokenPlace() int CEXEBuild::IsTokenPlacedRight(int pos, TCHAR *tok) { + if (preprocessonly) + return PS_OK; if ((unsigned int) pos > (sizeof(tokenlist) / sizeof(tokenType))) return PS_OK;