; NSIS LOGIC LIBRARY - LogicLib.nsh
; Version 2.6 - 08/12/2007
; By dselkirk@hotmail.com
; and eccles@users.sf.net
; with IfNot support added by Message
;
; Questions/Comments -
; See http://forums.winamp.com/showthread.php?s=&postid=1116241
;
; Description:
;   Provides the use of various logic statements within NSIS.
;
; Usage:
;   The following "statements" are available:
;       If|IfNot|Unless..{ElseIf|ElseIfNot|ElseUnless}..[Else]..EndIf|EndUnless
;         - Conditionally executes a block of statements, depending on the value
;           of an expression. IfNot and Unless are equivalent and
;           interchangeable, as are ElseIfNot and ElseUnless.
;       AndIf|AndIfNot|AndUnless|OrIf|OrIfNot|OrUnless
;         - Adds any number of extra conditions to If, IfNot, Unless, ElseIf,
;           ElseIfNot and ElseUnless statements.
;       IfThen|IfNotThen..|..|
;         - Conditionally executes an inline statement, depending on the value
;           of an expression.
;       IfCmd..||..|
;         - Conditionally executes an inline statement, depending on a true
;           value of the provided NSIS function.
;       Select..{Case[2|3|4|5]}..[CaseElse|Default]..EndSelect
;         - Executes one of several blocks of statements, depending on the value
;           of an expression.
;       Switch..{Case|CaseElse|Default}..EndSwitch
;         - Jumps to one of several labels, depending on the value of an
;           expression.
;       Do[While|Until]..{ExitDo|Continue|Break}..Loop[While|Until]
;         - Repeats a block of statements until stopped, or depending on the
;           value of an expression.
;       While..{ExitWhile|Continue|Break}..EndWhile
;         - An alias for DoWhile..Loop (for backwards-compatibility)
;       For[Each]..{ExitFor|Continue|Break}..Next
;         - Repeats a block of statements varying the value of a variable.
;
;   The following "expressions" are available:
;       Standard (built-in) string tests (which are case-insensitive):
;         a == b; a != b
;       Additional case-insensitive string tests (using System.dll):
;         a S< b; a S>= b; a S> b; a S<= b
;       Case-sensitive string tests:
;         a S== b; a S!= b
;       Standard (built-in) signed integer tests:
;         a = b; a <> b; a < b; a >= b; a > b; a <= b; a & b
;       Standard (built-in) unsigned integer tests:
;         a U< b; a U>= b; a U> b; a U<= b
;       64-bit integer tests (using System.dll):
;         a L= b; a L<> b; a L< b; a L>= b; a L> b; a L<= b
;       ptrdiff_t integer tests
;         a P= b; a P<> b; a P< b; a P>= b; a P> b; a P<= b
;       size_t integer tests
;         a Z= b; a Z<> b; a Z< b; a Z>= b; a Z> b; a Z<= b
;       Built-in NSIS flag tests:
;         ${Abort}; ${Errors}; ${RebootFlag}; ${Silent}; ${RtlLanguage};
;         ${ShellVarContextAll}
;       Built-in NSIS other tests:
;         ${FileExists} a
;       Complex string tests:
;         a ${StartsWith} b; a ${StartsWithS} b; a ${EndsWith} b; a ${EndsWithS} b;
;         a ${Contains} b; a ${ContainsS} b; ${IsLowerCase} b; ${IsUpperCase} b
;       Any conditional NSIS instruction test:
;         ${Cmd} a
;       Section flag tests:
;         ${SectionIsSelected} a; ${SectionIsSectionGroup} a;
;         ${SectionIsSectionGroupEnd} a; ${SectionIsBold} a;
;         ${SectionIsReadOnly} a; ${SectionIsExpanded} a;
;         ${SectionIsPartiallySelected} a
;       Additional tests:
;         HK ${RegKeyIsEmpty} SubKey
;
; Examples:
;   See LogicLib.nsi in the Examples folder for lots of example usage.

!verbose push
!verbose 3
!ifndef LOGICLIB_VERBOSITY
  !define LOGICLIB_VERBOSITY 3
!endif
!define _LOGICLIB_VERBOSITY ${LOGICLIB_VERBOSITY}
!undef LOGICLIB_VERBOSITY
!verbose ${_LOGICLIB_VERBOSITY}

