Extract function call from #DefineFunction/SubRoutine statement
;==========================================================================================================================================
; Find all wbt files in foldertree and
; create a list of all used functions and subroutines
; (defined by "#DefineFunction" and "#DefineSubRoutine" statements).
;
; (c)Detlev Dalitz.20110204.
;==========================================================================================================================================

DirChange (DirScript ())
intPrevIC50 = IntControl (50, 0, 0, 0, 0) ; Remove "Go to web page" button from error boxes.
GoSub DEFINE_FUNCTIONS


; Define output file names.
strFileOut1 = "Functions.All.txt"
strFileOut2 = "Functions.Unique.txt"

; Change the folderpath to your needs.
strFolderStart = "W:\WINBATCH\*.wbt"


; By using the DOS commandline utility FINDSTR.EXE find all wbt files in foldertree,
; which contains "#DefineFunction" or "#DefineSubroutine" statements.
; Get file name, line number, file offset, line content into CSV array resp. CSV text file.

; Leave this untouched.
strSearchText = "#DefineFunction #DefineSubRoutine"
strDosCmd = 'FINDSTR.EXE /S /P /I /O /N /B "' : strSearchText : '" "' : strFolderStart : '"'

arrData = udfCaptureDosOutput (strDosCmd) ; This opens a DOS window, please wait as needed.
; arrData = udfCaptureDosOutput (Environment ("ComSpec") : " /U /C " : strDosCmd) ; This opens a DOS window, please wait as needed.
; udfDisplayResult (arrData)
Terminate (arrData[2] != 0, "Terminated.", "Error while capturing DOS output." : @LF : "Error = " : arrData[2] : @LF : arrData[1])

; Get StdOut data string into variable and remove capture array.
strData = arrData[0]
Drop (arrData)


