;; 
;;=====================================================================================----- 
;; 
;;FUNCTION       Sanity() 
;; 
;;ACTION         Performs a "sanity check" on KiX scripts 
;; 
;;AUTHOR         Glenn Barnas / Inno-Tech Consulting Group 
;; 
;;VERSION	 1.4 - 2019-09-17 
;; 
;;HISTORY	 1.1 - 2007-02-12 - initial public release 
;;               1.2 - 2009-03-31 - added If/While/Do/For/Select processing 
;;               1.3 - 2009-11-05 - enhanced detection of unbalanced functions 
;;               1.4 - 2019-09-17 - added Flags to suppress var checking and other 
;;                                  controls when using modular code. 
;; 
;;SYNTAX         Sanity( ScriptName [, LogName] [, Flags] ) 
;; 
;;PARAMETERS     ScriptName - REQUIRED, Name of script to review 
;; 
;;               LogName    - OPTIONAL, Name of file to write log info to 
;; 
;;               Flags      - OPTIONAL,  
;; 
;;REMARKS        Developed as part of KGen, the Kix Script Generator, this 
;;               UDF performs basic sanity checks and reports the results. 
;;                * Generate list of Declared VarNames - Global, Main, & per UDF 
;;                * Identify VarNames declared by both DIM and GLOBAL 
;;                * Identify VarNames that were not declared 
;;                * Identify VarNamed Declared but not used 
;;                * Identify suspected use of Vars or Macros in strings 
;;                * Identify suspected mismatched single & double quotes 
;;                * Identify suspected mismatched parens 
;;                * Identify suspected mismatched If/EndIf statements 
;;                * Identify suspected mismatched While/Loop statements 
;;                * Identify suspected mismatched Do/Until statements 
;;                * Identify suspected mismatched For/Next statements 
;;                * Identify suspected mismatched Select/EndSelect statements 
;; 
;;		 Creates three files: 
;;		  VarInfo.ini	 listing of vars by funciton 
;;		  Warnings.csv	 CSV format listing of generated warnings 
;;		  codemap.txt	 A Map of the structure of the script w/ warnings 
;;				 about unbalanced If, While, Do, For, and Select statements 
;; 
;;		  This UDF generates screen output listing all syntax warnings.  
;;		  The same information is optionally written to the report file. 
;; 
;;RETURNS        Nothing 
;; 
;;DEPENDENCIES   L_Msg(), WriteMap() (included in this file) 
;; 
;;TESTED WITH    W2K, WXP, W2K3, W2K8, Vista 
;; 
;;EXAMPLES       Sanity('myfile.kix') 
; 
Function Sanity($_File, OPTIONAL $_Log, OPTIONAL $_Flags)
 
  Dim $_, $_X, $_Y				; temp/index vars 
  Dim $_Line					; line being processed 
  Dim $_Ctr					; line counter 
  Dim $_SQF					; Single Quote flag/counter 
  Dim $_DQF					; Double Quote flag/counter 
  Dim $_PF					; Paren flag/counter 
  Dim $_FnF					; Function flag/counter 
  Dim $_IE					; If error counter 
  Dim $_WE					; While error counter 
  Dim $_DE					; Do error counter 
  Dim $_FE					; For error counter 
  Dim $_SE					; Select error counter 
  Dim $_IF					; If flag/counter 
  Dim $_WF					; While flag/counter 
  Dim $_DF					; Do flag/counter 
  Dim $_FF					; For flag/counter 
  Dim $_SF					; Select flag/counter 
  Dim $_aIOpen[5,41], $_aWOpen[5,41]		; Open If/While counter 
  Dim $_aDOpen[5,41], $_aSOpen[5,41]		; Open Do/Select counter 
  Dim $_aFOpen[5,41]				; Open For counter 
  Dim $_VF					; Var flag 
  Dim $_Ind					; Indent size 
  Dim $_DT					; Declaration Type 
  Dim $_Fn					; active function name 
  Dim $_Vt					; Varname terminator list 
  Dim $_C					; char value 
  Dim $_P					; Pointer var 
  Dim $_E					; Working var 
  Dim $_ShowLine				; Flag to show error line 
  Dim $_G					; Global var position 
  Dim $_L					; Local var position 
  Dim $_ECt					; Error count 
  Dim $_Pad					; pad string 
  Dim $_SectList				; List of sections to enumerate 
  Dim $_KeyList					; List of Keys to enumerate 
  Dim $_Tag					; loop control var 
  Dim $_Var, $_Vars				; Array of varnames, enumerator 
  Dim $_FData[0]				; source data array 
  Dim $_IgnoreNext				; flag set to ignore next error 
 
  $_Ctr = 0	; Line Counter 
  $_SQF = 0	; Single Quote Flag 
  $_DQF = 0	; Double Quote Flag 
  $_VF  = 0	; Variable Flag 
  $_FnF = 0	; Function Flag 
  $_PF  = 0	; Paren Flag 
  $_ECt = 0
  $_Fn  = 'Main'	
  $_Pad = '                                        '	; 40 spaces 
  ; $_Vt defines characters not allowed in a variable name (denoting the end of a var name!) 
  $_Vt  = "-+\,.?&=()[]<>"+Chr(9)+Chr(32)+Chr(34)+Chr(36)+Chr(39)+Chr(64)
 
  ; Start with a fresh index and map files 
  Del '.\VarInfo.ini'
  Del '.\codemap.txt'
 
;============================================================================================= 
; Open & load the source file into an array 
;============================================================================================= 
  $_Ctr = -1
  If Open(5, $_File, 2) = 0
    $_Line = Trim(ReadLine(5))
    While Not @ERROR
      $_Ctr = $_Ctr + 1
      ReDim Preserve $_FData[$_Ctr]
      $_FData[$_Ctr] = $_Line
      $_Line = Trim(ReadLine(5))
    Loop
    $_ = Close(5)
  EndIf
 
 
 
