#include <windows.h>
#include <shlobj.h>
#include "fileform.h"
#include "util.h"
#include "state.h"
#include "ui.h"
#include "exec.h"
#include "lang.h"

#define EXEC_ERROR 0x7FFFFFFF

#ifdef NSIS_CONFIG_COMPONENTPAGE
HWND g_lbSectionHack;
#endif

#ifdef NSIS_SUPPORT_STACK
typedef struct _stack_t {
  struct _stack_t *next;
  char text[NSIS_MAX_STRLEN];
} stack_t;

static stack_t *g_st;
#endif

static int exec_errorflag;
#ifdef NSIS_SUPPORT_REBOOT
static int exec_rebootflag;
#endif

static WIN32_FIND_DATA *file_exists(char *buf)
{
  HANDLE h;
  static WIN32_FIND_DATA fd;
  h = FindFirstFile(buf,&fd);
  if (h != INVALID_HANDLE_VALUE) 
  {
    FindClose(h);
    return &fd;
  }
  return NULL;
}

#ifdef NSIS_SUPPORT_RMDIR
static void doRMDir(char *buf, int recurse)
{
  if (recurse && is_valid_instpath(buf))
  {
    int i=lstrlen(buf);
    HANDLE h;
    WIN32_FIND_DATA fd;
    lstrcat(buf,"\\*.*");
    h = FindFirstFile(buf,&fd);
    if (h != INVALID_HANDLE_VALUE) 
    {
      do
      {
        if (fd.cFileName[0] != '.' ||
            (fd.cFileName[1] != '.' && fd.cFileName[1]))
        {
          lstrcpy(buf+i+1,fd.cFileName);
          if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) doRMDir(buf,recurse);
          else 
          {
            update_status_text(LANG_STR(LANG_DELETEFILE),buf);
            if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) 
              SetFileAttributes(buf,fd.dwFileAttributes^FILE_ATTRIBUTE_READONLY);
            DeleteFile(buf);
          }
        }
      } while (FindNextFile(h,&fd));
      FindClose(h);
    }
    buf[i]=0; // fix buffer
  }
  log_printf2("RMDir: RemoveDirectory(\"%s\")",buf);
  update_status_text(LANG_STR(LANG_REMOVEDIR),buf);
  RemoveDirectory(buf);
}
#endif//NSIS_SUPPORT_RMDIR



#ifdef NSIS_SUPPORT_REGISTRYFUNCTIONS
// based loosely on code from Tim Kosse
// in win9x this isn't necessary (RegDeleteKey() can delete a tree of keys),
// but in win2k you need to do this manually.
static LONG myRegDeleteKeyEx(HKEY thiskey, LPCTSTR lpSubKey, int onlyifempty)
{
	HKEY key;
	int retval=RegOpenKeyEx(thiskey,lpSubKey,0,KEY_ALL_ACCESS,&key);
	if (retval==ERROR_SUCCESS)
	{
		char buffer[MAX_PATH+1];
		while (RegEnumKey(key,0,buffer,MAX_PATH+1)==ERROR_SUCCESS)
    {
      if (onlyifempty) 
      {
    		RegCloseKey(key);
        return !ERROR_SUCCESS;
      }
      if ((retval=myRegDeleteKeyEx(key,buffer,0)) != ERROR_SUCCESS) break;
    }
		RegCloseKey(key);
		retval=RegDeleteKey(thiskey,lpSubKey);
	}
	return retval;
}
#endif//NSIS_SUPPORT_REGISTRYFUNCTIONS

extern char g_all_user_var_flag;

static int ExecuteEntry(entry *entries, int pos);

static int resolveaddr(int v) 
{ 
  if (v<0) return myatoi(g_usrvars[-(v+1)]);  // if <0, that means we 
  return v; 
}

int ExecuteCodeSegment(entry *entries, int pos, HWND hwndProgress) 
{
  while (pos >= 0)
  {
    int rv;
    if (entries[pos].which == EW_RET) return 0;
    rv=ExecuteEntry(entries,pos);
    if (rv == EXEC_ERROR) return EXEC_ERROR;

    rv=resolveaddr(rv);
    
    if (!rv) { rv++; pos++; }
    else
    {
      int t=pos;
      rv--; // rv is decremented here by 1, since it was +1 on the other end.
      pos=rv; // set new position
      rv-=t; // set rv to delta for progress adjustment
    }
   
    if (hwndProgress) 
    {
      extern int progress_bar_pos, progress_bar_len;
      progress_bar_pos+=rv;
      if (!progress_bar_len) progress_bar_len++;
      SendMessage(hwndProgress,PBM_SETPOS,MulDiv(progress_bar_pos,30000,progress_bar_len),0);
    }
  }
  return 0;
}

