From 21f668de9c7e3d53f93074f1c88e302c4eaa0620 Mon Sep 17 00:00:00 2001 From: ramon18 Date: Mon, 21 Jul 2003 19:48:48 +0000 Subject: [PATCH] Fixed problem in IO dirreq/filereq, optimized code, nsExec with /CMD param to allow execute 16Bits DOS applications git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@2762 212acab6-be3b-0410-9dea-997c60f758d6 --- Contrib/InstallOptions/InstallerOptions.cpp | 101 +++++--------------- Contrib/nsExec/nsExec.txt | 18 +++- Contrib/nsExec/nsexec.c | 60 ++++++++++-- Plugins/InstallOptions.dll | Bin 13824 -> 13824 bytes Plugins/nsExec.dll | Bin 5120 -> 5120 bytes 5 files changed, 91 insertions(+), 88 deletions(-) diff --git a/Contrib/InstallOptions/InstallerOptions.cpp b/Contrib/InstallOptions/InstallerOptions.cpp index bdd3ef3f..0f519997 100644 --- a/Contrib/InstallOptions/InstallerOptions.cpp +++ b/Contrib/InstallOptions/InstallerOptions.cpp @@ -18,8 +18,6 @@ #include "../exdll/exdll.h" #undef popstring -void * mini_memcpy(void *out, const void *in, int len); -void * mini_zeromem(void *in, int len); static int popstring(char *str) { @@ -138,7 +136,7 @@ struct FieldType { UINT nControlID; int nParentIdx; // this is used by the filerequest and dirrequest controls, used to store original windowproc for LINK - HANDLE hImage; // this is used by image/icon field to save the handle to the image, or used by LINK control to store the color + HANDLE hImage; // this is used by image/icon field to save the handle to the image }; // initial buffer size. buffers will grow as required. @@ -434,56 +432,6 @@ bool SaveSettings(void) { } #define BROWSE_WIDTH 15 - -void AddBrowseButtons() { - // this function loops through all the controls and if a filerequest or dirrequest - // control is found, then it adds the corresponding browse button. - // NOTE: this also resizes the text box created to make room for the button. - int nIdx; - FieldType *pNewField; - - for (nIdx = nNumFields - 1; nIdx >= 0; nIdx--) { - // we loop down so we don't run into the newly added fields. - switch (pFields[nIdx].nType) { - case FIELD_FILEREQUEST: - case FIELD_DIRREQUEST: - // Insert button after edit, tabstop depends in creation order, - // with this trick browse button get tabstop after parent edit - if ( nIdx < nNumFields - 1 ) - { - mini_memcpy(&pFields[nIdx+2], &pFields[nIdx+1], (nNumFields-nIdx-1)*sizeof(FieldType) ); - for ( int i = nIdx+2; i < nNumFields; i++ ) - { - if ( pFields[i].nType == FIELD_BROWSEBUTTON ) - pFields[i].nParentIdx++; // Ohh! where is your dady :) - } - } - // After moving controls 1 position, don't forget to increment nNumFields at *end* of loop - pNewField = &pFields[nIdx+1]; - mini_zeromem(pNewField, sizeof(FieldType)); // don't use settings from other control - pNewField->nControlID = 1200 + nNumFields; - pNewField->nParentIdx = nIdx; - pNewField->nType = FIELD_BROWSEBUTTON; - pNewField->nFlags = pFields[nIdx].nFlags & (FLAG_DISABLED | FLAG_NOTABSTOP); - //pNewField->pszListItems = NULL; - //pNewField->nMaxLength = 0; - //pNewField->nMinLength = 0; - pNewField->pszText = STRDUP(szBrowseButtonCaption); // needed for generic FREE - //pNewField->pszValidateText = NULL; - - pNewField->rect.right = pFields[nIdx].rect.right; - pNewField->rect.left = pNewField->rect.right - BROWSE_WIDTH; - pNewField->rect.bottom = pFields[nIdx].rect.bottom; - pNewField->rect.top = pFields[nIdx].rect.top; - - pFields[nIdx].rect.right = pNewField->rect.left - 3; - - nNumFields++; - break; - } - } -} - static char szResult[4096]; DWORD WINAPI myGetProfileString(LPCTSTR lpAppName, LPCTSTR lpKeyName) @@ -502,7 +450,7 @@ char * WINAPI myGetProfileStringDup(LPCTSTR lpAppName, LPCTSTR lpKeyName) bool ReadSettings(void) { static char szField[25]; - int nIdx; + int nIdx, nCtrlIdx; pszTitle = myGetProfileStringDup("Settings", "Title"); pszCancelButtonText = myGetProfileStringDup("Settings", "CancelButtonText"); @@ -525,7 +473,7 @@ bool ReadSettings(void) { pFields = (FieldType *)MALLOC(sizeof(FieldType)*2*nNumFields); } - for(nIdx = 0; nIdx < nNumFields; nIdx++) { + for(nIdx = 0, nCtrlIdx = 0; nCtrlIdx < nNumFields; nCtrlIdx++, nIdx++) { // Control types static TableEntry TypeTable[] = { { "LABEL", FIELD_LABEL }, @@ -574,7 +522,7 @@ bool ReadSettings(void) { { NULL, 0 } }; - wsprintf(szField, "Field %d", nIdx + 1); + wsprintf(szField, "Field %d", nCtrlIdx + 1); myGetProfileString(szField, "TYPE"); // Get the control type @@ -671,9 +619,24 @@ bool ReadSettings(void) { pFields[nIdx].hImage = (HANDLE)GetPrivateProfileInt(szField, "TxtColor", RGB(0,0,255), pszFilename); pFields[nIdx].nControlID = 1200 + nIdx; + if ( pFields[nIdx].nType == FIELD_FILEREQUEST || pFields[nIdx].nType == FIELD_DIRREQUEST ) + { + FieldType *pNewField = &pFields[nIdx+1]; + pNewField->nControlID = 1200 + nIdx + 1; + pNewField->nParentIdx = nIdx; + pNewField->nType = FIELD_BROWSEBUTTON; + pNewField->nFlags = pFields[nIdx].nFlags & (FLAG_DISABLED | FLAG_NOTABSTOP); + pNewField->pszText = STRDUP(szBrowseButtonCaption); // needed for generic FREE + pNewField->rect.right = pFields[nIdx].rect.right; + pNewField->rect.left = pNewField->rect.right - BROWSE_WIDTH; + pNewField->rect.bottom = pFields[nIdx].rect.bottom; + pNewField->rect.top = pFields[nIdx].rect.top; + pFields[nIdx].rect.right = pNewField->rect.left - 3; + nNumFields++; + nIdx++; + } } - AddBrowseButtons(); return true; } @@ -696,7 +659,7 @@ LRESULT WMCommandProc(HWND hWnd, UINT id, HWND hwndCtl, UINT codeNotify) { } break; } else if (pFields[nIdx].nType == FIELD_LINK) { - ShellExecute(hMainWindow, NULL, pFields[nIdx].pszState, NULL, NULL, SW_SHOWDEFAULT); + ShellExecute(hMainWindow, NULL, pFields[nIdx].pszState, NULL, NULL, SW_SHOWDEFAULT); } } break; @@ -761,7 +724,7 @@ BOOL CALLBACK cfgDlgProc(HWND hwndDlg, if ( ( lpdis->itemState & ODS_FOCUS && lpdis->itemAction & ODA_DRAWENTIRE) || (lpdis->itemAction & ODA_FOCUS) || (lpdis->itemAction & ODA_SELECT)) DrawFocusRect(lpdis->hDC, &pFields[nIdx].rect); - + SetTextColor(lpdis->hDC, (COLORREF)pFields[nIdx].hImage); pFields[nIdx].rect = lpdis->rcItem; @@ -1363,23 +1326,3 @@ void ConvertNewLines(char *str) { *p2 = 0; } -void * mini_memcpy(void *outBuf, const void *inBuf, int len) -{ - char *c_out=(char*)outBuf+(len-1); - char *c_in=(char *)inBuf+(len-1); - while (len-- > 0) - { - *c_out--=*c_in--; - } - return outBuf; -} - -void * mini_zeromem(void *in, int len) -{ - char *c_in=(char *)in; - while (len-- > 0) - { - *c_in++=0; - } - return in; -} diff --git a/Contrib/nsExec/nsExec.txt b/Contrib/nsExec/nsExec.txt index dcf06c92..5f1e2f75 100644 --- a/Contrib/nsExec/nsExec.txt +++ b/Contrib/nsExec/nsExec.txt @@ -6,15 +6,15 @@ without opening a dos box. Usage ----- -nsExec::Exec [/TIMEOUT=x] path +nsExec::Exec [/TIMEOUT=x] [/CMD] path -or- -nsExec::ExecToLog [/TIMEOUT=x] path +nsExec::ExecToLog [/TIMEOUT=x] [/CMD] path -or- -nsExec::ExecToStack [/TIMEOUT=x] path +nsExec::ExecToStack [/TIMEOUT=x] [/CMD] path All functions are the same except ExecToLog will print the output to the logwindow and ExecToStack will push up to ${NSIS_MAX_STRLEN} @@ -26,6 +26,18 @@ process is received, the timeout value is reset and it will again wait for more output using the timeout value. See Return Value for how to check if there was a timeout. +The /CMD param is optional. The /CMD indicates that the exec procedure should use +command processor to execute the command, ie, %COMSPEC% path + +*IMPORTANT NOTICE* The /CMD param should be used for 16bits MS-DOS applications +or applications that create windows, or any form of user interface + +To ensure that command are executed without problems on all windows versions, +is recommended to use the following syntax: + + nsExec::ExecToStack [OPTIONS] '"PATH" param1 param2 paramN' + +This way the application path may contain non 8.3 paths (with spaces) Return Value ------------ diff --git a/Contrib/nsExec/nsexec.c b/Contrib/nsExec/nsexec.c index e0fcc614..8547857e 100644 --- a/Contrib/nsExec/nsexec.c +++ b/Contrib/nsExec/nsexec.c @@ -40,6 +40,7 @@ void ExecScript(BOOL log); void LogMessage(const char *pStr); char *my_strstr(const char *string, const char *strCharSet); unsigned int my_atoi(char *s); +void * mini_memcpy(void *outBuf, const void *inBuf, int len); void __declspec(dllexport) Exec(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) { g_hwndParent=hwndParent; @@ -69,23 +70,50 @@ BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lp return TRUE; } +#define TAB_REPLACE " " +#define TAB_REPLACE_SIZE (sizeof(TAB_REPLACE)-1) + void ExecScript(int log) { char szRet[128] = ""; + char *pExec; + char sAux[MAX_PATH]; + char *pRoot = NULL; + int nComSpecSize; + BOOL bUseComSpec=false; + nComSpecSize = ExpandEnvironmentStrings("%ComSpec% /c ", sAux, MAX_PATH); g_to = 0; // default is no timeout g_hwndList = FindWindowEx(FindWindowEx(g_hwndParent,NULL,"#32770",NULL),NULL,"SysListView32",NULL); - g_exec = (char *)GlobalAlloc(GPTR, sizeof(char)*g_stringsize+1); - if (!popstring(g_exec)) { + g_exec = (char *)GlobalAlloc(GPTR, sizeof(char)*g_stringsize+nComSpecSize+1); + lstrcpy(g_exec, sAux); + pExec = g_exec + nComSpecSize - 1; + + while ( !popstring(pExec) ) { if (my_strstr(g_exec,"/TIMEOUT=")) { char *szTimeout = g_exec + 9; g_to = my_atoi(szTimeout); - popstring(g_exec); } + else if (my_strstr(g_exec,"/CMD")) { + bUseComSpec = true; + } + else + break; } - if (!g_exec[0]) { + + if (!pExec[0] ) + { lstrcpy(szRet, "error"); goto done; } + + if ( bUseComSpec ) + { + GetWindowsDirectory(sAux, MAX_PATH); // UNC paths not supported on cmd.exe / command.com + pRoot = sAux; + } + else + lstrcpy(g_exec, pExec); + { STARTUPINFO si={sizeof(si),}; SECURITY_ATTRIBUTES sa={sizeof(sa),}; @@ -128,7 +156,7 @@ void ExecScript(int log) { si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); si.hStdOutput = newstdout; si.hStdError = newstdout; - if (!CreateProcess(NULL,g_exec,NULL,NULL,TRUE,CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi)) { + if (!CreateProcess(NULL,g_exec,NULL,NULL,TRUE,0,NULL,pRoot,&si,&pi)) { lstrcpy(szRet, "error"); goto done; } @@ -160,12 +188,21 @@ void ExecScript(int log) { } if (!(log & 2)) { + while ( p = my_strstr(p, "\t") ) + { + mini_memcpy(p+TAB_REPLACE_SIZE, p+1, lstrlen(p)); + lstrcpy(p, TAB_REPLACE); + p += TAB_REPLACE_SIZE; + *p = ' '; + } + + p = szUnusedBuf; // get the old left overs while (lineBreak = my_strstr(p, "\r\n")) { *lineBreak = 0; LogMessage(p); p = lineBreak + 2; } - + // If data was taken out from the unused buffer, move p contents to the start of szUnusedBuf if (p != szUnusedBuf) { char *p2 = szUnusedBuf; @@ -270,3 +307,14 @@ unsigned int my_atoi(char *s) { } return (int)v; } + +void * mini_memcpy(void *outBuf, const void *inBuf, int len) +{ + char *c_out=(char*)outBuf+(len-1); + char *c_in=(char *)inBuf+(len-1); + while (len-- > 0) + { + *c_out--=*c_in--; + } + return outBuf; +} diff --git a/Plugins/InstallOptions.dll b/Plugins/InstallOptions.dll index 23434d82eca2c8489bef5d470f6f614cd48851fa..cc206d572c1e1826b963329c3325a3dd73ca015f 100644 GIT binary patch delta 2106 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 diff --git a/Plugins/nsExec.dll b/Plugins/nsExec.dll index 101f64b430fbccf2881454d65431902337c8d193..1dd8bf93efe09f4352d704a7e1831f4d739dfb91 100644 GIT binary patch literal 5120 zcmeHLeQaCR6~9j0G%3ypP@}N5)TKdVP|{~VzceAFb3R@|S<*i8(zq!&t!uy54vuZv z@6mKiNWh7R^Yo-iL)wSfR2FGs1Z@;`v_pfrgf%ULt^9%MMyGWsf4FmmQnl+6HSqSk z&rZXHfNB4%e{iL9?>(RQoO91T{^;2AJyJ*rF#~j+kP}$xa2fL#PiMg{+wf``Iacz{ z#uKLYcQ$qff^l0k7C9L6_S<~ka5y5{_Di;y61D}yww9futv}+Is!L1PROL+<4tLJB z-SNQj6=*oV2l$(}d5^oW-r8U}-ir0awpwHD4Eh38_VYXwctYAuW>V=~_pN1_3#4r0 zO{OA3ib1jDs83g5rC>Ag3S(c8%LAp*d>BK(oJYXMfD+ZazUI(nP0%L!S)xht7U0G2LI2iF;V|$tk;9U9>Ye+zUr9YD|-ofn_fByhD<&uz>ML- z^;hS_$T+pZ0BG<=+*tF&MM$bTCWRtCLN=Q~1i_HI1?#7Vi;$*Ip+A6T(;DmJ2~f7Uj>`$(O#2v zU7s0?Vs3%i8U7r7&X=dOq4VROV^dQ|-O#V6FpxC5e>>?p-B$(NNa{W$b!O~Q%+DBF ztd2ak)p?4Oo=kFRA?cY-dY0sSuJXkNMwXOSCukcG`kM4~{dAJQQa<`eTo^oUho?z? zNi&QAe-L=$wGb>9C^n5h<^~NN2Un6u%rrFqSdu@VElhgOYy3G;(D->kMo11_(5)>H z7)zCp_TvD}2oNS*Aog9I;h!}ji3gINBf9kgkdplKDnFm(Urh2ZY5ZwnSzehFMYkyC z(kANG+Pux%VNH{(Bt?8Q4pc0WOOS7DT5P*&~+t4B%tHk z0s(r>RB|&URek~K8~-6CmXAJ3Rg@)_yBjP$ZHS^6)4Fn{a6z|Lfm%Nk-&Ws1OE6YM z^QQ)D|Bb24=eQxbgv2LYa3tF2==*}b%Y2r;wpb`@9Tmc%=r;2Rd znf=I_y3kX9tw%Yf4lM=NQlU&crOx-%U+$3y-Q#y;PQq*LiGmq(RgS;;I$ya^KeQyf z)sCgCdEC_4HnIBtY31tp?b((((&Z*sTqwT zV8OwNl0MpOai3qeS}2d7MFt`01d_k(^3h6^=4bBt;ym7d-j;dA7R``cg3M-KMV5Mi zn&dK-U#hK?R|}bolveu_+NqREU75}6<8zbxBI8gbE=LqHhas#s>;th!SKij&r;cHo zr&9)#(OOh400BLXN|MX2mjm6T_x3(=X8JkWp1a3V@;6mx?5ey;Yc&hHTxH2cu9h0z zyQn*9(yi1Go9ac2?R8Bj8YqXzb;e`vPNa%y%d^r>sB=c|pQmOFb4IBc2dN}kWNea5 z?=+fpV(xCy-B-eXfEH4>o_0_)o22L50h=zr-$n%mzMjub(;hhiM`YDy(y@(n|i?B$IyLF`)RqnB<3nDtv!eH}A zJyBMvd~vqkV9}PJRy&ICU0_r9{vp|6QFj$j+@xNvwNxm#s2vtock6dF0 zE7f$iB(b=4_#Un0e9c2Ub}Ikc+dGYv{x@UuCPFFzbUd|@kO{y~0WSbv z0h|J)0keR4z!D$?Sa&BO)qrgP0niHw0|o)l0FD5D3Fw8a1*?FSj#=!_0j>b(IBzo+ zV|oiTJXhk%%Tl5lzTwlHoCPe8cIvNf8T-%m{~gaiO>Lpbes8EXCP~N;X%0o=(u3Zx zKO~V(Q=26713|eNKZ1l<#3#k$glsPqLy{CF4;OYxvHoD#E3fcgDh$Qtn9nOW67t*J zK6*IZNS5-abV`jO;f`GCl;a=14pE79%Y1Cd6u zcUgxTB!8p)ZYdTIM#B6+BU#M*)oyrsk#gO^us?Dr-V%&SJ~25~Vx0-*r6eIO=%QanUjF_{34gvMkT;VE3>=Hp&jO zN%qI=i|p&{4Eq=ML-rE8z?z&D&J9k7v(CBA>2@A+PCBQYN1eZSo^`(O{JZlbr-fU` zZQ>r~eB1!{eeOrxFSrxjyWIQShukG@kt?dP)U2!7SaVNJbB()Ze@&!Dt@&Bav6>q= J>o<&R{|1tovNHex delta 2246 zcmc&#e{54#6uz$=qr+`<Kv(HkfV#;%nb~xG{zW>4v&Mp|DY)gE30Frm|nJh{`Nx zE1`L-aXc1{N&-xYIE@4hQ3TU412aMtA_W3OBqSDDv}9vUGq`^Db%=?7@TV{N-o59X z@0@$@dFR}>wQy_U+jF=J9i`9PpWV6lMDy)ao%gZ!ZbxUMCZF!~VN62ags~t#+}VV& z{amj$F0HHeF`te+OUI9fwAY)*&h5Ay$jF^aNPrNsSzW?S<$BddotTyju_JHdn3=M7 zvkcpvboDIvB-gI~$rW&G)jVAR_voP)bf0rO(ng0?i^QZX$j|JurzT^&7NjPou7#K+ z)**nq2aGg7*Yz;6^nk2;DA~Zsp&lV-Kb}dJD}8VFeaI2Ay8^4aDy#gHw}+GWD0K_5 z=&@Uv2$%Z1U5ez(B_`ht9vqi1AH#-jS0rZze5k~B_X&+VXzKD9Q-uxHE7*X%ei})+#1VpCgzMHBxUYmD^-Q z#1@zi`4~CGpAP*}w=ff7=M(|zy^PQR|m$6eh zoPf9wa+q>gT85jZv&73!C=y$*3YCz>0#EN70A4jbeHtI|IO`i*UF)WLref5XbOsaR=RtdJH=J7)$` zn~;zSH$+a(Rvg1c7J0NYv5)mu(Jt0NK?@D`jRYX6%>=M&>$jOHr86-AvLda3(oKV> zXq@c~uLOG~@zNSZk=BAh(h5l}Nh0nOzf9t34DYHmhw~D-Z2B3W=m^lL%kUHtNFqslo~xP+Oi%WypoLR!@%k z?NFmGg9|055ONA&Pg5QtuL0YD_kexCLEt!W7Ptzm28I9wd@R5;U^Y+ztOWePW?&IG zeSjTc&u-{;0tW#0{FJK=g7)49C8~_@BV?wGL5Hi8^aA%E1O6Q(wQs139b@G|Nr34X@54WU+pkx9R1&jWx2@*;HTaZEhyyvy{C?hW#tG zJF9rYQB4!@ZiHsPZmG9seOX<=i%VX;n-$9WJ+($)ULa6kOI~s;UF4_`Y%|vf@Y{^5 z7qcgFCF1M^$oA?R*6kg;@`7(YnU&H(PW_}C*4u6>cnm^8e&!6G1@YncX z`F>t!HCQdy0_zNGk@XAfS?e|HpjB_nw-wteYz?+oY++ls?TGD^?VRlwTheyNmM)lt zJV6i~!Xjaf;1e2zEkdi%F1#-s6UuG~w}d;wBjPwQM=TIUajCdYtP}T&`^9g>i{j7X sAL2c6yp$sqN@dbY$tyKUZ%H3X2c;v@N$Iq7MY<;4l>U-X+_SmA0mX#tF#rGn