;============================================================================================= 
; PASS 1 
; Parse the file data, looking for all function and variable declaration statements.  
; Variable declaration statements can span multiple lines (which are consolidated in 
; the array for further processing). This pass prepares the raw data for further  
; processing. Note that when a reference is made to a consolidated line, it is made  
; to the first line of reference (Declaration Line), and not the actual line in the  
; source file. 
;============================================================================================= 
 
  $_Ctr = 0							; init array/line pointer 
  While $_Ctr <= UBound($_FData)
    $_Line = $_FData[$_Ctr]
    $_Ctr = $_Ctr + 1						; data is 0 indexed, references are 1 indexed 
 
    'Pass 1 : '$_Ctr '         ' CHR(13)			; simple progress indicator 
 
    $_DT = 0							; Start with _DT undefined 
    $_E  = 0
 
    If $_Line <> ''						; skip blank lines 
 
      ; Check for "dim $" and "global $" inside other code, and convert line to place 
      ; the declaration on the left edge of the line so the processing that follows 
      ; will find it properly.  
      $_L = InStr($_Line, ' Dim ' + Chr(36))
      $_G = InStr($_Line, ' Global ' + Chr(36))
      If $_L > 1 Or $_G > 1					; only 1 will be found 
        $_SQF = 0 $_DQF = 0
        ; read from beginning of line to declaration, setting quote flags and checking 
        ; for comments Ignore the declaration if it exists within quotes, or in a comment 
        For $_P = 1 to $_L + $_G				; combine 
          $_C = SubStr($_Line, $_P, 1)
          Select
            ; set the quote flags: 0=not active, 1=active alone, -1=active inside other quote 
            Case $_C = Chr(34) And Not $_DQF			; opening double quote 
              If $_SQF $_DQF = -1 Else $_DQF = 1 EndIF
            Case $_C = Chr(34) And $_DQF			; closing double quote 
              $_DQF = 0
 
            Case $_C = Chr(39) And Not $_SQF			; opening single quote 
              If $_DQF <> 0 $_SQF = -1 Else $_SQF = 1 EndIF
            Case $_C = Chr(39) And $_SQF			; closing single quote 
              $_SQF = 0
 
            Case $_C = ';' And $_SQF = 0 And $_DQF = 0		; comment - ignore rest of line 
              $_E = 1
          EndSelect
        Next
 
        ; If a one quote (any type) or a comment tag exist before the declaration, 
        ; it isn't valid process the line if none of the flags are active 
        If $_DQF =  0 And $_SQF = 0 And $_E = 0			; Declaration is valid 
          $_ = Split(SubStr($_Line, $_L + $_G + 1), ' ')	; break into words, first is "Global" 
          $_Line = $_[0] + ' '
          For $_P = 1 to UBound($_)
            If Left($_[$_P], 1) = Chr(36)			; If first char is $, add to line 
              $_Line = $_Line + $_[$_P]
            EndIF
          Next
        EndIF	; active flag 
 
      EndIf	; Dim or Global in code 
 
      ; Line is ready for evaluation - find function, dim, and global declarations 
 
      Select
 
        ; =============================================================================== 
        ; Check for function Start 
        Case InStr($_Line, 'Function ') = 1
          $_Fn = Trim(Split(SubStr($_Line, 10), '(')[0])
          ; warn if defining a function without terminating the prior definition 
          If $_FnF
            L_Msg('Recursive function definition',
             'Prior function definition may not be properly terminated.',
             '', $_Fn, $_Ctr, $_Log)
            L_Msg(Right('        ' + CStr($_Ctr) + ': ', 9) + $_Line, '', '', '', '', $_Log)
            $_ECt = $_ECt + 1
          EndIF
 
          ; define the reference to the function, and turn on the active Function Flag 
          $_FnF = 1
          $_ = WriteProfileString('.\VarInfo.ini', $_Fn, 'DefinedOnLine', $_Ctr)
 
          ; include the Fn name as a declared var 
          $_ = WriteProfileString('.\VarInfo.ini', $_Fn, cHR(36) + $_Fn, '1,' + CStr($_Ctr))
 
          ; mark any passed arguments as defined variables 
          $_Vars = Join(Split(Split(Split($_Line, Chr(40))[1], Chr(41))[0], ' '), '')
          If $_Vars <> ''
            For Each $_Var in Split($_Vars, ',')
              If Left($_Var, 9) = 'optional' + Chr(36)		; check for "Optional $VarName" 
                $_Var = SubStr($_Var, 9)
              EndIf
              If Trim($_Var) <> 'Optional'			; exclude "optional" tags 
                $_ = WriteProfileString('.\VarInfo.ini', $_Fn, Trim($_Var), '1,' + CStr($_Ctr))
              EndIf
            Next
          EndIF
        ; Function ? 
 
        ; Check for Function End 
        Case InStr($_Line, 'EndFunction') = 1
          $_FnF = 0
          $_Fn = 'Main'
        ; EndFunction ? 
        
 
        ; =============================================================================== 
        ; Check for Variable Declaration statements - can be one of: 
        ;  
        ; Declare Varname, varname...		(type 1/2) 
        ;  
        ; Declare Varname, varname... ,		(type 3/4) 
        ; more_var_names..., last_var_name 
        ;  
        ; Declare 				(type 5/6) 
        ; Varname, varname... , 
        ; more_var_names..., last_var_name 
        ;  
        ; "Declare" can be "Global" or "Dim" 
 
        Case InStr($_Line, 'Global') = 1			; found a GLOBAL declaration 
          $_Line = Trim(Split($_Line, ';')[0])			; remove comment portion 
          Select
            Case $_Line = 'Global'				; type 5 
              $_DT = 5
              $_Line = $_Line + ' '
            Case Right($_Line, 1) = ','				; type 3 
              $_DT = 3
            Case 1						; type 1 
              $_DT = 1
          EndSelect
 
        Case InStr($_Line, 'Dim') = 1
          $_Line = Trim(Split($_Line, ';')[0])			; remove comment portion 
          Select
            Case $_Line = 'Dim'					; type 6 
              $_DT = 6
              $_Line = $_Line + ' '
            Case Right($_Line, 1) = ','				; type 4 
              $_DT = 4
            Case 1						; type 2 
              $_DT = 2
          EndSelect
 
      EndSelect
 
 
      ; If $_DT > 2, source lines are split. 
      ; collect & combine the following lines until none end in ',' before continuing 
      ; The data in the array source is combined - this is the  
      If $_DT > 2
        $_ = $_Ctr - 1						; remember where the declaration started 
        Do
          $_Line = $_Line + $_FData[$_Ctr]
          $_FData[$_Ctr] = ''					; clear the extended source line 
          $_Ctr = $_Ctr + 1
        Until Right($_Line, 1) <> ','
        $_FData[$_] = $_Line					; write the combined source line 
      EndIf
 
      ; Lines are combined, process either basic DIM or GLOBAL statements 
      If $_DT = 2 Or $_DT = 4 Or $_DT = 6			; DIM 
      ;  $_Line = Split($_Line, ';')[0]				; remove comment portion 
        $_E = Len($_Line)
        For $_P = 5 to $_E
          $_C = SubStr($_Line, $_P, 1)
 
          Select
            Case $_C = Chr(36) And $_VF = 0			; found "$" 
              $_Var = $_C
              If $_P = $_E					; trap for "Dim $" by itself 
                $_VF = 0
                $_ = WriteProfileString('.\VarInfo.ini', $_Fn, Trim($_Var), '1,' + CStr($_Ctr))
              Else
                $_VF = 1
              EndIf
 
            Case $_VF = 1 And (InStr($_Vt, $_C) Or $_P = $_E)	; terminating char 
              $_VF = 0
              If $_P = $_E And Not InStr($_Vt, $_C)		; EOL - add last char if needed 
                $_Var = $_Var + $_C
              EndIf
              $_ = WriteProfileString('.\VarInfo.ini', $_Fn, Trim($_Var), '1,' + CStr($_Ctr))
 
              If $_C = '[' $_VF = -1 EndIf			; handle arrays 
 
            Case $_VF = -1					; skip chars between [ and ] 
              If $_C = ']' $_VF = 0 EndIf
 
            Case $_VF = 1 And Not InStr($_Vt, $_C)		; build var name 
              $_Var = $_Var + $_C
          
          EndSelect
        Next
      EndIf
 
      If $_DT = 1 Or $_DT = 3 Or $_DT = 5			; GLOBAL 
      ;  $_Line = Split($_Line, ';')[0]				; remove comment portion 
        $_E = Len($_Line)
        For $_P = 8 to $_E
          $_C = SubStr($_Line, $_P, 1)
 
          Select
            Case $_C = Chr(36) And $_VF = 0			; found "$" 
              $_Var = $_C
              If $_P = $_E					; trap for "Global $" by itself (rare) 
                $_VF = 0
                $_ = WriteProfileString('.\VarInfo.ini', 'Global', Trim($_Var), '1,' + CStr($_Ctr))
              Else
                $_VF = 1
              EndIf
 
            Case $_VF = 1 And (InStr($_Vt, $_C) Or $_P = $_E)	; terminating char 
              $_VF = 0
              If $_P = $_E And Not InStr($_Vt, $_C)		; EOL - add last char if needed 
                $_Var = $_Var + $_C
              EndIf
              $_ = WriteProfileString('.\VarInfo.ini', 'Global', Trim($_Var), '1,' + CStr($_Ctr))
              If $_C = '['					; handle arrays 
                $_VF = -1
              EndIf
 
            Case $_VF = -1					; skip chars between [ and ] 
              If $_C = ']'
                $_VF = 0
              EndIF
 
            Case $_VF = 1 And Not InStr($_Vt, $_C)		; build var name 
              $_Var = $_Var + $_C
          
          EndSelect
        Next
      EndIf
 
 
    EndIf	; $_Line <> '' 
 
  Loop
  @CRLF
 