// returns EXEC_ERROR on error
// returns 0, advance position by 1
// otherwise, returns new_position+1
static int ExecuteEntry(entry *entries, int pos)
{
  static char buf[NSIS_MAX_STRLEN],buf2[NSIS_MAX_STRLEN],buf3[NSIS_MAX_STRLEN],buf4[NSIS_MAX_STRLEN];
  int *parms=entries[pos].offsets;
  int which=entries[pos].which;
  switch (which)
  {
    case EW_NOP: 
      log_printf2("Jump: %d",parms[0]); 
    return parms[0];
    case EW_ABORT:
      {
        process_string_fromtab(buf,parms[0]);
        log_printf2("Aborting: \"%s\"",buf);
        update_status_text("",buf);
      }
    return EXEC_ERROR;
    case EW_QUIT:
      g_quit_flag++;
      if (g_hwnd) PostQuitMessage(0); // make sure we bail out fast.
    return EXEC_ERROR;
    case EW_CALL:
      {
        int v=resolveaddr(parms[0])-1;  // address is -1, since we encode it as +1
        log_printf2("Call: %d",v); 
        return ExecuteCodeSegment(entries,v,NULL);
      }
    case EW_UPDATETEXT:
      if (parms[1]) ui_st_updateflag=parms[1];
      else
      {
        process_string_fromtab(buf4,parms[0]);
        log_printf2("detailprint: %s",buf4);
        update_status_text(buf4,"");
      }
    return 0;
    case EW_SLEEP:
      {
        int x=process_string_fromtab_toint(parms[0]);
        if (x < 1) x=1;
        log_printf2("Sleep(%d)",x);
        Sleep(x);
      }
    return 0;
    case EW_SETSFCONTEXT:
      g_all_user_var_flag=parms[0];
    return 0;
    case EW_HIDEWINDOW:
      log_printf("HideWindow");
      ShowWindow(g_hwnd,SW_HIDE);
    return 0;
    case EW_BRINGTOFRONT:
      log_printf("BringToFront");
      ShowWindow(g_hwnd,SW_SHOW);
      SetForegroundWindow(g_hwnd);
    return 0;
    case EW_SETWINDOWCLOSE:
      g_autoclose=parms[0];
    return 0;
    case EW_CHDETAILSVIEW:
      if (insthwndbutton) ShowWindow(insthwndbutton,parms[1]);
      if (insthwnd) ShowWindow(insthwnd,parms[0]); 
    return 0;
    case EW_SETFILEATTRIBUTES:
      process_string_fromtab(buf,parms[0]);
      log_printf3("SetFileAttributes: \"%s\":%08X",buf,parms[1]);
      if (!SetFileAttributes(buf,parms[1]))
      {
        exec_errorflag++;
        log_printf("SetFileAttributes failed.");
      }
    return 0;
    case EW_CREATEDIR:
      process_string_fromtab(buf2,parms[0]);
      log_printf3("CreateDirectory: \"%s\" (%d)",buf2,parms[1]);
      if (parms[1]) 
      {
        update_status_text(LANG_STR(LANG_OUTPUTDIR),buf2);
        lstrcpy(state_output_directory,buf2);     
      }
      else update_status_text(LANG_STR(LANG_CREATEDIR),buf2);
      recursive_create_directory(buf2);
    return 0;
    case EW_IFFILEEXISTS:
      process_string_fromtab(buf,parms[0]);
      if (file_exists(buf)) 
      {
        log_printf3("IfFileExists: file \"%s\" exists, jumping %d",buf,parms[1]);
        return parms[1];
      }
      log_printf3("IfFileExists: file \"%s\" does not exist, jumping %d",buf,parms[2]);
    return parms[2];
    case EW_IFERRORS:
      {
        int f=exec_errorflag;
        exec_errorflag=parms[2];
        if (f)
        {
          return parms[0];
        }
      }
    return parms[1];
#ifdef NSIS_SUPPORT_RENAME
    case EW_RENAME:
      {
        process_string_fromtab(buf,parms[0]);
        process_string_fromtab(buf2,parms[1]);
        lstrcpy(buf4,buf);
        if (lstrlen(buf)+lstrlen(buf2) < NSIS_MAX_STRLEN-3)
        {
          lstrcat(buf4,"->");
          lstrcat(buf4,buf2);
        }
        log_printf2("Rename: %s",buf4);
        if (MoveFile(buf,buf2))
        {
          update_status_text(LANG_STR(LANG_RENAME),buf4);
        }
        else
        {
#ifdef NSIS_SUPPORT_MOVEONREBOOT
          if (parms[2] && file_exists(buf))
          {
#ifdef NSIS_SUPPORT_REBOOT
            exec_rebootflag++;
#endif
            MoveFileOnReboot(buf,buf2);
            update_status_text(LANG_STR(LANG_RENAMEONREBOOT),buf4);
            log_printf2("Rename on reboot: %s",buf4);
          }
          else
#endif
          {
            exec_errorflag++;
            log_printf2("Rename failed: %s",buf4);
          }
        }
      }
    return 0;
#endif//NSIS_SUPPORT_RENAME
#ifdef NSIS_SUPPORT_FNUTIL
    case EW_GETFULLPATHNAME:
      {
        char *p=g_usrvars[parms[0]];
        char *fp;
        process_string_fromtab(buf,parms[1]);
        if (!GetFullPathName(buf,NSIS_MAX_STRLEN,p,&fp)) 
        {
          exec_errorflag++;
          *p=0;
        }
        else if (fp>buf && *fp)
        {
          WIN32_FIND_DATA *fd=file_exists(buf);
          if (fd)
          {
            lstrcpy(fp,fd->cFileName);
          }
          else
          {
            exec_errorflag++;
            *p=0;
          }
        }
        if (!parms[2]) GetShortPathName(p,p,NSIS_MAX_STRLEN);
      }
    return 0;
    case EW_SEARCHPATH:
      {
        char *fp;
        char *p=g_usrvars[parms[0]];
        process_string_fromtab(buf,parms[1]);          
        if (!SearchPath(NULL,buf,NULL,NSIS_MAX_STRLEN,p,&fp))
        {
          p[0]=0;
          exec_errorflag++;
        }
      }
    return 0;
    case EW_GETTEMPFILENAME:
      {
        char *textout=g_usrvars[parms[0]];
        if (!GetTempPath(NSIS_MAX_STRLEN,buf) || !GetTempFileName(buf,"nst",0,textout))
        {
          *textout=0;
          exec_errorflag++;
        }
      }
    return 0;
#endif
#ifdef NSIS_SUPPORT_FILE
    case EW_EXTRACTFILE:
      {
        HANDLE hOut;
        int ret;
        int overwriteflag=parms[0];
        lstrcpy(buf,state_output_directory);
        addtrailingslash(buf);

        process_string_fromtab(buf4,parms[1]);
        log_printf3("File: overwriteflag=%d, name=\"%s\"",overwriteflag,buf4);
        if (validpathspec(buf4))
        {
          lstrcpy(buf,buf4);
        }
        else lstrcat(buf,buf4);
      _tryagain:
        if (!overwriteflag)
        {
          int attr=GetFileAttributes(buf);
          if (attr & FILE_ATTRIBUTE_READONLY)
            SetFileAttributes(buf,attr^FILE_ATTRIBUTE_READONLY);
        }
        if (overwriteflag == 3) // check date and time
        {
          WIN32_FIND_DATA *ffd=file_exists(buf); 
          overwriteflag=1; // if it doesn't exist, fall back to no overwrites (since it shouldn't matter anyway)
          if (ffd)
          {
            overwriteflag=(CompareFileTime(&ffd->ftLastWriteTime,(FILETIME*)(parms+3)) >= 0);  // if first one is newer, then don't overwrite
          }
        }
        hOut=myOpenFile(buf,GENERIC_WRITE,(overwriteflag==1)?CREATE_NEW:CREATE_ALWAYS);
        if (hOut == INVALID_HANDLE_VALUE)
        {
          if (overwriteflag) 
          {
            update_status_text(LANG_STR(LANG_SKIPPED),buf4);
            if (overwriteflag==2) exec_errorflag++;
            log_printf3("File: skipped: \"%s\" (overwriteflag=%d)",buf,overwriteflag); 
            return 0;
          }
          log_printf2("File: error creating \"%s\"",buf); 
          lstrcpy(buf3,g_usrvars[0]);//save $0
          lstrcpy(g_usrvars[0],buf); 

          process_string_fromtab(buf2,g_inst_cmnheader->fileerrtext_ptr);
          lstrcpy(g_usrvars[0],buf3); // restore $0

          switch (MessageBox(g_hwnd,buf2,g_caption,MB_ABORTRETRYIGNORE|MB_ICONSTOP))
          {
            case IDRETRY:
              log_printf("File: error, user retry"); 
              goto _tryagain;
            case IDIGNORE:
              log_printf("File: error, user cancel"); 
              exec_errorflag++;
              return 0;
            default:
              log_printf("File: error, user abort"); 
              update_status_text(LANG_STR(LANG_CANTWRITE),buf);
            return EXEC_ERROR;
          }
        }

        update_status_text(LANG_STR(LANG_EXTRACT),buf4);
        ret=GetCompressedDataFromDataBlock(parms[2],hOut);

        log_printf3("File: wrote %d to \"%s\"",ret,buf);

        if (parms[3] != 0xffffffff || parms[4] != 0xffffffff)
          SetFileTime(hOut,(FILETIME*)(parms+3),NULL,(FILETIME*)(parms+3));

        CloseHandle(hOut);

        if (ret < 0)
        {
          if (ret == -2)
          {
            lstrcpy(buf,LANG_STR(LANG_ERRORWRITING));
            lstrcat(buf,buf4);
          }
          else
          {
            lstrcpy(buf,LANG_STR(LANG_ERRORDECOMPRESSING));
          }
          log_printf2("%s",buf);
          MessageBox(g_hwnd,buf,g_caption,MB_OK|MB_ICONSTOP);
          return EXEC_ERROR;
        }
      }
    return 0;
#endif//NSIS_SUPPORT_FILE
#ifdef NSIS_SUPPORT_DELETE
    case EW_DELETEFILE:
      {
		    HANDLE h;
		    WIN32_FIND_DATA fd;
        process_string_fromtab(buf2,parms[0]);
        lstrcpy(buf,buf2);
        log_printf2("Delete: \"%s\"",buf); 
        trimslashtoend(buf);
    		h=FindFirstFile(buf2,&fd);
		    if (h != INVALID_HANDLE_VALUE)
		    {
          do
          {
			      if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 
            {
              wsprintf(buf2,"%s\\%s",buf,fd.cFileName);
              if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) 
                SetFileAttributes(buf2,fd.dwFileAttributes^FILE_ATTRIBUTE_READONLY);
              if (DeleteFile(buf2))
              {
                log_printf2("Delete: DeleteFile(\"%s\")",buf2); 
                update_status_text(LANG_STR(LANG_DELETEFILE),buf2);
              }
              else
              {
#ifdef NSIS_SUPPORT_MOVEONREBOOT
                if (parms[1])
                {
#ifdef NSIS_SUPPORT_REBOOT
                  exec_rebootflag++;
#endif
                  log_printf2("Delete: DeleteFile on Reboot(\"%s\")",buf2); 
                  update_status_text(LANG_STR(LANG_DELETEONREBOOT),buf2);
                  MoveFileOnReboot(buf2,NULL);
                }
                else
#endif
                {
                  exec_errorflag++;
                }
              }
            }
          } while (FindNextFile(h,&fd));
			    FindClose(h);
    		}
      }
    return 0;
#endif//NSIS_SUPPORT_DELETE
#ifdef NSIS_SUPPORT_MESSAGEBOX
    case EW_MESSAGEBOX: // MessageBox      
      {
        int v;
        process_string_fromtab(buf4,parms[1]);
        log_printf3("MessageBox: %d,\"%s\"",parms[0],buf4); 
        v=MessageBox(g_hwnd,buf4,g_caption,parms[0]);
        if (v)
        {
          if (v==(parms[2]&0xffff))
          {
            return parms[3];
          }
          if (v==(parms[2]>>16))
          {
            return parms[4];
          }
        }
        else exec_errorflag++;
      }
    return 0;
#endif//NSIS_SUPPORT_MESSAGEBOX
#ifdef NSIS_SUPPORT_RMDIR
    case EW_RMDIR:
      {
        process_string_fromtab(buf,parms[0]);
        log_printf2("RMDir: \"%s\"",buf); 

        if (lastchar(buf)=='\\') trimslashtoend(buf);

        doRMDir(buf,parms[1]);
        if (file_exists(buf)) exec_errorflag++;
      }
    return 0;
#endif//NSIS_SUPPORT_RMDIR
#ifdef NSIS_SUPPORT_STROPTS
    case EW_STRLEN:
      process_string_fromtab(buf,parms[1]);
      myitoa(g_usrvars[parms[0]],lstrlen(buf));
    return 0;
    case EW_ASSIGNVAR:
      {
        int newlen=process_string_fromtab_toint(parms[2]);
        int start=process_string_fromtab_toint(parms[3]);
        int l;
        char *p=g_usrvars[parms[0]];
        process_string_fromtab(buf,parms[1]);
        *p=0;
        if (parms[2] < 0 || newlen)
        {
          l=lstrlen(buf);

          if (start<0) start=l+start;
          if (start>=0)
          {
            if (start>l) start=l;
            lstrcpy(p,buf+start);        
            if (newlen)
            {
              if (newlen<0) newlen=lstrlen(p)+newlen;
              if (newlen<0) newlen=0;
              if (newlen < NSIS_MAX_STRLEN) p[newlen]=0;
            }
          }
        }
      }
    return 0;
    case EW_STRCMP:
      process_string_fromtab(buf3,parms[0]);
      process_string_fromtab(buf4,parms[1]);
      if (!lstrcmpi(buf3,buf4)) return parms[2];
    return parms[3];
#endif//NSIS_SUPPORT_STROPTS
#ifdef NSIS_SUPPORT_ENVIRONMENT
    case EW_READENVSTR:
      {
        char *p=g_usrvars[parms[0]];
        process_string_fromtab(buf,parms[1]);
        if (parms[2])
        {
          if (!GetEnvironmentVariable(buf,p,NSIS_MAX_STRLEN))
          {
            *p=0;
            exec_errorflag++;
          }
        }
        else
        {
          ExpandEnvironmentStrings(buf,p,NSIS_MAX_STRLEN);
        }
        p[NSIS_MAX_STRLEN-1]=0;
      }
    return 0;
#endif//NSIS_SUPPORT_ENVIRONMENT
#ifdef NSIS_SUPPORT_INTOPTS
    case EW_INTCMP:
      {
        int v,v2;
        v=process_string_fromtab_toint(parms[0]);
        v2=process_string_fromtab_toint(parms[1]);
        if (v<v2) return parms[3];
        if (v>v2) return parms[4];
      }
    return parms[2];
    case EW_INTCMPU:
      {
        unsigned int v,v2;
        v=(unsigned int)process_string_fromtab_toint(parms[0]);
        v2=(unsigned int)process_string_fromtab_toint(parms[1]);
        if (v<v2) return parms[3];
        if (v>v2) return parms[4];
      }
    return parms[2];
    case EW_INTOP:
      {
        int v,v2;
        char *p=g_usrvars[parms[0]];
        v=process_string_fromtab_toint(parms[1]);
        v2=process_string_fromtab_toint(parms[2]);
        switch (parms[3])
        {
          case 0: v+=v2; break;
          case 1: v-=v2; break;
          case 2: v*=v2; break;
          case 3: if (v2) v/=v2; else { v=0; exec_errorflag++; } break;
          case 4: v|=v2; break;
          case 5: v&=v2; break;
          case 6: v^=v2; break;
          case 7: v=~v; break;
          case 8: v=!v; break;
          case 9: v=v||v2; break;
          case 10: v=v&&v2; break;
          case 11: if (v2) v%=v2; else { v=0; exec_errorflag++; } break;
        }
        myitoa(p,v);
      }
    return 0;
    case EW_INTFMT:
      process_string_fromtab(buf,parms[1]);
      wsprintf(g_usrvars[parms[0]],
               buf,
               process_string_fromtab_toint(parms[2]));
    return 0;
#endif//NSIS_SUPPORT_INTOPTS
#ifdef NSIS_SUPPORT_STACK
    case EW_PUSHPOP:
      {
        stack_t *s=g_st; 
        int cnt=parms[2];
        if (cnt) //Exch contributed by Fritz Elfert
        {
          while (cnt--&&s) s=s->next;
          if (!s)
          {
            log_printf2("Exch: stack < %d elements",parms[2]);
            break;
          }
          lstrcpy(buf,s->text);
          lstrcpy(s->text,g_st->text);
          lstrcpy(g_st->text,buf);
        }
        else if (parms[1]) 
        {
          if (!s) 
          {
            log_printf("Pop: stack empty");
            exec_errorflag++;
            return 0;
          }
          lstrcpy(g_usrvars[parms[0]],s->text); 
          g_st=s->next; 
          GlobalFree((HGLOBAL)s);
        }
        else
        {
          s=(stack_t*)GlobalAlloc(GPTR,sizeof(stack_t));
          process_string_fromtab(s->text,parms[0]);
          s->next=g_st;
          g_st=s;
        }
      }
    return 0;
#endif//NSIS_SUPPORT_STACK
#ifdef NSIS_SUPPORT_HWNDS
    case EW_FINDWINDOW:
    case EW_SENDMESSAGE:
      {
        int v;
        int b3=process_string_fromtab_toint(parms[3]);
        int b4=process_string_fromtab_toint(parms[4]);

        process_string_fromtab(buf,parms[1]);
        process_string_fromtab(buf2,parms[2]);

        if (which == EW_SENDMESSAGE) v=SendMessage((HWND)myatoi(buf),myatoi(buf2),b3,b4);
        else v=(int)FindWindowEx((HWND)b3,(HWND)b4,buf[0]?buf:NULL,buf2[0]?buf2:NULL);
        
        if (parms[0]>=0) 
          myitoa(g_usrvars[parms[0]],v);
      }
    return 0;
    case EW_ISWINDOW:
        if (IsWindow((HWND)process_string_fromtab_toint(parms[0]))) return parms[1];
    return parms[2];
#endif
#ifdef NSIS_SUPPORT_SHELLEXECUTE
    case EW_SHELLEXEC: // this uses improvements of Andras Varga
      {
        int x;
        process_string_fromtab(buf,parms[0]);
        process_string_fromtab(buf2,parms[1]);
        process_string_fromtab(buf3,parms[2]);
        lstrcpy(buf4,buf);
        lstrcat(buf4," ");
        lstrcat(buf4,buf2);
        update_status_text(LANG_STR(LANG_EXECSHELL), buf4);
        x=(int)ShellExecute(g_hwnd,buf[0]?buf:NULL,buf2,buf3[0]?buf3:NULL,state_output_directory,parms[3]);
        if (x < 33)
        {
          log_printf5("ExecShell: warning: error (\"%s\": file:\"%s\" params:\"%s\")=%d",buf,buf2,buf3,x);
          exec_errorflag++;
        }
        else
        {
          log_printf4("ExecShell: success (\"%s\": file:\"%s\" params:\"%s\")",buf,buf2,buf3);
        }
      }
    return 0;
#endif//NSIS_SUPPORT_SHELLEXECUTE
#ifdef NSIS_SUPPORT_EXECUTE
    case EW_EXECUTE:
      {
        HANDLE hProc;
        process_string_fromtab(buf,parms[0]);
        log_printf2("Exec: command=\"%s\"",buf);
        update_status_text(LANG_STR(LANG_EXECUTE),buf);

        hProc=myCreateProcess(buf,*state_output_directory?state_output_directory:NULL);

        if (hProc)
        {
          log_printf2("Exec: success (\"%s\")",buf);
          if (parms[1]) 
          {
            DWORD lExitCode;
            while (WaitForSingleObject(hProc,100) == WAIT_TIMEOUT)
            {
              static MSG msg;
              while (PeekMessage(&msg,NULL,WM_PAINT,WM_PAINT,PM_REMOVE))
                DispatchMessage(&msg);
            }
            GetExitCodeProcess(hProc, &lExitCode);

            if (parms[2]>=0) myitoa(g_usrvars[parms[2]],lExitCode);
            else if (lExitCode) exec_errorflag++;
          }
          CloseHandle( hProc );
        }
        else 
        { 
          exec_errorflag++;
          log_printf2("Exec: failed createprocess (\"%s\")",buf); 
        }
      }
    return 0;
#endif//NSIS_SUPPORT_EXECUTE
#ifdef NSIS_SUPPORT_GETFILETIME
    case EW_GETFILETIME: 
      // this new implementation based on one by Dave Bau
      // used FindFirstFile instead of GetFileTime to better handle files that are locked.
      // also allows GetFileTime to be passed a wildcard.
      { 
        WIN32_FIND_DATA *ffd; 
        char *highout=g_usrvars[parms[1]]; 
        char *lowout=g_usrvars[parms[2]]; 
        process_string_fromtab(buf,parms[0]); 

        ffd=file_exists(buf);
        if (ffd) 
        { 
          myitoa(lowout,ffd->ftLastWriteTime.dwLowDateTime); 
          myitoa(highout,ffd->ftLastWriteTime.dwHighDateTime); 
        } 
        else 
        {
          *lowout=*highout=0;
          exec_errorflag++; 
        }
      }
    return 0;
#endif//NSIS_SUPPORT_GETFILETIME
#ifdef NSIS_SUPPORT_GETDLLVERSION
    case EW_GETDLLVERSION:
      {
        char *highout=g_usrvars[parms[1]];
        char *lowout=g_usrvars[parms[2]];
        DWORD s1;
        DWORD t[4]; // our two members are the 3rd and 4th..
        VS_FIXEDFILEINFO *pvsf1=(VS_FIXEDFILEINFO*)t;
        DWORD d;
        process_string_fromtab(buf,parms[0]);
        s1=GetFileVersionInfoSize(buf,&d);
        *lowout=*highout=0;
        exec_errorflag++;
        if (s1)
        {
          void *b1;
          b1=(void*)GlobalAlloc(GPTR,s1);
          if (b1)
          {
            UINT uLen;
            if (GetFileVersionInfo(buf,0,s1,b1) && VerQueryValue(b1,"\\",(void*)&pvsf1,&uLen))
            {
              myitoa(highout,pvsf1->dwFileVersionMS);
              myitoa(lowout,pvsf1->dwFileVersionLS);

              exec_errorflag--;
            }
            GlobalFree(b1);
          }
        }
      }
      return 0;
#endif//NSIS_SUPPORT_GETDLLVERSION
#ifdef NSIS_SUPPORT_ACTIVEXREG
    case EW_REGISTERDLL:
      {
        HRESULT hres=OleInitialize(NULL);
        exec_errorflag++;
        if (hres == S_FALSE || hres == S_OK)
        {
          HANDLE h;
          process_string_fromtab(buf,parms[0]);
          process_string_fromtab(buf2,parms[1]);
          
          h=LoadLibrary(buf);
          if (h)
          {
            FARPROC funke = GetProcAddress(h,buf2);
            if (funke) 
            {
              exec_errorflag--;
              if (parms[2]<0)
              {
                void (*func)(HWND,int,char*,void*);
                func=(void*)funke;
                func(g_hwnd,NSIS_MAX_STRLEN,(char*)g_usrvars,
#ifdef NSIS_SUPPORT_STACK
                (void*)&g_st);
#else
                NULL);
#endif
              }
              else 
              {
                process_string_fromtab(buf3,parms[2]);
                update_status_text(buf3,buf);
                funke();
              }
            }
            else
            {
              update_status_text(LANG_STR(LANG_CANNOTFINDSYMBOL),buf2); 
              log_printf3("Error registering DLL: %s not found in %s",buf2,buf);
            }
            FreeLibrary(h);
          }
          else
          {
            update_status_text(LANG_STR(LANG_COULDNOTLOAD),buf); 
            log_printf2("Error registering DLL: Could not load %s",buf);
          }
          OleUninitialize();
        }
        else
        {
          update_status_text(LANG_STR(LANG_NOOLE),buf); 
          log_printf("Error registering DLL: Could not initialize OLE");
        }
      }
    return 0;