!ifndef LOGICLIB
  !define LOGICLIB 20260419
  !define | "'"
  !define || "' '"
  !define LOGICLIB_COUNTER 0

  !include Sections.nsh

  !macro _LOGICLIB_TEMP
    !ifndef _LOGICLIB_TEMP
      !define _LOGICLIB_TEMP
      Var /GLOBAL _LOGICLIB_TEMP  ; Temporary variable to aid the more elaborate logic tests
    !endif
  !macroend

  !macro LogicLib_JumpToBranch _Jump _Skip
    !if `${_Jump}${_Skip}` != ``
      StrCmp "" "" `${_Jump}` ${_Skip}
    !endif
  !macroend

  !macro _IncreaseCounter
    !define /redef /math LOGICLIB_COUNTER `${LOGICLIB_COUNTER}` + 1
  !macroend

  !macro _PushLogic
    !insertmacro _PushScope Logic _LogicLib_Label_${LOGICLIB_COUNTER}
    !insertmacro _IncreaseCounter
  !macroend

  !macro _PopLogic
    !insertmacro _PopScope Logic
  !macroend

  !macro _PushScope Type label
    !ifdef _${Type}                                       ; If we already have a statement
      !define _Cur${Type} ${_${Type}}
      !undef _${Type}
      !define _${Type} ${label}
      !define ${_${Type}}Prev${Type} ${_Cur${Type}}       ; Save the current logic
      !undef _Cur${Type}
    !else
      !define _${Type} ${label}                           ; Initialise for first statement
    !endif
  !macroend

  !macro _PopScope Type
    !ifndef _${Type}
      !error "Cannot use _Pop${Type} without a preceding _Push${Type}"
    !endif
    !ifdef ${_${Type}}Prev${Type}                         ; If a previous statement was active then restore it
      !define _Cur${Type} ${_${Type}}
      !undef _${Type}
      !define _${Type} ${${_Cur${Type}}Prev${Type}}
      !undef ${_Cur${Type}}Prev${Type}
      !undef _Cur${Type}
    !else
      !undef _${Type}
    !endif
  !macroend

  !macro _LogicLib_AlwaysTrue _a _b _t _f
    !insertmacro LogicLib_JumpToBranch `${_t}` `${_f}`
  !macroend
  !macro _LogicLib_AlwaysFalse _a _b _t _f
    !insertmacro LogicLib_JumpToBranch `${_f}` `${_t}`
  !macroend

  ; String tests
  !macro _== _a _b _t _f
    StrCmp `${_a}` `${_b}` `${_t}` `${_f}`
  !macroend

  !macro _!= _a _b _t _f
    !insertmacro _== `${_a}` `${_b}` `${_f}` `${_t}`
  !macroend

  ; Case-sensitive string tests
  !macro _S== _a _b _t _f
    StrCmpS `${_a}` `${_b}` `${_t}` `${_f}`
  !macroend

  !macro _S!= _a _b _t _f
    !insertmacro _S== `${_a}` `${_b}` `${_f}` `${_t}`
  !macroend

  ; Extra string tests (cannot do these case-sensitively - I tried and lstrcmp still ignored the case)
  !macro _StrCmpI _a _b _e _l _m
    !insertmacro _LOGICLIB_TEMP
    System::Call `kernel32::lstrcmpi(ts, ts) i.s` `${_a}` `${_b}`
    Pop $_LOGICLIB_TEMP
    IntCmp $_LOGICLIB_TEMP 0 `${_e}` `${_l}` `${_m}`
  !macroend

  !macro _S< _a _b _t _f
    !insertmacro _StrCmpI `${_a}` `${_b}` `${_f}` `${_t}` `${_f}`
  !macroend

  !macro _S>= _a _b _t _f
    !insertmacro _S< `${_a}` `${_b}` `${_f}` `${_t}`
  !macroend

  !macro _S> _a _b _t _f
    !insertmacro _StrCmpI `${_a}` `${_b}` `${_f}` `${_f}` `${_t}`
  !macroend

  !macro _S<= _a _b _t _f
    !insertmacro _S> `${_a}` `${_b}` `${_f}` `${_t}`
  !macroend

  ; Integer tests
  !macro _= _a _b _t _f
    IntCmp `${_a}` `${_b}` `${_t}` `${_f}` `${_f}`
  !macroend

  !macro _<> _a _b _t _f
    !insertmacro _= `${_a}` `${_b}` `${_f}` `${_t}`
  !macroend

  !macro _< _a _b _t _f
    IntCmp `${_a}` `${_b}` `${_f}` `${_t}` `${_f}`
  !macroend

  !macro _>= _a _b _t _f
    !insertmacro _< `${_a}` `${_b}` `${_f}` `${_t}`
  !macroend

  !macro _> _a _b _t _f
    IntCmp `${_a}` `${_b}` `${_f}` `${_f}` `${_t}`
  !macroend

  !macro _<= _a _b _t _f
    !insertmacro _> `${_a}` `${_b}` `${_f}` `${_t}`
  !macroend

  !macro _& _a _b _t _f
    !insertmacro _LOGICLIB_TEMP
    IntOp $_LOGICLIB_TEMP `${_a}` & `${_b}`
    !insertmacro _<> $_LOGICLIB_TEMP 0 `${_t}` `${_f}`
  !macroend

  ; Unsigned integer tests (NB: no need for extra equality tests)
  !macro _U< _a _b _t _f
    IntCmpU `${_a}` `${_b}` `${_f}` `${_t}` `${_f}`
  !macroend

  !macro _U>= _a _b _t _f
    !insertmacro _U< `${_a}` `${_b}` `${_f}` `${_t}`
  !macroend

  !macro _U> _a _b _t _f
    IntCmpU `${_a}` `${_b}` `${_f}` `${_f}` `${_t}`
  !macroend

  !macro _U<= _a _b _t _f
    !insertmacro _U> `${_a}` `${_b}` `${_f}` `${_t}`
  !macroend

  ; Int64 tests
  !macro _Int64Cmp _a _o _b _t _f
    !insertmacro _LOGICLIB_TEMP
    System::Int64Op `${_a}` `${_o}` `${_b}`
    Pop $_LOGICLIB_TEMP
    !insertmacro _= $_LOGICLIB_TEMP 0 `${_f}` `${_t}`
  !macroend

  !macro _L= _a _b _t _f
    !insertmacro _Int64Cmp `${_a}` = `${_b}` `${_t}` `${_f}`
  !macroend

  !macro _L<> _a _b _t _f
    !insertmacro _L= `${_a}` `${_b}` `${_f}` `${_t}`
  !macroend

  !macro _L< _a _b _t _f
    !insertmacro _Int64Cmp `${_a}` < `${_b}` `${_t}` `${_f}`
  !macroend

  !macro _L>= _a _b _t _f
    !insertmacro _L< `${_a}` `${_b}` `${_f}` `${_t}`
  !macroend

  !macro _L> _a _b _t _f
    !insertmacro _Int64Cmp `${_a}` > `${_b}` `${_t}` `${_f}`
  !macroend

  !macro _L<= _a _b _t _f
    !insertmacro _L> `${_a}` `${_b}` `${_f}` `${_t}`
  !macroend

  ; ptrdiff_t & size_t tests
  !macro LogicLib_PtrDiffTest _o _a _b _t _f
    !if "${NSIS_PTR_SIZE}" <= 4
      !insertmacro _${_o} `${_a}` `${_b}` `${_t}` `${_f}`
    !else
      !insertmacro _L${_o} `${_a}` `${_b}` `${_t}` `${_f}`
    !endif
  !macroend
  !macro _P= _a _b _t _f
    !insertmacro LogicLib_PtrDiffTest = `${_a}` `${_b}` `${_t}` `${_f}`
  !macroend
  !macro _P<> _a _b _t _f
    !insertmacro LogicLib_PtrDiffTest <> `${_a}` `${_b}` `${_t}` `${_f}`
  !macroend
  !macro _P< _a _b _t _f
    !insertmacro LogicLib_PtrDiffTest < `${_a}` `${_b}` `${_t}` `${_f}`
  !macroend
  !macro _P>= _a _b _t _f
    !insertmacro LogicLib_PtrDiffTest >= `${_a}` `${_b}` `${_t}` `${_f}`
  !macroend
  !macro _P> _a _b _t _f
    !insertmacro LogicLib_PtrDiffTest > `${_a}` `${_b}` `${_t}` `${_f}`
  !macroend
  !macro _P<= _a _b _t _f
    !insertmacro LogicLib_PtrDiffTest <= `${_a}` `${_b}` `${_t}` `${_f}`
  !macroend
  !include Util.nsh
  !macro _Z= _a _b _t _f
    !insertmacro LogicLib_PtrDiffTest = `${_a}` `${_b}` `${_t}` `${_f}`
  !macroend
  !macro _Z<> _a _b _t _f
    !insertmacro LogicLib_PtrDiffTest <> `${_a}` `${_b}` `${_t}` `${_f}`
  !macroend
  !macro _Z< _a _b _t _f
    !insertmacro IntPtrCmpU `${_a}` `${_b}` `${_f}` `${_t}` `${_f}`
  !macroend
  !macro _Z>= _a _b _t _f
    !insertmacro IntPtrCmpU `${_a}` `${_b}` `${_t}` `${_f}` `${_t}`
  !macroend
  !macro _Z> _a _b _t _f
    !insertmacro IntPtrCmpU `${_a}` `${_b}` `${_f}` `${_f}` `${_t}`
  !macroend
  !macro _Z<= _a _b _t _f
    !insertmacro IntPtrCmpU `${_a}` `${_b}` `${_t}` `${_t}` `${_f}`
  !macroend

  ; Flag tests
  !macro _Abort _a _b _t _f
    IfAbort `${_t}` `${_f}`
  !macroend
  !define Abort `"" Abort ""`

  !macro _Errors _a _b _t _f
    IfErrors `${_t}` `${_f}`
  !macroend
  !define Errors `"" Errors ""`

  !macro _FileExists _a _b _t _f
    IfFileExists `${_b}` `${_t}` `${_f}`
  !macroend
  !define FileExists `"" FileExists`

  !macro _RebootFlag _a _b _t _f
    IfRebootFlag `${_t}` `${_f}`
  !macroend
  !define RebootFlag `"" RebootFlag ""`

  !macro _Silent _a _b _t _f
    IfSilent `${_t}` `${_f}`
  !macroend
  !define Silent `"" Silent ""`

  !macro _ShellVarContextAll _a _b _t _f
    IfShellVarContextAll `${_t}` `${_f}`
  !macroend
  !define ShellVarContextAll `"" ShellVarContextAll ""`

  !macro _RtlLanguage _a _b _t _f
    IfRtlLanguage `${_t}` `${_f}`
  !macroend
  !define RtlLanguage `"" RtlLanguage ""`

  !macro _AltRegView _a _b _t _f
    IfAltRegView `${_t}` `${_f}`
  !macroend
  !define AltRegView `"" AltRegView ""`

  ; Complex string tests
  !macro _LL_StartsWith _op _a _b _t _f
    !insertmacro _LOGICLIB_TEMP
    StrLen $_LOGICLIB_TEMP `${_b}`
    StrCpy $_LOGICLIB_TEMP `${_a}` $_LOGICLIB_TEMP
    ${_op} $_LOGICLIB_TEMP `${_b}` `${_t}` `${_f}`
  !macroend
  !define StartsWith `"LL_StartsWith StrCmp"`
  !define StartsWithS `"LL_StartsWith StrCmpS"`

  !macro _LL_EndsWith _op _a _b _t _f
    !insertmacro _LOGICLIB_TEMP
    StrLen $_LOGICLIB_TEMP `${_b}`
    StrCpy $_LOGICLIB_TEMP `${_a}` "" -$_LOGICLIB_TEMP
    ${_op} $_LOGICLIB_TEMP `${_b}` `${_t}` `${_f}`
  !macroend
  !define EndsWith `"LL_EndsWith StrCmp"`
  !define EndsWithS `"LL_EndsWith StrCmpS"`

  !macro _LL_StrContains _op _a _b _t _f
    !insertmacro _LOGICLIB_TEMP
    Push $0
    Push $1
    Push $2
    StrCpy $0 `${_b}`
    StrCpy $1 `${_a}`
    StrLen $_LOGICLIB_TEMP $0
    _LL${LOGICLIB_COUNTER}_Loop:
      StrCpy $2 $1 $_LOGICLIB_TEMP
      StrCmp $2 "" _LL${LOGICLIB_COUNTER}_Done
      StrCpy $1 $1 "" 1
      ${_op} $2 $0 ""_LL${LOGICLIB_COUNTER}_Loop
      StrCpy $_LOGICLIB_TEMP ""
    _LL${LOGICLIB_COUNTER}_Done:
    Pop $2
    Pop $1
    Pop $0
    StrCmp $_LOGICLIB_TEMP "" `${_t}` `${_f}`
    !insertmacro _IncreaseCounter
  !macroend
  !define Contains `"LL_StrContains StrCmp"`
  !define ContainsS `"LL_StrContains StrCmpS"`

  !macro _LL_IsCase _func _a _b _t _f
    !insertmacro _LOGICLIB_TEMP
    System::Call USER32::${_func}(tss) `${_b}`
    Pop $_LOGICLIB_TEMP
    StrCmpS $_LOGICLIB_TEMP `${_b}` `${_t}` `${_f}`
  !macroend
  !define IsLowerCase `"" "LL_IsCase CharLower"`
  !define IsUpperCase `"" "LL_IsCase CharUpper"`

  ; Registry tests
  !macro _RegKeyIsEmpty _a _b _t _f
    !insertmacro _LOGICLIB_TEMP
    ClearErrors
    EnumRegValue $_LOGICLIB_TEMP ${_a} `${_b}` ""
    !if `${_f}` != ``
    IfErrors "" `${_f}` ; Skip calls to EnumRegKey and _== if possible
    !else
    IfErrors +3
    StrCpy $_LOGICLIB_TEMP "1" ; The default value is also named "", make sure we don't mistake it as empty
    Goto +2
    !endif
    EnumRegKey $_LOGICLIB_TEMP ${_a} `${_b}` ""
    !insertmacro _== $_LOGICLIB_TEMP "" `${_t}` `${_f}`
  !macroend
  !define RegKeyIsEmpty `RegKeyIsEmpty`

  ; "Any instruction" test
  !macro _Cmd _a _b _t _f
    !define _t=${_t}
    !ifdef _t=                                            ; If no true label then make one
      !define __t _LogicLib_Label_${LOGICLIB_COUNTER}
      !insertmacro _IncreaseCounter
    !else
      !define __t ${_t}
    !endif
    ${_b} ${__t}
    !define _f=${_f}
    !ifndef _f=                                           ; If a false label then go there
      Goto ${_f}
    !endif
    !undef _f=${_f}
    !ifdef _t=                                            ; If we made our own true label then place it
      ${__t}:
    !endif
    !undef __t
    !undef _t=${_t}
  !macroend
  !define Cmd `"" Cmd`

  ; Section flag test
  !macro _SectionFlagIsSet _a _b _t _f
    !insertmacro _LOGICLIB_TEMP
    SectionGetFlags `${_b}` $_LOGICLIB_TEMP
    IntOp $_LOGICLIB_TEMP $_LOGICLIB_TEMP & `${_a}`
    !insertmacro _= $_LOGICLIB_TEMP `${_a}` `${_t}` `${_f}`
  !macroend
  !define SectionIsSelected `${SF_SELECTED} SectionFlagIsSet`
  !define SectionIsSubSection `${SF_SUBSEC} SectionFlagIsSet`
  !define SectionIsSubSectionEnd `${SF_SUBSECEND} SectionFlagIsSet`
  !define SectionIsSectionGroup `${SF_SECGRP} SectionFlagIsSet`
  !define SectionIsSectionGroupEnd `${SF_SECGRPEND} SectionFlagIsSet`
  !define SectionIsBold `${SF_BOLD} SectionFlagIsSet`
  !define SectionIsReadOnly `${SF_RO} SectionFlagIsSet`
  !define SectionIsExpanded `${SF_EXPAND} SectionFlagIsSet`
  !define SectionIsPartiallySelected `${SF_PSELECTED} SectionFlagIsSet`

  !define IfCmd `!insertmacro _IfThen "" Cmd ${|}`

  !macro _If _c _a _o _b
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    !insertmacro _PushLogic
    !define ${_Logic}If
    !define ${_Logic}Else _LogicLib_ElseLabel_${LOGICLIB_COUNTER} ; Get a label for the Else
    !insertmacro _IncreaseCounter
    !define _c=${_c}
    !ifdef _c=true                                        ; If is true
      !insertmacro _${_o} `${_a}` `${_b}` "" ${${_Logic}Else}
    !else                                                 ; If condition is false
      !insertmacro _${_o} `${_a}` `${_b}` ${${_Logic}Else} ""
    !endif
    !undef _c=${_c}
    !verbose pop
  !macroend
  !define If     `!insertmacro _If true`
  !define Unless `!insertmacro _If false`
  !define IfNot  `!insertmacro _If false`

  !macro _And _c _a _o _b
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    !ifndef _Logic | ${_Logic}If
      !error "Cannot use And without a preceding If or IfNot/Unless"
    !endif
    !ifndef ${_Logic}Else
      !error "Cannot use And following an Else"
    !endif
    !define _c=${_c}
    !ifdef _c=true                                        ; If is true
      !insertmacro _${_o} `${_a}` `${_b}` "" ${${_Logic}Else}
    !else                                                 ; If condition is false
      !insertmacro _${_o} `${_a}` `${_b}` ${${_Logic}Else} ""
    !endif
    !undef _c=${_c}
    !verbose pop
  !macroend
  !define AndIf     `!insertmacro _And true`
  !define AndUnless `!insertmacro _And false`
  !define AndIfNot  `!insertmacro _And false`

  !macro _Or _c _a _o _b
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    !ifndef _Logic | ${_Logic}If
      !error "Cannot use Or without a preceding If or IfNot/Unless"
    !endif
    !ifndef ${_Logic}Else
      !error "Cannot use Or following an Else"
    !endif
    !define _label _LogicLib_Label_${LOGICLIB_COUNTER}    ; Skip this test as we already
    !insertmacro _IncreaseCounter
    Goto ${_label}                                        ; have a successful result
    ${${_Logic}Else}:                                     ; Place the Else label
    !undef ${_Logic}Else                                  ; and remove it
    !define ${_Logic}Else _LogicLib_ElseLabel_${LOGICLIB_COUNTER} ; Get a label for the next Else and perform the new If
    !insertmacro _IncreaseCounter
    !define _c=${_c}
    !ifdef _c=true                                        ; If is true
      !insertmacro _${_o} `${_a}` `${_b}` "" ${${_Logic}Else}
    !else                                                 ; If condition is false
      !insertmacro _${_o} `${_a}` `${_b}` ${${_Logic}Else} ""
    !endif
    !undef _c=${_c}
    ${_label}:
    !undef _label
    !verbose pop
  !macroend
  !define OrIf     `!insertmacro _Or true`
  !define OrUnless `!insertmacro _Or false`
  !define OrIfNot  `!insertmacro _Or false`

  !macro _Else
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    !ifndef _Logic | ${_Logic}If
      !error "Cannot use Else without a preceding If or IfNot/Unless"
    !endif
    !ifndef ${_Logic}Else
      !error "Cannot use Else following an Else"
    !endif
    !ifndef ${_Logic}EndIf                                ; First Else for this If?
      !define ${_Logic}EndIf _LogicLib_EndIfLabel_${LOGICLIB_COUNTER} ; Get a label for the EndIf
      !insertmacro _IncreaseCounter
    !endif
    Goto ${${_Logic}EndIf}                                ; Go to the EndIf
    ${${_Logic}Else}:                                     ; Place the Else label
    !undef ${_Logic}Else                                  ; and remove it
    !verbose pop
  !macroend
  !define Else `!insertmacro _Else`

  !macro _ElseIf _c _a _o _b
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    ${Else}                                               ; Perform the Else
    !define ${_Logic}Else _LogicLib_ElseLabel_${LOGICLIB_COUNTER} ; Get a label for the next Else and perform the new If
    !insertmacro _IncreaseCounter
    !define _c=${_c}
    !ifdef _c=true                                        ; If is true
      !insertmacro _${_o} `${_a}` `${_b}` "" ${${_Logic}Else}
    !else                                                 ; If condition is false
      !insertmacro _${_o} `${_a}` `${_b}` ${${_Logic}Else} ""
    !endif
    !undef _c=${_c}
    !verbose pop
  !macroend
  !define ElseIf     `!insertmacro _ElseIf true`
  !define ElseUnless `!insertmacro _ElseIf false`
  !define ElseIfNot  `!insertmacro _ElseIf false`

  !macro _EndIf _n
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    !ifndef _Logic | ${_Logic}If
      !error "Cannot use End${_n} without a preceding If or IfNot/Unless"
    !endif
    !ifdef ${_Logic}Else
      ${${_Logic}Else}:                                   ; Place the Else label
      !undef ${_Logic}Else                                ; and remove it
    !endif
    !ifdef ${_Logic}EndIf
      ${${_Logic}EndIf}:                                  ; Place the EndIf
      !undef ${_Logic}EndIf                               ; and remove it
    !endif
    !undef ${_Logic}If
    !insertmacro _PopLogic
    !verbose pop
  !macroend
  !define EndIf     `!insertmacro _EndIf If`
  !define EndUnless `!insertmacro _EndIf Unless`

  !macro _IfThen _a _o _b _t
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    ${If} `${_a}` `${_o}` `${_b}`
      ${_t}
    ${EndIf}
    !verbose pop
  !macroend
  !define IfThen `!insertmacro _IfThen`

  !macro _IfNotThen _a _o _b _t
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    ${IfNot} `${_a}` `${_o}` `${_b}`
      ${_t}
    ${EndIf}
    !verbose pop
  !macroend
  !define IfNotThen `!insertmacro _IfNotThen`

  !macro _ForEach _v _f _t _o _s
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    StrCpy "${_v}" "${_f}"                                ; Assign the initial value
    Goto +2                                               ; Skip the loop expression for the first iteration
    !define _DoLoopExpression `IntOp "${_v}" "${_v}" "${_o}" "${_s}"` ; Define the loop expression
    !define _o=${_o}
    !ifdef _o=+                                           ; Check the loop expression operator
      !define __o >                                       ; to determine the correct loop condition
    !else ifdef _o=-
      !define __o <
    !else
      !error "Unsupported ForEach step operator (must be + or -)"
    !endif
    !undef _o=${_o}
    !insertmacro _Do For false `${_v}` `${__o}` `${_t}`   ; Let Do do the rest
    !undef __o
    !verbose pop
  !macroend
  !define ForEach `!insertmacro _ForEach`

  !macro _For _v _f _t
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    ${ForEach} `${_v}` `${_f}` `${_t}` + 1                ; Pass on to ForEach
    !verbose pop
  !macroend
  !define For `!insertmacro _For`

  !define ExitFor `!insertmacro _Goto ExitFor For`

  !define Next      `!insertmacro _Loop For Next "" "" "" ""`

  !define While     `!insertmacro _Do While true`

  !define ExitWhile `!insertmacro _Goto ExitWhile While`

  !define EndWhile  `!insertmacro _Loop While EndWhile "" "" "" ""`

  !macro _Do _n _c _a _o _b
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    !insertmacro _PushLogic
    !define ${_Logic}${_n} _LogicLib_Label_${LOGICLIB_COUNTER} ; Get a label for the start of the loop
    !insertmacro _IncreaseCounter
    ${${_Logic}${_n}}:
    !insertmacro _PushScope Exit${_n} _LogicLib_Label_${LOGICLIB_COUNTER} ; Get a label for the end of the loop
    !insertmacro _IncreaseCounter
    !insertmacro _PushScope Break ${_Exit${_n}}           ; Break goes to the end of the loop
    !ifdef _DoLoopExpression
      ${_DoLoopExpression}                                ; Special extra parameter for inserting code
      !undef _DoLoopExpression                            ; between the Continue label and the loop condition
    !endif
    !define _c=${_c}
    !ifdef _c=                                            ; No starting condition
      !insertmacro _PushScope Continue _LogicLib_Label_${LOGICLIB_COUNTER} ; Get a label for Continue at the end of the loop
      !insertmacro _IncreaseCounter
    !else
      !insertmacro _PushScope Continue ${${_Logic}${_n}}  ; Continue goes to the start of the loop
      !ifdef _c=true                                      ; If is true
        !insertmacro _${_o} `${_a}` `${_b}` "" ${_Exit${_n}}
      !else                                               ; If condition is false
        !insertmacro _${_o} `${_a}` `${_b}` ${_Exit${_n}} ""
      !endif
    !endif
    !undef _c=${_c}
    !define ${_Logic}Condition ${_c}                      ; Remember the condition used
    !verbose pop
  !macroend
  !define Do      `!insertmacro _Do Do "" "" "" ""`
  !define DoWhile `!insertmacro _Do Do true`
  !define DoUntil `!insertmacro _Do Do false`

  !macro _Goto _n _s
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    !ifndef _${_n}
      !error "Cannot use ${_n} without a preceding ${_s}"
    !endif
    Goto ${_${_n}}
    !verbose pop
  !macroend
  !define ExitDo   `!insertmacro _Goto ExitDo Do`

  !macro _Loop _n _e _c _a _o _b
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    !ifndef _Logic | ${_Logic}${_n}
      !error "Cannot use ${_e} without a preceding ${_n}"
    !endif
    !define _c=${${_Logic}Condition}
    !ifdef _c=                                            ; If Do had no condition place the Continue label
      ${_Continue}:
    !endif
    !undef _c=${${_Logic}Condition}
    !define _c=${_c}
    !ifdef _c=                                            ; No ending condition
      Goto ${${_Logic}${_n}}
    !else ifdef _c=true                                   ; If condition is true
      !insertmacro _${_o} `${_a}` `${_b}` ${${_Logic}${_n}} ${_Exit${_n}}
    !else                                                 ; If condition is false
      !insertmacro _${_o} `${_a}` `${_b}` ${_Exit${_n}} ${${_Logic}${_n}}
    !endif
    !undef _c=${_c}
    Goto ${_Continue}                                     ; Just to ensure it is referenced at least once
    Goto ${_Exit${_n}}                                    ; Just to ensure it is referenced at least once
    ${_Exit${_n}}:                                        ; Place the loop exit point
    !undef ${_Logic}Condition
    !insertmacro _PopScope Continue
    !insertmacro _PopScope Break
    !insertmacro _PopScope Exit${_n}
    !undef ${_Logic}${_n}
    !insertmacro _PopLogic
    !verbose pop
  !macroend
  !define Loop      `!insertmacro _Loop Do Loop "" "" "" ""`
  !define LoopWhile `!insertmacro _Loop Do LoopWhile true`
  !define LoopUntil `!insertmacro _Loop Do LoopUntil false`

  !define Continue `!insertmacro _Goto Continue "For or Do or While"`
  !define Break    `!insertmacro _Goto Break "For or Do or While"`

  !macro _Select _a
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    !insertmacro _PushLogic
    !define ${_Logic}Select `${_a}`                       ; Remember the left hand side of the comparison
    !verbose pop
  !macroend
  !define Select `!insertmacro _Select`

  !macro _Select_CaseElse
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    !ifndef _Logic | ${_Logic}Select
      !error "Cannot use Case without a preceding Select"
    !endif
    !ifdef ${_Logic}EndSelect                             ; This is set only after the first case
      !ifndef ${_Logic}Else
        !error "Cannot use Case following a CaseElse"
      !endif
      Goto ${${_Logic}EndSelect}                          ; Go to EndSelect (Ends the previous Case)
      !define /IfNDef _LogicLib_EndSelectLabelUsed_${_Logic}
      ${${_Logic}Else}:                                   ; Place the Else label
      !undef ${_Logic}Else                                ; and remove it
    !else
      !define ${_Logic}EndSelect _LogicLib_EndSelectLabel_${LOGICLIB_COUNTER} ; Get a label for the EndSelect
      !insertmacro _IncreaseCounter
    !endif
    !verbose pop
  !macroend
  !define CaseElse `!insertmacro _CaseElse`
  !define Case_Else `!insertmacro _CaseElse`              ; Compatibility with 2.2 and earlier
  !define Default `!insertmacro _CaseElse`                ; For the C-minded

  !macro _Select_Case _a
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    ${CaseElse}                                           ; Perform the CaseElse
    !define ${_Logic}Else _LogicLib_NextSelectCaseLabel_${LOGICLIB_COUNTER} ; Get a label for the next Else and perform the new Case
    !insertmacro _IncreaseCounter
    !insertmacro _== `${${_Logic}Select}` `${_a}` "" ${${_Logic}Else}
    !verbose pop
  !macroend
  !define Case `!insertmacro _Case`

  !macro _Case2 _a _b
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    ${CaseElse}                                           ; Perform the CaseElse
    !define ${_Logic}Else _LogicLib_NextSelectCaseLabel_${LOGICLIB_COUNTER} ; Get a label for the next Else and perform the new Case
    !insertmacro _IncreaseCounter
    !insertmacro _== `${${_Logic}Select}` `${_a}` +2 ""
    !insertmacro _== `${${_Logic}Select}` `${_b}` "" ${${_Logic}Else}
    !verbose pop
  !macroend
  !define Case2 `!insertmacro _Case2`

  !macro _Case3 _a _b _c
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    ${CaseElse}                                           ; Perform the CaseElse
    !define ${_Logic}Else _LogicLib_NextSelectCaseLabel_${LOGICLIB_COUNTER} ; Get a label for the next Else and perform the new Case
    !insertmacro _IncreaseCounter
    !insertmacro _== `${${_Logic}Select}` `${_a}` +3 ""
    !insertmacro _== `${${_Logic}Select}` `${_b}` +2 ""
    !insertmacro _== `${${_Logic}Select}` `${_c}` "" ${${_Logic}Else}
    !verbose pop
  !macroend
  !define Case3 `!insertmacro _Case3`

  !macro _Case4 _a _b _c _d
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    ${CaseElse}                                           ; Perform the CaseElse
    !define ${_Logic}Else _LogicLib_NextSelectCaseLabel_${LOGICLIB_COUNTER} ; Get a label for the next Else and perform the new Case
    !insertmacro _IncreaseCounter
    !insertmacro _== `${${_Logic}Select}` `${_a}` +4 ""
    !insertmacro _== `${${_Logic}Select}` `${_b}` +3 ""
    !insertmacro _== `${${_Logic}Select}` `${_c}` +2 ""
    !insertmacro _== `${${_Logic}Select}` `${_d}` "" ${${_Logic}Else}
    !verbose pop
  !macroend
  !define Case4 `!insertmacro _Case4`

  !macro _Case5 _a _b _c _d _e
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    ${CaseElse}                                           ; Perform the CaseElse
    !define ${_Logic}Else _LogicLib_NextSelectCaseLabel_${LOGICLIB_COUNTER} ; Get a label for the next Else and perform the new Case
    !insertmacro _IncreaseCounter
    !insertmacro _== `${${_Logic}Select}` `${_a}` +5 ""
    !insertmacro _== `${${_Logic}Select}` `${_b}` +4 ""
    !insertmacro _== `${${_Logic}Select}` `${_c}` +3 ""
    !insertmacro _== `${${_Logic}Select}` `${_d}` +2 ""
    !insertmacro _== `${${_Logic}Select}` `${_e}` "" ${${_Logic}Else}
    !verbose pop
  !macroend
  !define Case5 `!insertmacro _Case5`

  !macro _EndSelect
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    !ifndef _Logic | ${_Logic}Select
      !error "Cannot use EndSelect without a preceding Select"
    !endif
    !ifdef ${_Logic}Else
      ${${_Logic}Else}:                                   ; Place the Else label
      !undef ${_Logic}Else                                ; and remove it
    !endif
    !ifdef ${_Logic}EndSelect                             ; This won't be set if there weren't any cases
      !ifdef _LogicLib_EndSelectLabelUsed_${_Logic}                 ; There is no jump to ${${_Logic}EndSelect}: if there is only one Case
        ${${_Logic}EndSelect}:                            ; Place the EndSelect
        !undef _LogicLib_EndSelectLabelUsed_${_Logic}
      !endif
      !undef ${_Logic}EndSelect                           ; and remove it
    !endif
    !undef ${_Logic}Select
    !insertmacro _PopLogic
    !verbose pop
  !macroend
  !define EndSelect `!insertmacro _EndSelect`

  !macro _Switch _a
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    !insertmacro _PushLogic
    !insertmacro _PushScope Switch ${_Logic}              ; Keep a separate stack for switch data
    !insertmacro _PushScope Break _LogicLib_Label_${LOGICLIB_COUNTER} ; Get a label for beyond the end of the switch
    !insertmacro _IncreaseCounter
    !define ${_Switch}Var `${_a}`                         ; Remember the left hand side of the comparison
    !ifdef LL_NOAPPENDMEMFILE
    !tempfile ${_Switch}Tmp                               ; Create a temporary file
    !if "${NSIS_CHAR_SIZE}" > 1
      !appendfile /CHARSET=UTF8SIG "${${_Switch}Tmp}" ""  ; Make sure the file uses Unicode
    !endif
    !else
    !appendmemfile ${_Switch}Tmp                          ; Create a temporary file
    !endif
    !define ${_Logic}Switch _LogicLib_Label_${LOGICLIB_COUNTER} ; Get a label for the end of the switch
    !insertmacro _IncreaseCounter
    Goto ${${_Logic}Switch}                               ; and go there
    !verbose pop
  !macroend
  !define Switch `!insertmacro _Switch`

  !macro _Case _a
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    !ifdef _Logic & ${_Logic}Select                       ; Check for an active Select
      !insertmacro _Select_Case `${_a}`
    !else ifndef _Switch                                  ; If not then check for an active Switch
      !error "Cannot use Case without a preceding Select or Switch"
    !else
      !define _label _LogicLib_Label_${LOGICLIB_COUNTER}  ; Get a label for this case,
      !insertmacro _IncreaseCounter
      ${_label}:                                          ; place it and add it's check to the temp file
      !ifdef LL_NOAPPENDMEMFILE
      !appendfile "${${_Switch}Tmp}" `!insertmacro _== $\`${${_Switch}Var}$\` $\`${_a}$\` ${_label} ""$\n`
      !else
      !appendmemfile ${_Switch}Tmp `!insertmacro _== $\`${${_Switch}Var}$\` $\`${_a}$\` ${_label} ""$\n`
      !endif
      !undef _label
    !endif
    !verbose pop
  !macroend

  !macro _CaseElse
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    !ifdef _Logic & ${_Logic}Select                       ; Check for an active Select
      !insertmacro _Select_CaseElse
    !else ifndef _Switch                                  ; If not then check for an active Switch
      !error "Cannot use Case without a preceding Select or Switch"
    !else ifdef ${_Switch}Else                            ; Already had a default case?
      !error "Cannot use CaseElse following a CaseElse"
    !else
      !define ${_Switch}Else _LogicLib_Label_${LOGICLIB_COUNTER} ; Get a label for the default case,
      !insertmacro _IncreaseCounter
      ${${_Switch}Else}:                                  ; and place it
    !endif
    !verbose pop
  !macroend

  !macro _EndSwitch
    !verbose push
    !verbose ${LOGICLIB_VERBOSITY}
    !ifndef _Logic | ${_Logic}Switch
      !error "Cannot use EndSwitch without a preceding Switch"
    !endif
    Goto ${_Break}                                        ; Skip the jump table
    ${${_Logic}Switch}:                                   ; Place the end of the switch
    !undef ${_Logic}Switch
    !ifdef LL_NOAPPENDMEMFILE
    !include "${${_Switch}Tmp}"                           ; Include the jump table
    !delfile "${${_Switch}Tmp}"                           ; and delete it
    !undef ${_Switch}Tmp
    !else
    !include /memfile ${_Switch}Tmp                       ; Include the jump table
    !appendmemfile '' ${_Switch}Tmp                       ; and delete it
    !endif
    !ifdef ${_Switch}Else                                 ; Was there a default case?
      Goto ${${_Switch}Else}                              ; then go there if all else fails
      !undef ${_Switch}Else
    !endif
    !undef ${_Switch}Var
    ${_Break}:                                            ; Place the break label
    !insertmacro _PopScope Break
    !insertmacro _PopScope Switch
    !insertmacro _PopLogic
    !verbose pop
  !macroend
  !define EndSwitch `!insertmacro _EndSwitch`

!endif ; LOGICLIB
!verbose 3
!define LOGICLIB_VERBOSITY ${_LOGICLIB_VERBOSITY}
!undef _LOGICLIB_VERBOSITY
!verbose pop