;============================================================================================= 
; PASS 2 
; Enumerate the INI file and check for vars that have been declared both as local and global 
;============================================================================================= 
 
  'Pass 2 ' 
  $_SectList = ReadProfileString('.\VarInfo.ini', '', '')
  $_SectList = Split(Left($_SectList,len($_SectList)-1), Chr(10))
  For Each $_Fn in $_SectList
    '.'
    If $_Fn <> 'Global'
      $_KeyList = ReadProfileString('.\VarInfo.ini', $_Fn, '')
      $_KeyList = Split(Left($_KeyList ,len($_KeyList)-1), Chr(10))
      For Each $_Var in $_KeyList
        $_ = ReadProfileString('.\VarInfo.ini', 'Global', $_Var)
        If $_ <> ''
          $_P = Split(ReadProfileString('.\VarInfo.ini', $_Fn, $_Var), ',')[1]
          $_  = Split(ReadProfileString('.\VarInfo.ini', 'Global', $_Var), ',')[1]
          L_Msg('Variable declared multiple times.', 'Previously declared as GLOBAL on line ' + CStr($_) + '.', $_Var, $_Fn, $_P, $_Log)
          $_ECt = $_ECt + 1          
        EndIf
      Next
    EndIf
  Next
  @CRLF
 
 