#endif
#ifdef NSIS_SUPPORT_CREATESHORTCUT
    case EW_CREATESHORTCUT:
      process_string_fromtab(buf3,parms[0]);
      process_string_fromtab(buf2,parms[1]);
      process_string_fromtab(buf, parms[2]);
      process_string_fromtab(buf4,parms[3]);

      log_printf8("CreateShortCut: out: \"%s\", in: \"%s %s\", icon: %s,%d, sw=%d, hk=%d",
          buf3,buf2,buf,buf4,parms[4]&0xff,(parms[4]&0xff00)>>8,parms[4]>>16); 

      if (CreateShortCut(g_hwnd, buf3, buf4[0]?buf4:NULL, parms[4]&0xff, buf2, buf[0]?buf:NULL,
          state_output_directory,(parms[4]&0xff00)>>8,parms[4]>>16))
      {
        exec_errorflag++;
        update_status_text(LANG_STR(LANG_ERRORCREATINGSHORTCUT),buf3);
      }
      else
      {
        update_status_text(LANG_STR(LANG_CREATESHORTCUT),buf3);
      }
    return 0;
#endif//NSIS_SUPPORT_CREATESHORTCUT
#ifdef NSIS_SUPPORT_COPYFILES
    case EW_COPYFILES: // CopyFile (added by NOP)
      {
        int res;
		    SHFILEOPSTRUCT op;
        process_string_fromtab(buf,parms[0]);
        process_string_fromtab(buf2,parms[1]);
			  log_printf3("CopyFiles \"%s\"->\"%s\"",buf,buf2);
			  op.hwnd=g_hwnd;
			  op.wFunc=FO_COPY;
			  buf[lstrlen(buf)+1]=0;
			  buf2[lstrlen(buf2)+1]=0;

        lstrcpy(buf3,LANG_STR(LANG_COPYTO));
        lstrcat(buf3,buf2);

        op.pFrom=buf;
			  op.pTo=buf2;
        op.lpszProgressTitle=buf3;
			  op.fFlags=parms[2];        
        update_status_text("",buf3);
			  res=SHFileOperation(&op);
			  if (res) 
        { // some of these changes were from Edgewise (wiked_edge@yahoo.com)
          update_status_text(LANG_STR(LANG_COPYFAILED),"");
          exec_errorflag++;
			  }
    	}
		return 0;