; Remove empty leading and trailing lines (@CRLF's) from the StdOut data string.
intPosL = 1
While StrIndex (strData, @CRLF, intPosL, @FWDSCAN) == intPosL
   intPosL = intPosL + 2
EndWhile
intPosR = StrLen (strData)
While StrIndex (strData, @CRLF, intPosR, @BACKSCAN) == intPosR - 1
   intPosR = intPosR - 2
EndWhile
strData = StrSub (strData, intPosL, intPosR - intPosL + 1)
ClipPut (strData)

; Convert from DOS codepage 850 to ANSI codepage 1252.
; strData = udfCodepageConvert (strData, 850, 1252)  ; To convert äöüÄÖÜßéá and such foreign characters.
; Seems not to be needed.

; Make pretty to CSV format.
; The colon field delimiter from the original DOS output format will be changed to comma delimiter.
strReplFrom = " " : @CR
While StrIndex (strData, strReplFrom, 1, @FWDSCAN) ; Remove trailing spaces per line.
   strData = StrReplace (strData, strReplFrom, @CR)
EndWhile
strData = StrReplace (strData, '"', '""')          ; Make double apostrophes.
strData = StrReplace (strData, @LF, @LF : '"')     ; Enclose each item in apostrophes, left edge in line.
strData = StrReplace (strData, @CR, '"' : @CR)     ; Enclose each item in apostrophes, right edge in line.
strData = '"' : strData : '"'                      ; Left edge from the first item, right edge from the last item.
strData = StrReplace (strData, ":", '","')         ; Replace colon with comma, enclose item in apostrophes, left edge and right edge.
strData = StrReplace (strData, '","\', ':\')       ; Repair replace failure from step before.
strData = StrReplace (strData, @TAB, "   ")        ; Replace each tab character with three spaces.

; Put the CSV formatted data string into file and immediately get back the proper CSV format into a dim-2 array.
intBW = FilePut (strFileOut1, strData)

Run (strFileOut1, "")


; Some additional work before reading back the data from file into dim-2 array.
; Make pretty for later comparing. Do it with the entire file at once.
; Don't mind to possibly touch the other path data within the file.
; We do not need the other data.
strData = FileGet (strFileOut1)
strData = StrReplace (strData, "(", " (")
;strData = StrReplace (strData, ")", ") ")
strData = StrReplace (strData, ",", ", ")
strData = StrReplace (strData, ";", " ; ")
While StrIndex (strData, "  ", 1, @FWDSCAN)
   strData = StrReplace (strData, "  ", " ")
EndWhile
strData = StrReplace (strData, "( ", "(")
strData = StrReplace (strData, " )", ")")
intBW = FilePut (strFileOut2, strData) ; This file will be overwritten later.


; Consolidate the list to get only unique function names from #DefineFunction and #DefineSubroutine statements.

; Get the CSV format into a dim-2 array, use standard comma delimiter.
arrData = ArrayFileGetCSV (strFileOut2, 0)

; Prepare a new array to collect the unique entries. Array will be re-dimed later as needed.
; Must have at least one cell to avoid error "1710 Array is empty" when first time use ArrayLocate.
arrUnique = ArrDimension (1)

; Process each data item.
intLast = ArrInfo (arrData, 1) - 1
For intD = 0 To intLast
   strData = ItemRemove (1, arrData[intD, 3], " ") ; Remove "#DefineFunction " or "#DefineSubroutine ".
   arrResult = ArrayLocate (arrUnique, strData)
   If arrResult[0] == -1 ; If not found, then put data into the "unique" array.
      arrUnique[ArrInfo (arrUnique, 1) - 1] = strData
      ArrayRedim (arrUnique, ArrInfo (arrUnique, 1) + 1) ; Prepare one more cell for a possible next entry.
   EndIf
Next
ArrayRemove (arrUnique, ArrInfo (arrUnique, 1) - 1) ; Remove last unused cell.

ArraySort (arrUnique)
intBW = ArrayFilePut (strFileOut2, arrUnique)

Run (strFileOut2, "")

:CANCEL
Exit


;==========================================================================================================================================
:DEFINE_FUNCTIONS
;------------------------------------------------------------------------------------------------------------------------------------------
#DefineFunction udfCaptureDosOutput (strDosCommand)
DebugTrace (22, "")         ; Allow DebugTrace continuation (inherit the debug mode from the caller).
arrData = ArrDimension (4)  ; Allocate return array.
ArrInitialize (arrData, "") ; Initialize to all null strings.
; Stuff the command in the 4th element of the array.
arrData[3] = strDosCommand
objShell = ObjectCreate ("WScript.Shell") ; Open shell object.

; If 64 bit turn off redirection?
If WinMetrics (-7) == 2
   ; 64-bit Windows.
   intIC92old = IntControl (92, "disable", 0, 0, 0)
   objScriptExec = objShell.Exec(strDosCommand) ; Run the command.
   IntControl (92, "revert", intIC92old, 0, 0)
Else
   objScriptExec = objShell.Exec(strDosCommand) ; Run the command.
EndIf

; Capture data from StdOut and StdErr.
objStdOut = objScriptExec.StdOut
objStdErr = objScriptExec.StdErr
While @TRUE
   While !objStdOut.AtEndOfStream
      strLine = objStdOut.ReadLine
      arrData[0] = arrData[0] : strLine : @CRLF
   EndWhile
   While !objStdErr.AtEndOfStream
      strLine = objStdErr.ReadLine
      arrData[1] = arrData[1] : strLine : @CRLF
   EndWhile
   If objScriptExec.Status != 0
      ; Get remainder of data, if any.
      While !objStdOut.AtEndOfStream
         strLine = objStdOut.ReadLine
         arrData[0] = arrData[0] : strLine : @CRLF
      EndWhile
      While !objStdErr.AtEndOfStream
         strLine = objStdErr.ReadLine
         arrData[1] = arrData[1] : strLine : @CRLF
      EndWhile
      Break
   EndIf
EndWhile

arrData[2] = objScriptExec.ExitCode ; Save errorlevel/exit code.

; Close handles.
objStdOut = 0
objStdErr = 0
objScriptExec = 0
objShell = 0

; Return the array.
Return arrData
;..........................................................................................................................................
; arrData[0] ... strStdOut
; arrData[1] ... strStdErr
; arrData[2] ... intExitcode
; arrData[3] ... strDosCommand
;..........................................................................................................................................
; Adapted from ...
;   Topic:  ftype equivalent?
;   Conf:  WinBatch
;   From:  deanad
;   Date:  Tuesday, December 21, 2010 08:49 AM
;
; ... and modified by ...
; Detlev Dalitz.20101221.20110204.
;..........................................................................................................................................
#EndFunction
;------------------------------------------------------------------------------------------------------------------------------------------


;------------------------------------------------------------------------------------------------------------------------------------------
#DefineFunction udfCodepageConvert (strTextFrom, intCpFrom, intCpTo) ; To convert äöüÄÖÜßéá and such foreign characters.
intPrevCodepage = ChrSetCodepage (intCpFrom)
strUnicode = ChrStringToUnicode (strTextFrom)
ChrSetCodepage (intCpTo)
strTextTo = ChrUnicodeToString (strUnicode)
ChrSetCodepage (intPrevCodepage)
Return strTextTo
; (c)Detlev Dalitz.20100220.
#EndFunction
;------------------------------------------------------------------------------------------------------------------------------------------


;------------------------------------------------------------------------------------------------------------------------------------------
#DefineFunction udfDisplayResult (arrData)
strStdOut = arrData[0]
strStdErr = arrData[1]
intExitcode = arrData[2]
strDosCommand = arrData[3]
strMsgTitle = "CaptureDosOutput"
If strStdErr != ""
   strMsgText = "EXITCODE = " : intExitcode : @LF : StrReplace (strStdErr, @CRLF, @LF)
Else
   strMsgText = "EXITCODE = " : intExitcode : @LF : StrReplace (strStdOut, @CRLF, @LF)
EndIf
IntControl (28, 1, 0, 0, 0)
IntControl (63, 100, 150, 900, 850)
AskItemlist (strMsgTitle, strMsgText, @LF, @UNSORTED, @SINGLE)
Return
#EndFunction
;------------------------------------------------------------------------------------------------------------------------------------------

Return ; from GoSub DEFINE_FUNCTIONS.
;==========================================================================================================================================