;============================================================================================= 
; PASS 3 
; Scan the data and identify the variables that are used, verify that they are declared, and 
; check for variable and macro usage inside of strings 
;============================================================================================= 
 
  ; reset the line counter for the third pass 
  $_Ctr = 0
  $_Fn  = 'Main'
  $_VF  = 0	; Variable Flag 
  $_FnF = 0	; Function Flag 
 
 
  While $_Ctr <= UBound($_FData)
    $_Line = $_FData[$_Ctr]					; get working line 
    $_Ctr = $_Ctr + 1						; increment counter 
    'Pass 3 : '$_Ctr '         ' CHR(13)			; simple progress indicator 
 
    If $_IgnoreNext > 0
      $_IgnoreNext = $_IgnoreNext - 1
    EndIf
 
 
 
    If $_Line <> ''
 
      $_SQF = 0							; init per-line flags 
      $_DQF = 0
      $_PF  = 0
 
      ; =============================================================================== 
      ; filter out lines we don't want to process char by char 
 
      Select
      ; Check for function Start / End to get name to reference 
      Case InStr($_Line, 'Function') = 1
        $_Fn = Trim(Split(SubStr($_Line, 10), Chr(40))[0])	; set active function name 
 
      Case InStr($_Line, 'EndFunction') = 1
        $_Fn = 'Main'						; default active function to "main" 
 
      Case InStr($_Line, 'IsDeclared')				; do nothing - just ignore this line 
 
      Case InStr($_Line, ';KGEN:IGNORENEXT')			; ignore error on next line 
       $_IgnoreNext = 2
 
      Case $_IgnoreNext = 1					; ignore this line 
 
      Case 1							; valid line to parse 
 
        ; =============================================================================== 
        ; parse the line, char by char, looking for varnames, quotes, parens, and comments 
 
        $_VF = 0						; clear the var active flag 
 
        $_E = Len($_Line)					; line end pointer 
        For $_P = 1 to $_E					; char pointer 
          $_C = SubStr($_Line, $_P, 1)				; get the active char 
 
          ; =============================================================================== 
          ; set the quote flags: 0=not active, 1=active alone, -1=active inside other quote 
          If $_C = Chr(34) Or $_C = Chr(39)
            Select
            Case $_C = Chr(34) And $_DQF = 0			; opening double quote 
              $_DQF = IIf($_SQF <> 0, -1, 1)			; process embedded quotes 
            Case $_C = Chr(34) And $_DQF <> 0			; closing double quote 
              $_DQF = 0
              If $_SQF = -1 $_SQF = 0 EndIf
 
            Case $_C = Chr(39) And $_SQF = 0			; opening single quote 
              $_SQF = IIf($_DQF <> 0, -1, 1)			; process embedded quotes 
            Case $_C = Chr(39) And $_SQF <> 0			; closing single quote 
              $_SQF = 0
              If $_DQF = -1 $_DQF = 0 EndIf
            EndSelect
          EndIf
 
          If $_C = Chr(40) And $_SQF = 0 And $_DQF = 0		; Opening Paren 
            $_PF = $_PF + 1
          EndIf
          If $_C = Chr(41)And $_SQF = 0 And $_DQF = 0		; Closing Paren 
            $_PF = $_PF - 1
          EndIf
 
          If $_C = ';' And $_SQF = 0 And $_DQF = 0		; comment - ignore rest of line 
            $_P = Len($_Line) + 1
          EndIf
 
          If $_P < $_E And $_C = Chr(36)			; double-$ 
            ; Ignore if next char is a $ 
            If SubStr($_Line, $_P + 1, 1) = Chr(36)
              $_P = $_P + 1					; move to next char position, same char 
            EndIf
          EndIf
 
 
          ; =============================================================================== 
          ; Build a variable name if a reference is encountered 
 
          ; If the VarFlag is active, check for variable termination 
          ; this occurs when EOL is reached, or an invalid varname char is present 
          If $_VF						; actively building varname 
            If $_P = $_E Or InStr($_Vt, $_C)			; are we terminating? 
              If $_P = $_E And Not InStr($_Vt, $_C)		; add last char if EOL Terminated 
                $_Var = $_Var + $_C
              EndIf
              $_VF = 0						; clear active var flag 
 
              ; Find any Local or Global declarations 
              $_L = ReadProfileString('.\VarInfo.ini', $_Fn, $_Var)
              $_G = ReadProfileString('.\VarInfo.ini', 'Global', $_Var)
 
              ; gripe if variable is not declared at all 
              If  $_L = '' And $_G = '' And Not InStr($_Flags, 'VARS')
                If $_Fn = 'Main' 	; declare IMPLICIT global if Undeclared in MAIN 
                  $_ = WriteProfileString('.\VarInfo.ini', 'Global', $_Var, '0,' + CStr($_Ctr) + ',IMPLICIT')
                Else
                  $_ = WriteProfileString('.\VarInfo.ini', $_Fn, $_Var, '0,' + CStr($_Ctr))
                EndIf
                $_ = IIf($_Fn = 'Main', 'Implicit declaration as GLOBAL!', '')
                L_Msg('Undeclared variable.', $_, $_Var, $_Fn, $_Ctr, $_Log)
                $_ECt = $_ECt + 1
                $_ShowLine = 0
              Else						; update the declared var reference 
                If $_G						; global Var 
                  If Val(Split($_G, ',')[1]) <> $_Ctr
                    If Left($_G, 1) <> 2
                      $_ = Split($_G, ',')
                      If UBound($_) = 2
                        $_ = $_[1] + ',' + $_[2]
                      Else
                        $_ = $_[1]
                      EndIf
                      $_ = WriteProfileString('.\VarInfo.ini', 'Global', $_Var, '2,' + $_)
                    EndIf
                  EndIf
                EndIf
                If $_L						; local Var 
                  If Val(Split($_L, ',')[1]) <> $_Ctr
                    If Left($_L, 1) <> 2
                      $_ = Split($_L, ',')[1]
                      $_ = WriteProfileString('.\VarInfo.ini', $_Fn, $_Var, '2,' + $_)
                    EndIf
                  EndIf
                EndIf
              EndIf
 
              ; gripe if var is inside of quotes 
              If $_SQF = 1 or $_DQF = 1
                L_Msg('Variable referenced inside string.', '', $_Var, $_Fn, $_Ctr, $_Log)
                $_ECt = $_ECt + 1
                $_ShowLine = 1
              EndIf
 
            Else
              $_Var = $_Var + $_C				; build the var name 
            EndIf
          Else
            If $_C = Chr(36)					; found a $? 
              $_Var = $_C
              $_VF = 1						; set active var flag 
            EndIf
          EndIf
 
        Next	; char 
 
      EndSelect 
 
 
      ; process warnings for inconsistent flags 
      If $_SQF = 1						; Unterminated single quote 
        L_Msg('Possible unterminated single-quote.', '', '', $_Fn, $_Ctr, $_Log)
        $_ECt = $_ECt + 1
        $_ShowLine = 1
      EndIf
      If $_DQF = 1						; Unterminated double quote 
        L_Msg('Possible unterminated double-quote.', '', '', $_Fn, $_Ctr, $_Log)
        $_ECt = $_ECt + 1
        $_ShowLine = 1
      EndIf
 
      If $_PF <> 0						; Mismatched parenthesis 
        L_Msg('Possible mismatched parenthesis.', 'PF='+$_PF, '', $_Fn, $_Ctr, $_Log)
        $_ECt = $_ECt + 1
        $_ShowLine = 1
 
      EndIf
 
      ; display the line if errors were encountered 
      If $_ShowLine
        L_Msg(Right('        ' + CStr($_Ctr) + ': ', 9) + $_Line, '', '', '', '', $_Log)
        $_ShowLine = 0
      EndIf
 
    EndIf	; $_Line <> '' 
 
  Loop
  @CRLF
 
 
;============================================================================================= 
; PASS 4 
; Scan the INI file and list all variables that are declared but not referenced 
;============================================================================================= 
 
  'Pass 4 '
  $_SectList = ReadProfileString('.\VarInfo.ini', '', '')
  $_SectList = Split(Left($_SectList,len($_SectList)-1), Chr(10))
  For Each $_Fn in $_SectList
    '.'
    $_KeyList = ReadProfileString('.\VarInfo.ini', $_Fn, '')
    $_KeyList = Split(Left($_KeyList ,len($_KeyList)-1), Chr(10))
    For Each $_Var in $_KeyList
      If $_Var <> 'DefinedOnLine'
        $_ = Split(ReadProfileString('.\VarInfo.ini', $_Fn, $_Var) + ',unknown', ',')
        If $_[0] = '1'
          If Not InStr($_Var,$_Fn)			; don't complain about UDF return vars not being used 
            L_Msg('Variable declared but not referenced.', 'Declaration line is referenced above.', $_Var, $_Fn, $_[1], $_Log)
          $_ECt = $_ECt + 1
          EndIf
        EndIf
      EndIf
    Next
  Next
  @CRLF
 
 