#endif//NSIS_SUPPORT_COPYFILES
#ifdef NSIS_SUPPORT_REBOOT
    case EW_REBOOT:
      exec_errorflag++;
      if (parms[0] == 0xbadf00d) 
      {
        HANDLE h=LoadLibrary("advapi32.dll");
        if (h)
        {
          BOOL (*OPT)(HANDLE, DWORD,PHANDLE);
          BOOL (*LPV)(LPCTSTR,LPCTSTR,PLUID);
          BOOL (*ATP)(HANDLE,BOOL,PTOKEN_PRIVILEGES,DWORD,PTOKEN_PRIVILEGES,PDWORD);
          OPT=(void*)GetProcAddress(h,"OpenProcessToken");
          LPV=(void*)GetProcAddress(h,"LookupPrivilegeValueA");
          ATP=(void*)GetProcAddress(h,"AdjustTokenPrivileges");
          if (OPT && LPV && ATP)
          {
            HANDLE hToken; 
            TOKEN_PRIVILEGES tkp; 
            if (OPT(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
            {
              LPV(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid); 
              tkp.PrivilegeCount = 1;
              tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 
              ATP(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);             
            }
          }
        }
         
        if (ExitWindowsEx(EWX_REBOOT|EWX_FORCE,0)) ExitProcess(0);

        FreeLibrary(h);

        return 0;
      }
    break;
    case EW_IFREBOOTFLAG: return parms[!exec_rebootflag];
    case EW_SETREBOOTFLAG: exec_rebootflag=parms[0]; return 0;
#endif//NSIS_SUPPORT_REBOOT
#ifdef NSIS_SUPPORT_INIFILES
    case EW_WRITEINI:
      {
        char *sec, *ent;
        sec=ent=0;
#ifdef NSIS_CONFIG_LOG      
        lstrcpy(buf2,"<RM>");
        lstrcpy(buf3,buf2);
#endif
        process_string_fromtab(buf,parms[0]);
        if (parms[1]>=0) 
        { 
          process_string_fromtab(buf2,parms[1]); 
          sec=buf2; 
        }
        if (parms[2]>=0) 
        { 
          process_string_fromtab(buf3,parms[2]); 
          ent=buf3; 
        }
        process_string_fromtab(buf4,parms[3]); 
        log_printf5("WriteINIStr: wrote [%s] %s=%s in %s",buf,buf2,buf3,buf4);
        if (!WritePrivateProfileString(buf,sec,ent,buf4)) exec_errorflag++;
      }
    return 0;
    case EW_READINISTR:
      {
        static const char *errstr="!N~";
        char *p=g_usrvars[parms[0]];
        process_string_fromtab(buf,parms[1]);
        process_string_fromtab(buf2,parms[2]);
        process_string_fromtab(buf3,parms[3]);
        GetPrivateProfileString(buf,buf2,errstr,p,NSIS_MAX_STRLEN-1,buf3);
        if (*((int*)errstr) == *((int*)p))
        {
          exec_errorflag++;
          p[0]=0;
        }
      }
    return 0;
#endif//NSIS_SUPPORT_INIFILES
#ifdef NSIS_SUPPORT_REGISTRYFUNCTIONS
    case EW_DELREG:
      {
        int rootkey=parms[0];
        exec_errorflag++;
        process_string_fromtab(buf4,parms[1]);
        if (parms[2] != -1)
        {
          HKEY hKey;
          if (RegOpenKeyEx((HKEY)rootkey,buf4,0,KEY_ALL_ACCESS,&hKey) == ERROR_SUCCESS) 
          {
            process_string_fromtab(buf,parms[2]);
            log_printf4("DeleteRegValue: %d\\%s\\%s",rootkey,buf4,buf);
            if (RegDeleteValue(hKey,buf) == ERROR_SUCCESS) exec_errorflag--;
            RegCloseKey(hKey);
          }
        }
        else
        {
          log_printf3("DeleteRegKey: %d\\%s",rootkey,buf4);
          if (myRegDeleteKeyEx((HKEY)rootkey,buf4,parms[3]) == ERROR_SUCCESS) exec_errorflag--;
        }
      }
    return 0;
    case EW_WRITEREG: // write registry value
      {
        HKEY hKey;
        int rootkey=parms[0];
        int type=parms[4];
        exec_errorflag++; 
        process_string_fromtab(buf2,parms[2]);
        process_string_fromtab(buf4,parms[1]);
        if (RegCreateKey((HKEY)rootkey,buf4,&hKey) == ERROR_SUCCESS) 
        {
          if (type <= 1)
          {
            process_string_fromtab(buf3,parms[3]);
            if (RegSetValueEx(hKey,buf2,0,type==1?REG_SZ:REG_EXPAND_SZ,buf3,lstrlen(buf3)+1) == ERROR_SUCCESS) exec_errorflag--;
            log_printf5("WriteRegStr: set %d\\%s\\%s to %s",rootkey,buf4,buf2,buf3);
          }
          else if (type == 2)
          {
            DWORD l;
            l=process_string_fromtab_toint(parms[3]);
            if (RegSetValueEx(hKey,buf2,0,REG_DWORD,(unsigned char*)&l,4) == ERROR_SUCCESS) exec_errorflag--;
            log_printf5("WriteRegDWORD: set %d\\%s\\%s to %d",rootkey,buf4,buf2,l);
          }
          else if (type == 3)
          {
            int len=GetCompressedDataFromDataBlockToMemory(parms[3], buf3, NSIS_MAX_STRLEN);
            if (len >= 0)
            {
              if (RegSetValueEx(hKey,buf2,0,REG_BINARY,buf3,len) == ERROR_SUCCESS) exec_errorflag--;
            }
            log_printf5("WriteRegBin: set %d\\%s\\%s with %d bytes",rootkey,buf4,buf2,len);

          }
          RegCloseKey(hKey);
        }
        else { log_printf3("WriteReg: error creating key %d\\%s",rootkey,buf4); }
      }
    return 0;
    case EW_READREGSTR: // read registry string
      {
        HKEY hKey;
        char *p=g_usrvars[parms[0]];
        int rootkey=parms[1];
        process_string_fromtab(buf,parms[2]); // buf == subkey
        process_string_fromtab(buf2,parms[3]); // buf == key name
        p[0]=0;
 		    if (RegOpenKeyEx((HKEY)rootkey,buf,0,KEY_READ,&hKey) == ERROR_SUCCESS)
        {
			    DWORD l = NSIS_MAX_STRLEN;
			    DWORD t;

          if (RegQueryValueEx(hKey,buf2,NULL,&t,p,&l ) != ERROR_SUCCESS ||
              (t != REG_DWORD && t != REG_SZ && t != REG_EXPAND_SZ))
          {
            p[0]=0;
            exec_errorflag++;
          }
          else
          {            
            if (t==REG_DWORD) 
            {
              if (!parms[4]) exec_errorflag++;
              myitoa(p,*((DWORD*)p));
            }
            else if (parms[4]) exec_errorflag++;
          }
          RegCloseKey(hKey);
        }
        else exec_errorflag++;
     }
    return 0;
    case EW_REGENUM:
      {
        HKEY key;
        char *p=g_usrvars[parms[0]];
        int b=process_string_fromtab_toint(parms[3]);
        process_string_fromtab(buf2,parms[2]);
        p[0]=0;
      	if (RegOpenKeyEx((HKEY)parms[1],buf2,0,KEY_ALL_ACCESS,&key) == ERROR_SUCCESS) 
        {
          DWORD d=NSIS_MAX_STRLEN-1;
          if (parms[4]) RegEnumKey(key,b,p,d);
          else RegEnumValue(key,b,p,&d,NULL,NULL,NULL,NULL);
          p[NSIS_MAX_STRLEN-1]=0;
          RegCloseKey(key);
        }
        else exec_errorflag++;
      }

    return 0;
#endif//NSIS_SUPPORT_REGISTRYFUNCTIONS
#ifdef NSIS_SUPPORT_FILEFUNCTIONS
    case EW_FCLOSE:      
      {
        char *t=g_usrvars[parms[0]];
        if (*t) CloseHandle((HANDLE)myatoi(t));
      }
    return 0;
    case EW_FOPEN:
      {
        HANDLE h;
        char *handleout=g_usrvars[parms[3]];
        process_string_fromtab(buf,parms[0]);
        h=myOpenFile(buf,parms[1],parms[2]);
        if (h == INVALID_HANDLE_VALUE)
        {
          *handleout=0;
          exec_errorflag++;
        }
        else
        {
          myitoa(handleout,(int)h);
        }
      }
    return 0;
    case EW_FPUTS:
      {
        DWORD dw;
        int l;
        char *t=g_usrvars[parms[0]];
        if (parms[2])
        {
          ((unsigned char *)buf2)[0]=process_string_fromtab_toint(parms[1])&0xff;
          l=1;
        }
        else
        {
          process_string_fromtab(buf2,parms[1]);
          l=lstrlen(buf2);
        }
        if (!*t || !WriteFile((HANDLE)myatoi(t),buf2,l,&dw,NULL))
        {
          exec_errorflag++;
        }
      }
    return 0;
    case EW_FGETS:
      {
        char *textout=g_usrvars[parms[1]];
        DWORD dw;
        int rpos=0;
        char *hptr=g_usrvars[parms[0]];
        int maxlen=process_string_fromtab_toint(parms[2]);
        if (maxlen<1) return 0;
        if (maxlen > NSIS_MAX_STRLEN-1) maxlen=NSIS_MAX_STRLEN-1;
        if (*hptr)
        {
          char lc=0;
          int rcnt=0;
          HANDLE h=(HANDLE)myatoi(hptr);
          while (rpos<maxlen)
          {
            char c;
            if (!ReadFile(h,&c,1,&dw,NULL) || dw != 1) break;
            if (parms[3])
            {
              myitoa(textout,(unsigned int)(unsigned char)c);
              return 0;
            }
            if (!c) break;
            if (lc == '\r' || lc == '\n') 
            {
              if (lc == c || (c != '\r' && c != '\n')) SetFilePointer(h,-1,NULL,FILE_CURRENT);
              else textout[rpos++]=c;
              break;
            }
            textout[rpos++]=c;
            lc=c;
          }
        }
        textout[rpos]=0;
        if (!rpos) exec_errorflag++;
      }
    return 0;
    case EW_FSEEK:
      {
        char *t=g_usrvars[parms[0]];
        if (*t)
        {
          DWORD v=SetFilePointer((HANDLE)myatoi(t),process_string_fromtab_toint(parms[1]),NULL,parms[2]);
       
          if (parms[3]>=0)
          {
            myitoa(g_usrvars[parms[3]],v);
          }
        }
      }
    return 0;
#endif//NSIS_SUPPORT_FILEFUNCTIONS
#ifdef NSIS_SUPPORT_FINDFIRST
    case EW_FINDCLOSE:
      {
        char *t=g_usrvars[parms[0]];
        if (*t) FindClose((HANDLE)myatoi(t));
      }
    return 0;
    case EW_FINDNEXT:
      {
        char *textout=g_usrvars[parms[0]];
        char *t=g_usrvars[parms[1]];
        WIN32_FIND_DATA fd;
        if (*t && FindNextFile((HANDLE)myatoi(t),&fd))
        {
          lstrcpy(textout,fd.cFileName);
        }
        else
        {
          exec_errorflag++;
          *textout=0;
        }

      }
    return 0;
    case EW_FINDFIRST:
      {
        char *textout=g_usrvars[parms[1]];
        char *handleout=g_usrvars[parms[2]];
        HANDLE h;
        WIN32_FIND_DATA fd;
        process_string_fromtab(buf,parms[0]);
        h=FindFirstFile(buf,&fd);
        if (h == INVALID_HANDLE_VALUE)
        {
          *handleout=0;
          *textout=0;
          exec_errorflag++;
        }
        else
        {
          myitoa(handleout,(int)h);
          lstrcpy(textout,fd.cFileName);
        }
      }
    return 0;
#endif//NSIS_SUPPORT_FINDFIRST
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
    case EW_WRITEUNINSTALLER:
      {
        int ret=-666;
        HANDLE hFile;
        process_string_fromtab(buf,parms[0]);

        if (validpathspec(buf))
        {
          lstrcpy(buf2,buf);
        }
        else 
        {
          lstrcpy(buf2,state_install_directory);
          addtrailingslash(buf2);
          lstrcat(buf2,buf);
        }


        hFile=myOpenFile(buf2,GENERIC_WRITE,CREATE_ALWAYS);
        if (hFile != INVALID_HANDLE_VALUE)
        {
          char *filebuf;
          DWORD l;
          filebuf=(char *)GlobalAlloc(GMEM_FIXED,g_filehdrsize);
          if (filebuf)
          {
            int fixoffs=0;
            SetFilePointer(g_db_hFile,0,NULL,FILE_BEGIN);
            ReadFile(g_db_hFile,filebuf,g_filehdrsize,&l,NULL);
            if (g_inst_header->uninstexehead_iconoffset > 0)
            {
              GetCompressedDataFromDataBlockToMemory(g_inst_header->uninstdata_offset,
                filebuf+g_inst_header->uninstexehead_iconoffset,g_inst_header->uninstexehead_iconlen);
            }
            WriteFile(hFile,filebuf,g_filehdrsize,&l,NULL);
            GlobalFree(filebuf);
            ret=GetCompressedDataFromDataBlock(-1,hFile);
          }
          CloseHandle(hFile);
        }
        log_printf3("created uninstaller: %d, \"%s\"",ret,buf2);
        if (ret < 0)
        {
          update_status_text(LANG_STR(LANG_ERRORCREATING),buf);
          DeleteFile(buf2);
        }
        else
          update_status_text(LANG_STR(LANG_CREATEDUNINST),buf);
      }
    return 0;
#endif//NSIS_CONFIG_UNINSTALL_SUPPORT
#ifdef NSIS_CONFIG_LOG
    case EW_LOG:
      if (parms[0])
      {
        log_printf2("settings logging to %d",parms[1]);
        log_dolog=parms[1];
        log_printf2("logging set to %d",parms[1]);
      }
      else
      {
        process_string_fromtab(buf,parms[1]);
        log_printf2("%s",buf);
      }
    return 0;
#endif//NSIS_CONFIG_LOG
#ifdef NSIS_CONFIG_COMPONENTPAGE
    case EW_SECTIONSET:
      {
        int x=process_string_fromtab_toint(parms[0]);
        if (g_inst_section && x >= 0 && x < g_inst_header->num_sections)
        {
          int z=0;
          if (g_lbSectionHack)
          {
            int a;
            for (a = 0; a < x; a ++) if (g_inst_section[a].name_ptr>=0) z++;
          }

          if (parms[1]==0) //set text
          {
            if (g_lbSectionHack)
            {
              if (g_inst_section[x].name_ptr>=0) 
                SendMessage(g_lbSectionHack,LB_DELETESTRING,z,0);
              
              if (parms[2]>=0)
              {
                process_string_fromtab(buf,parms[2]);
                SendMessage(g_lbSectionHack,LB_INSERTSTRING,z,(LPARAM)buf);
                SendMessage(g_lbSectionHack,LB_SETITEMDATA,z,x);
                SendMessage(g_lbSectionHack,LB_SETSEL,!!(g_inst_section[x].default_state&DFS_SET),z);
              }
            }
            g_inst_section[x].name_ptr=parms[2];
          }
          else if (parms[1]==1) // get text
          {
            process_string_fromtab(g_usrvars[parms[2]],g_inst_section[x].name_ptr);
          }
          else if (parms[1]==2) // set flags
          {
            g_inst_section[x].default_state=process_string_fromtab_toint(parms[2]);
            if (g_lbSectionHack && g_inst_section[x].name_ptr>=0)
            {
              SendMessage(g_lbSectionHack,LB_SETSEL,!!(g_inst_section[x].default_state&DFS_SET),z);
            }
          }
          else // get flags
          {
            myitoa(g_usrvars[parms[2]],g_inst_section[x].default_state);
          }
        }
        else exec_errorflag++;
      }
    return 0;
#endif//NSIS_CONFIG_COMPONENTPAGE
  }
  MessageBox(g_hwnd,LANG_STR(LANG_INSTCORRUPTED),g_caption,MB_OK|MB_ICONSTOP);
  return EXEC_ERROR;
}