;============================================================================================= 
; PASS 5 
; Scan the source file to check for imbalanced If, While, Do, For, and Select statements 
; create the MAP file 
;============================================================================================= 
 
;  'Pass 5' 
  ; reset the line counter for the last pass 
  $_Ctr = 0
  $_Fn  = 'Main'
  $_Ind = 0
  $_Tag = 1
  $_IE  = 0
  $_WE  = 0
  $_DE  = 0
  $_FE  = 0
  $_SE  = 0
  $_IF  = 0
  $_WF  = 0
  $_DF  = 0
  $_FF  = 0
  $_SF  = 0
 
  WriteMap('000001: ' + $_Fn)
 
  While $_Ctr <= UBound($_FData) And $_Tag
    $_Line = Trim($_FData[$_Ctr])				; get working line 
    'Pass 5 : '$_Ctr '         ' CHR(13)			; simple progress indicator 
 
    ; remove any comment portion 
    $_ = InStr($_Line, ';')
    If $_
      If $_ = 1
        $_Line = ''
      Else
        $_Line = Left($_Line, $_ - 1)
      EndIf
    EndIf
 
    ; Remove single-quoted strings 
    $_X = InStr($_Line, "'")
    If $_X
      $_ = ''
      $_Y = 1
      For $_X = 1 to Len($_Line)
        $_C = SubStr($_Line, $_X, 1)
        If $_C = "'"
          $_Y = Not $_Y
          $_C = ''
        EndIf
        If $_Y
          $_ = $_ + $_C
        EndIf
      Next
      $_Line = $_
    EndIf
 
    ; Remove double-quoted strings 
    $_X = InStr($_Line, '"')
    If $_X
      $_ = ''
      $_Y = 1
      For $_X = 1 to Len($_Line)
        $_C = SubStr($_Line, $_X, 1)
        If $_C = '"'
          $_Y = Not $_Y
          $_C = ''
        EndIf
        If $_Y
          $_ = $_ + $_C
        EndIf
      Next
      $_Line = $_
    EndIf
 
 
 
    ; Standardize delimiters 
    $_Y = Chr(9) + ' +-('
    For $_X = 1 to Len($_Y)
      $_C = SubStr($_Y, $_X, 1)
      If InStr($_Line, $_C)
        $_Line = Join(Split($_Line, $_C), Chr(31))
      EndIf
    Next
    $_Line = Join(Split($_Line, Chr(9)), Chr(31))
    $_Line = Chr(31) + $_Line + Chr(31)				; leading/trailing delims 
 
    ; process function/endfunction statements 
    If InStr($_Line, Chr(31) + 'Function' + Chr(31))
      WriteMap('')						; separate functions 
      $_Fn = Split($_Line, Chr(31))[2]				; set active function name 
      $_Ind = 0
      WriteMap(Right('000000' + $_Ctr, 6) + ': ' + Left($_Pad, $_Ind) + 'Function: ' + $_Fn)
      ; Check for open objects - should be none 
      If $_IF > 0
        WriteMap(' - Warning: Open If when defining function. ' + $_IF)
        L_Msg('Open If when defining function.', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
        $_IE = $_IE + 1
      EndIf
      If $_IF < 0
        WriteMap(' - Warning: EndIf without If when defining function. ' + $_IF)
        L_Msg('EndIf without If when defining function.', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
        $_IE = $_IE + 1
      EndIf
      If $_WF > 0
        WriteMap(' - Warning: Open While when defining function. ' + $_WF)
        L_Msg('Open While when defining function.', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
      EndIf
      If $_WF < 0
        WriteMap(' - Warning: Loop without While when defining function. ' + $_WF)
        L_Msg('', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
      EndIf
      If $_DF > 0
        WriteMap(' - Warning: Open Do when defining function. ' + $_DF)
        L_Msg('Open Do when defining function.', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
      EndIf
      If $_DF < 0
        WriteMap(' - Warning: Until without Do when defining function. ' + $_DF)
        L_Msg('Until without Do when defining function.', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
      EndIf
      If $_FF > 0
        WriteMap(' - Warning: Open For when defining function. ' + $_FF)
        L_Msg('Open For when defining function.', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
      EndIf
      If $_FF < 0
        WriteMap(' - Warning: Next without For when defining function. ' + $_FF)
        L_Msg('Next without For when defining function.', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
      EndIf
      If $_SF > 0
        WriteMap(' - Warning: Open Select when defining function. ' + $_SF)
        L_Msg('Open Select when defining function.', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
      EndIf
      If $_SF < 0
        WriteMap(' - Warning: EndSelect without Select when defining function. ' + $_SF)
        L_Msg('EndSelect without Select when defining function.', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
      EndIf
      $_aIOpen[0, 0] = 0 ;$_IF					; set # of open objects 
      $_aWOpen[0, 0] = 0 ;$_WF					; technically, should be zero! 
      $_aDOpen[0, 0] = 0 ;$_DF 
      $_aFOpen[0, 0] = 0 ;$_FF 
      $_aSOpen[0, 0] = 0 ;$_SF 
      $_IF = 0
      $_WF = 0
      $_DF = 0
      $_FF = 0
      $_SF = 0
    EndIf
 
    If InStr($_Line, Chr(31) + 'EndFunction' + Chr(31))
      $_Ind = 0
      WriteMap(Right('000000' + $_Ctr, 6) + ': ' + Left($_Pad, $_Ind) + 'EndFunction ' + $_Fn)
      If $_aIOpen[0, 0] < $_IF				; same number of open If statements? 
        WriteMap(' - Warning: Unterminated If when closing function!')
        L_Msg('Unterminated If when closing function!', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
        $_IE = $_IE + 1
      EndIf
      If $_aIOpen[0, 0] > $_IF				; same number of open If statements? 
        WriteMap(' - Warning: EndIf without If when closing function!')
        L_Msg('EndIf without If when closing function!', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
        $_IE = $_IE + 1
      EndIf
      If $_aWOpen[0, 0] < $_WF				; same number of open While statements? 
        WriteMap(' - Warning: Unterminated While when closing function!')
        L_Msg('Unterminated While when closing function!', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
        $_WE = $_WE + 1
      EndIf
      If $_aWOpen[0, 0] > $_WF				; same number of open While statements? 
        WriteMap(' - Warning: Loop without While when closing function!')
        L_Msg('Loop without While when closing function!', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
        $_WE = $_WE + 1
      EndIf
      If $_aDOpen[0, 0] < $_DF				; same number of open Do statements? 
        WriteMap(' - Warning: Unterminated Do when closing function!')
        L_Msg('Unterminated Do when closing function!', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
        $_DE = $_DE + 1
      EndIf
      If $_aDOpen[0, 0] > $_DF				; same number of open Do statements? 
        WriteMap(' - Warning: Until without Do when closing function!')
        L_Msg('Until without Do when closing function!', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
        $_DE = $_DE + 1
      EndIf
      If $_aFOpen[0, 0] < $_FF				; same number of open For statements? 
        WriteMap(' - Warning: Unterminated For when closing function!')
        L_Msg('Unterminated For when closing function!', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
        $_FE = $_FE + 1
      EndIf
      If $_aFOpen[0, 0] > $_FF				; same number of open For statements? 
        WriteMap(' - Warning: Next without For when closing function!')
        L_Msg('Next without For when closing function!', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
        $_FE = $_FE + 1
      EndIf
      If $_aSOpen[0, 0] < $_SF				; same number of open Select statements? 
        WriteMap(' - Warning: Unterminated Select when closing function!')
        L_Msg('Unterminated Select when closing function!', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
        $_SE = $_SE + 1
      EndIf
      If $_aSOpen[0, 0] > $_SF				; same number of open Select statements? 
        WriteMap(' - Warning: EndSelect without Select when closing function!')
        L_Msg('EndSelect without Select when closing function!', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
        $_SE = $_SE + 1
      EndIf
      $_Fn = 'Main'
    EndIf
 
 
    ; process If/EndIf statements 
    If InStr($_Line, Chr(31) + 'If' + Chr(31)) And InStr($_Line, Chr(31) + 'EndIf' + Chr(31))		; both If/Endif 
      WriteMap(Right('000000' + $_Ctr, 6) + ': ' + Left($_Pad, $_Ind + 1) + 'If/EndIf')
    Else							; check for if or endif 
      If InStr($_Line, Chr(31) + 'If' + Chr(31))
        $_Ind = $_Ind + 1
        $_IF = $_IF + 1
        WriteMap(Right('000000' + $_Ctr, 6) + ': ' + Left($_Pad, $_Ind) + Left($_FData[$_Ctr], 30))
        $_aIOpen[1, $_IF] = $_IF				; set # of open objects 
        $_aWOpen[1, $_IF] = $_WF
        $_aDOpen[1, $_IF] = $_DF
        $_aFOpen[1, $_IF] = $_FF
        $_aSOpen[1, $_IF] = $_SF
      EndIf
 
      If InStr($_Line, Chr(31) + 'EndIf' + Chr(31)) 
        WriteMap(Right('000000' + $_Ctr, 6) + ': ' + Left($_Pad, $_Ind) + 'EndIf')
        If $_aWOpen[1, $_IF] <> $_WF				; same number of open While statements? 
          WriteMap(' - Warning: Unterminated While when closing If!')
          L_Msg('Unterminated While when closing If!', '', '', $_Fn, $_Ctr, $_Log)
          $_Ect = $_Ect + 1
          $_WE = $_WE + 1
        EndIf
        If $_aDOpen[1, $_IF] <> $_DF				; same number of open Do statements? 
          WriteMap(' - Warning: Unterminated Do when closing If!')
          L_Msg('Unterminated Do when closing If!', '', '', $_Fn, $_Ctr, $_Log)
          $_Ect = $_Ect + 1
          $_DE = $_DE + 1
        EndIf
        If $_aFOpen[1, $_IF] <> $_FF				; same number of open For statements? 
          WriteMap(' - Warning: Unterminated For when closing If!')
          L_Msg('Unterminated For when closing If!', '', '', $_Fn, $_Ctr, $_Log)
          $_Ect = $_Ect + 1
          $_FE = $_FE + 1
        EndIf
        If $_aSOpen[1, $_IF] <> $_SF				; same number of open Select statements? 
          WriteMap(' - Warning: Unterminated Select when closing If!')
          L_Msg('Unterminated Select when closing If!', '', '', $_Fn, $_Ctr, $_Log)
          $_Ect = $_Ect + 1
          $_SE = $_SE + 1
        EndIf
        $_IF = $_IF - 1
        $_Ind = $_Ind - 1
      EndIf
    EndIf
 
 
    ; process While/Loop statements 
    If InStr($_Line, Chr(31) + 'While' + Chr(31)) And InStr($_Line, Chr(31) + 'Loop' + Chr(31))	; both While/Loop 
      WriteMap(Right('000000' + $_Ctr, 6) + ': ' + Left($_Pad, $_Ind + 1) + 'While/Loop')
    Else							; check for While or loop 
      If InStr($_Line, Chr(31) + 'While' + Chr(31))
        $_Ind = $_Ind + 1
        $_WF = $_WF + 1
        $_aIOpen[2, $_WF] = $_IF				; set # of open objects 
        $_aWOpen[2, $_WF] = $_WF
        $_aDOpen[2, $_WF] = $_DF
        $_aFOpen[2, $_WF] = $_FF
        $_aSOpen[2, $_WF] = $_SF
        WriteMap(Right('000000' + $_Ctr, 6) + ': ' + Left($_Pad, $_Ind) + Left($_FData[$_Ctr], 30))
      EndIf
 
      If InStr($_Line, Chr(31) + 'Loop' + Chr(31))
        WriteMap(Right('000000' + $_Ctr, 6) + ': ' + Left($_Pad, $_Ind) + 'Loop')
        If $_aIOpen[2, $_WF] <> $_IF				; same number of open IF statements? 
          WriteMap(' - Warning: Unterminated If when closing While!')
          L_Msg('Unterminated If when closing While!', '', '', $_Fn, $_Ctr, $_Log)
          $_Ect = $_Ect + 1
          $_IE = $_IE + 1
        EndIf
        If $_aDOpen[2, $_WF] <> $_DF				; same number of open Do statements? 
          WriteMap(' - Warning: Unterminated Do when closing While!')
          L_Msg('Unterminated Do when closing While!', '', '', $_Fn, $_Ctr, $_Log)
          $_Ect = $_Ect + 1
          $_DE = $_DE + 1
        EndIf
        If $_aFOpen[2, $_WF] <> $_FF				; same number of open For statements? 
          WriteMap(' - Warning: Unterminated For when closing While!')
          L_Msg('Unterminated For when closing While!', '', '', $_Fn, $_Ctr, $_Log)
          $_Ect = $_Ect + 1
          $_FE = $_FE + 1
        EndIf
        If $_aSOpen[2, $_WF] <> $_SF				; same number of open Select statements? 
          WriteMap(' - Warning: Unterminated Select when closing While!')
          L_Msg('Unterminated Select when closing While!', '', '', $_Fn, $_Ctr, $_Log)
          $_Ect = $_Ect + 1
          $_SE = $_SE + 1
        EndIf
        $_WF = $_WF - 1
        $_Ind = $_Ind - 1
      EndIf
    EndIf
 
 
    ; process Do/Until statements 
    If InStr($_Line, Chr(31) + 'Do' + Chr(31)) And InStr($_Line, Chr(31) + 'Until' + Chr(31))	; both Do/Until 
      WriteMap(Right('000000' + $_Ctr, 6) + ': ' + Left($_Pad, $_Ind + 1) + 'Do/Until')
    Else							; check for if or endif 
      If InStr($_Line, Chr(31) + 'Do' + Chr(31))
        $_Ind = $_Ind + 1
        $_DF = $_DF + 1
        $_aIOpen[3, $_DF] = $_IF				; set # of open objects 
        $_aWOpen[3, $_DF] = $_WF
        $_aDOpen[3, $_DF] = $_DF
        $_aFOpen[3, $_DF] = $_FF
        $_aSOpen[3, $_DF] = $_SF
        WriteMap(Right('000000' + $_Ctr, 6) + ': ' + Left($_Pad, $_Ind) + Left($_FData[$_Ctr], 30))
      EndIf
 
      If InStr($_Line, Chr(31) + 'Until' + Chr(31))
        WriteMap(Right('000000' + $_Ctr, 6) + ': ' + Left($_Pad, $_Ind) + 'Until')
        If $_aIOpen[3, $_DF] <> $_IF				; same number of open IF statements? 
          WriteMap(' - Warning: Unterminated If when closing Do!')
          L_Msg('Unterminated If when closing Do!', '', '', $_Fn, $_Ctr, $_Log)
          $_Ect = $_Ect + 1
          $_IE = $_IE + 1
        EndIf
        If $_aWOpen[3, $_DF] <> $_WF				; same number of open While statements? 
          WriteMap(' - Warning: Unterminated While when closing Do!')
          L_Msg('Unterminated While when closing Do!', '', '', $_Fn, $_Ctr, $_Log)
          $_Ect = $_Ect + 1
          $_WE = $_WE + 1
        EndIf
        If $_aFOpen[3, $_DF] <> $_FF				; same number of open For statements? 
          WriteMap(' - Warning: Unterminated For when closing Do!')
          L_Msg('Unterminated For when closing Do!', '', '', $_Fn, $_Ctr, $_Log)
          $_Ect = $_Ect + 1
          $_FE = $_FE + 1
        EndIf
        If $_aSOpen[3, $_DF] <> $_SF				; same number of open Select statements? 
          WriteMap(' - Warning: Unterminated Select when closing Do!')
          L_Msg('Unterminated Select when closing Do!', '', '', $_Fn, $_Ctr, $_Log)
          $_Ect = $_Ect + 1
          $_SE = $_SE + 1
        EndIf
        $_DF = $_DF - 1
        $_Ind = $_Ind - 1
      EndIf
    EndIf
 
 
    ; process For/Next statements 
    If InStr($_Line, Chr(31) + 'For' + Chr(31)) And InStr($_Line, Chr(31) + 'Next' + Chr(31))	; both Do/Until 
      WriteMap(Right('000000' + $_Ctr, 6) + ': ' + Left($_Pad, $_Ind + 1) + 'For/Next')
    Else							; check for if or endif 
      If InStr($_Line, Chr(31) + 'For' + Chr(31))
        $_Ind = $_Ind + 1
        $_FF = $_FF + 1
        $_aIOpen[4, $_FF] = $_IF				; set # of open objects 
        $_aWOpen[4, $_FF] = $_WF
        $_aDOpen[4, $_FF] = $_DF
        $_aFOpen[4, $_FF] = $_FF
        $_aSOpen[4, $_FF] = $_SF
        WriteMap(Right('000000' + $_Ctr, 6) + ': ' + Left($_Pad, $_Ind) + Left($_FData[$_Ctr], 30))
      EndIf
 
      If InStr($_Line, Chr(31) + 'Next' + Chr(31))
        WriteMap(Right('000000' + $_Ctr, 6) + ': ' + Left($_Pad, $_Ind) + 'Next')
        If $_aIOpen[4, $_FF] <> $_IF				; same number of open IF statements? 
          WriteMap(' - Warning: Unterminated If when closing For!')
          L_Msg('Unterminated If when closing For!', '', '', $_Fn, $_Ctr, $_Log)
          $_Ect = $_Ect + 1
          $_IE = $_IE + 1
        EndIf
        If $_aWOpen[4, $_FF] <> $_WF				; same number of open While statements? 
          WriteMap(' - Warning: Unterminated While when closing For!')
          L_Msg('Unterminated While when closing For!', '', '', $_Fn, $_Ctr, $_Log)
          $_Ect = $_Ect + 1
          $_WE = $_WE + 1
        EndIf
        If $_aDOpen[4, $_FF] <> $_DF				; same number of open For statements? 
          WriteMap(' - Warning: Unterminated For when closing For!')
          L_Msg('Unterminated For when closing For!', '', '', $_Fn, $_Ctr, $_Log)
          $_Ect = $_Ect + 1
          $_DE = $_DE + 1
        EndIf
        If $_aSOpen[4, $_FF] <> $_SF				; same number of open Select statements? 
          WriteMap(' - Warning: Unterminated Select when closing For!')
          L_Msg('Unterminated Select when closing For!', '', '', $_Fn, $_Ctr, $_Log)
          $_Ect = $_Ect + 1
          $_SE = $_SE + 1
        EndIf
        $_FF = $_FF - 1
        $_Ind = $_Ind - 1
      EndIf
    EndIf
 
 
    ; process Select/EndSelect statements 
    If InStr($_Line, Chr(31) + 'Select' + Chr(31))
      $_Ind = $_Ind + 1
      $_SF = $_SF + 1
      $_aIOpen[5, $_SF] = $_IF					; how many open IF statements? 
      $_aWOpen[5, $_SF] = $_WF					; how many open While statements? 
      $_aDOpen[5, $_SF] = $_DF					; how many open Do statements? 
      $_aFOpen[5, $_SF] = $_FF					; how many open For statements? 
      $_aSOpen[5, $_SF] = $_SF					; how many open Select statements? 
      WriteMap(Right('000000' + $_Ctr, 6) + ': ' + Left($_Pad, $_Ind) + 'Select')
    EndIf
 
    If InStr($_Line, Chr(31) + 'EndSelect' + Chr(31))
      WriteMap(Right('000000' + $_Ctr, 6) + ': ' + Left($_Pad, $_Ind) + 'EndSelect')
      If $_aIOpen[5, $_SF] <> $_IF				; same number of open IF statements? 
        WriteMap(' - Warning: Unterminated If when closing Select!')
        L_Msg('Unterminated If when closing Select!', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
        $_IE = $_IE + 1
      EndIf
      If $_aWOpen[5, $_SF] <> $_WF				; same number of open While statements? 
        WriteMap(' - Warning: Unterminated While when closing Select!')
        L_Msg('Unterminated While when closing Select!', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
        $_WE = $_WE + 1
      EndIf
      If $_aDOpen[5, $_SF] <> $_DF				; same number of open Select statements? 
        WriteMap(' - Warning: Unterminated Do when closing Select!')
        L_Msg('Unterminated Do when closing Select!', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
        $_SE = $_SE + 1
      EndIf
      If $_aFOpen[5, $_SF] <> $_FF				; same number of open For statements? 
        WriteMap(' - Warning: Unterminated For when closing Select!')
        L_Msg('Unterminated For when closing Select!', '', '', $_Fn, $_Ctr, $_Log)
        $_Ect = $_Ect + 1
        $_FE = $_FE + 1
      EndIf
      $_SF = $_SF - 1
      $_Ind = $_Ind - 1
    EndIf
 
    $_Ctr = $_Ctr + 1						; increment counter 
    If $_IF >= 40 Or $_WF >= 40 Or $_DF >= 40 Or $_SF >= 40 
      WriteMap('Nesting beyond 40 levels! Sanity will not continue.')
      WriteMap('CHECK YOUR CODE!')
      L_Msg('Nesting beyond 40 levels! Sanity will not continue.', 'CHECK YOUR CODE!', '', '', '', $_Log)
      $_Tag = 0
    EndIf
  Loop
  'Pass 5 : '$_Ctr '         ' CHR(13)				; final line number 
 
  If $_FN <> 'Main'
    WriteMap('Warning: Missing EndFunction in function ' + $_FN + '!')
    L_Msg('Missing EndFunction in function ' + $_FN + '!', '', '', $_Fn, $_Ctr, $_Log)
    $_Ect = $_Ect + 1
  EndIf
 
  @CRLF
 
  If $_IE
    WriteMap('Warning: ' + $_IE + ' Unbalanced If statement(s)!')
    L_Msg(' ' + $_IE + ' Unbalanced If statement(s)!', '', '', '', '', $_Log)
  EndIf
  If $_WE
    WriteMap('Warning: ' + $_WE + ' Unbalanced While statement(s)!')
    L_Msg(' ' + $_WE + ' Unbalanced While statement(s)!', '', '', '', '', $_Log)
  EndIf
  If $_DE
    WriteMap('Warning: ' + $_DE + ' Unbalanced Do statement(s)!')
    L_Msg(' ' + $_DE + ' Unbalanced Do statement(s)!', '', '', '', '', $_Log)
  EndIf
  If $_FE
    WriteMap('Warning: ' + $_FE + ' Unbalanced For statement(s)!')
    L_Msg(' ' + $_FE + ' Unbalanced For statement(s)!', '', '', '', '', $_Log)
  EndIf
  If $_SE
    WriteMap('Warning: ' + $_SE + ' Unbalanced Select statement(s)!')
    L_Msg(' ' + $_SE + ' Unbalanced Select statement(s)!', '', '', '', '', $_Log)
  EndIf
 
  L_Msg(' ' + $_Ect + ' warnings generated, ' + $_Ctr + ' lines processed.', '', '', '', '', $_Log)
 
  $Sanity = Not $_Ect
 
  @CRLF
 
  Exit Not $Sanity
 
EndFunction
 
 
 
; Display warning messages from the Sanity UDF 
Function L_Msg($_Text1, $_Text2, $_V, $_F, $_C, $_LogFile)
 
  Dim $_, $_Text
 
  ; If _Text1 begins with a space, it's the only message to display 
  If Left($_Text1, 1) = ' '
    $_Text = $_Text1 + @CRLF
 
  ; otherwise, create a formatted display 
  Else
    $_Text = '              ' + @CRLF + 'Warning: ' + $_Text1 + @CRLF
    If $_V
      $_Text = $_Text + '              Variable Name: ' + $_V + @CRLF
    EndIf
    If $_F
      $_Text = $_Text + '                In function: ' + $_F + @CRLF
    EndIf
    If $_C
      $_Text = $_Text + '         Referenced on line: ' + $_C + @CRLF
    EndIf
    If $_Text2
      $_Text = $_Text + '         ' + $_Text2 + @CRLF
    EndIf
 
  EndIF
 
 
  ; display, and optionally log the message 
  $_Text
  If $_LogFile
    $_ = RedirectOutput($_LogFile)
    $_Text
    $_ = RedirectOutput('')
  EndIF
 
  $_ = RedirectOutput('warnings.csv')
  $_Text1 ',' $_Text2 ',' $_V ',' $_F ',' $_C @CRLF
  $_ = RedirectOutput('')
 
  Exit 0
 
EndFunction
 
Function WriteMap($_Msg)
 
  Dim $_
 
  $_ = RedirectOutput('.\CodeMap.txt')
  $_Msg @CRLF
  $_ = RedirectOutput('')
 
  Exit 0
 
EndFunction