//-------------------------------------------------------------> // ReportUnusedLocals.pkg // R.Worsley 3/09/2008 // Locate any unused local variables // Conditions: // Variables declared in the method line are not checked // Read notes below on StringTokenizer // Will work with the old "Local" as a descriptor // Variables can be declared anywhere in the method // // If used as a class in an another application, there are // a couple of properties that can be set to automate the // input and output file names. See the property descriptions // in class cReportUnusedLocals // //------------------------------------------------------------> // 4/08/2008 RLW - Make into a class so the process can be // incorporated into other applications // 4/12/2008 RLW - Change read/write registry process // 4/12/2008 RLW - Fix so that wrapped variables on the function // or procedure line won't show odd results // 6/22/2008 RLW - Add missing Seq_Release_Channel calls // 6/30/2018 NGS - Changed to use registry functions of the cApplication class. // This is because that class uses the USER //-------------------------------------------------------------> Use cApplication.pkg Use Batchdd.pkg Use vWin32fh.pkg // AUTHOR: Torkild Resheim // Original 1.0 version modified slightly to work // better when using a space as a token. Presence // of two spaces between variables would result // in a blank "not found" variable. // Original version will work fine with this caution. Use StringTokenizer.pkg Enum_List Define VarName Define VarStatus Define MethodName Define Line# End_Enum_List Define CS_LogfileName for "UnusedVariables.txt" // Necessary for eliminating comments Define gsComments for "//" // Defines the end of the method and beginning of the comparison Define gsEndP for "END_PROCEDURE" Define gsEndF for "END_FUNCTION" Define CS_UnusedVariables for "UnusedVariables" Define CS_LastDirectory for "LastDirectory" Define CS_NoOfErrorFiles for "NoOfErrorFiles" // Enter all variable types you will be using here. Some users don't use the more exotic // ones like "BigInt" or "Real" Define gsVarTypes for "STRING;INTEGER;NUMBER;DATE;DATETIME;BOOLEAN;HANDLE;REAL;VARIANT;TIMESPAN;ROWID;DECIMAL" Class cReportUnusedLocals is a BusinessProcess Procedure Construct_object Forward Send Construct_object // Set the following properties if this package is used in another application Property String psSourceName // Name of the code file being worked on Property String psOutputFileName // Output file name for individual use - defaults to UnusedVariables.log if left blank. // Primarily available for users who want to incorporate the class into another product Property String psOutputPath // same usage as psOutputFileName Property Integer piOutputType // Output box=0 or output to a file=1. Property String psParsedFile // New 31 of Oct 2014 Nils G. Svedmyr Set Status_Panel_State to False // To write the currently worked on source file to the log file. Object oTokenizer is a StringTokenizer Set Separator to " " End_Object End_Procedure Function sGetSourceDirectory Returns String String sPath Integer iPos Get ReadString CS_UnusedVariables CS_LastDirectory "" to sPath If (sPath <> "") Begin // In case of multiple paths appended in config.sys Move (Pos(";", sPath)) to iPos If (iPos > 0) Begin Move (Left(sPath, (iPos-1))) to sPath End End Get vFolderFormat sPath to sPath Function_Return sPath End_Function // Simply removes the first word of any input string // all up to the first space encountered Function sGetRemoveFirstWord String sString Returns String Integer iPos Move (trim(sString)) to sString Move (Pos(" ", sString)) to iPos Function_Return (Right(sString, (Length(sString)-iPos))) End_Function // Parses out the name of any method from the rest of the line Function sGetMethodName String sMethod Returns String Integer iPos Get sGetRemoveFirstWord sMethod to sMethod Move (Right(sMethod, (Length(sMethod)-iPos))) to sMethod If (uppercase(Left(trim(sMethod), 4)) = "SET "); Get sGetRemoveFirstWord sMethod to sMethod // Remove any trailing arguments if any Move (Pos(" ", sMethod)) to iPos Function_Return (left(sMethod, (iPos-1))) End_Function Procedure OnProcess String sPath sLine sVars sSingleVar sMethodName sTest sOrigMethodName sFileName sOutputFileName Integer iPos iArrayCount iMax iCounter iItems iLineCount iFound iOutputType iChIn iChOut iNoOfErrorFiles String[][4] sLocalVars Move (Seq_New_Channel()) to iChIn If (iChIn=DF_SEQ_CHANNEL_NOT_AVAILABLE) Begin Send Info_Box "No Channel Available for Input" "Error" Procedure_Return End Move (Seq_New_Channel()) to iChOut If (iChOut=DF_SEQ_CHANNEL_NOT_AVAILABLE) Begin Send Info_Box "No Channel Available for Output" "Error" Procedure_Return End Move 0 to iNoOfErrorFiles Get piOutputType to iOutputType Get psSourceName to sFileName Get psOutputFileName to sOutputFileName If (sOutputFileName = "") Begin Move CS_LogfileName to sOutputFileName End Direct_Input channel iChIn sFileName If (SeqEof) Begin Send Stop_Box "Program not found!" Abort End If (psOutputPath(Self) = "") Begin Move (ParseFolderName(sFileName)) to sPath Send WriteString of ghoApplication CS_UnusedVariables CS_LastDirectory sPath End Else Begin Get psOutputPath to sPath Get vFolderFormat sPath to sPath End If (iOutputType = 1) Begin Append_Output channel iChOut (sPath + sOutputFileName) // Direct_Output channel iChOut (sPath + sOutputFileName) Writeln channel iChOut Writeln channel iChOut "Source File: " (psParsedFile(Self)) Writeln channel iChOut "Line # - Method Name - Variable Name" Writeln channel iChOut "------------------------------------" End Else Begin Showln "Line # - Method Name - Variable Name" Showln "------------------------------------------------------" End Move (ResizeArray(sLocalVars, 0)) to sLocalVars Repeat Readln channel iChIn sLine Increment iLineCount // Don't bother with comments If (Left(trim(sLine), 2) <> gsComments and trim(sLine) <> "") Begin Move (uppercase(trim(sLine))) to sMethodName If (Left(sMethodName, 10) = "PROCEDURE " or left(sMethodName, 9) = "FUNCTION ") Begin Get sGetMethodName sLine to sOrigMethodName // Looks nice for the report Repeat Readln channel iChIn sLine Increment iLineCount // Remove any trailing comments Move (Pos(gsComments, sLine)) to iPos If (iPos > 0) Begin Move (Left(sLine, (iPos-1))) to sLine End // Parse the variables // Remove "local" if there If (Uppercase(Left(Trim(sLine), 5)) = "LOCAL") Begin Get sGetRemoveFirstWord sLine to sLine End Move (Uppercase(Trim(sLine))) to sTest // The most common variable types are included here, if you require others, // feel free to add them. Be sure the length of the type is correct. // i.e. "STRING" is 6 characters. We need this for parsing If (gsVarTypes contains trim(left(sTest, (pos(" ", sTest))))) Begin Move (trim(sLine)) to sLine // Remove the data type command Move (pos(" ", sLine)) to iPos // first space, all after are variables Move (Mid(sLine, (Length(sLine)-iPos), (iPos+1))) to sVars // Now parse out all variables in the line Set ParseString of oTokenizer to (trim(sVars)) Get ItemCount of oTokenizer to iItems // Go through the Tokenizer and // add to array of locals // Need to do this here because we don't know where // the programmer will place local variables, and // we need to update the array as they occur. And // SearchArray doesn't work for this style of array Move 0 to iCounter While iCounter lt iItems Get TokenAt of oTokenizer iCounter to sSingleVar Move (SizeOfArray(sLocalVars)) to iArrayCount // if the procedure or function line is wrapped, it may // contain the variable type, and we don't want that // showing up as an unused variable If (not(gsVarTypes contains uppercase(sSingleVar))) Begin Move sSingleVar to sLocalVars[iArrayCount][VarName] Move "0" to sLocalVars[iArrayCount][VarStatus] // Usage indicator Move sOrigMethodName to sLocalVars[iArrayCount][MethodName] Move iLineCount to sLocalVars[iArrayCount][Line#] End Increment iCounter Loop End Else Begin // *** The rest of the method *** // for each line here we look in the LocalVars array // If the localvar is found before "END" then we mark // it in the array, after "END" we go through the local // array and look for any not marked and add to a report If (Trim(sLine) <> "") Begin Move (SizeOfArray(sLocalVars)) to iMax For iCounter from 0 to (iMax-1) If (Uppercase(sLine) contains Uppercase(sLocalVars[iCounter][VarName])) Begin // Mark array as found Move "1" to sLocalVars[iCounter][VarStatus] End Loop End End Until (Uppercase(sLine) contains gsEndP or Uppercase(sLine) contains gsEndF) // End_Procedure or End_Function // Check the array for non used variables Move (SizeOfArray(sLocalVars)) to iMax For iCounter from 0 to (iMax-1) If (sLocalVars[iCounter][VarStatus] = 0) Begin Increment iFound If (iOutputType = 0); Showln sLocalVars[iCounter][Line#] " " sLocalVars[iCounter][MethodName] " = " sLocalVars[iCounter][VarName] Else; Writeln channel iChOut sLocalVars[iCounter][Line#] " " sLocalVars[iCounter][MethodName] " = " sLocalVars[iCounter][VarName] End Loop // Zero the array Move (ResizeArray(sLocalVars, 0)) to sLocalVars End // If procedure, function End // if not comments Until (SeqEof) Close_Input channel iChIn Send Seq_Release_Channel iChIn If (iOutputType = 1) Begin If (iFound > 0) Begin Increment iNoOfErrorFiles Writeln channel iChOut (String(iFound) + " unused variables found!") Writeln channel iChOut "------------------------------------" Close_Output channel iChOut Send Seq_Release_Channel iChOut End Else Begin Writeln channel iChOut "None found, congratulations!" Writeln channel iChOut "----------------------------" Close_Output channel iChOut Send Seq_Release_Channel iChOut End End Else Begin If (iFound = 0) Begin Showln "None found, congratulations!" End Else Begin Showln (String(iFound) + " unused variables found!") Showln "------------------------------------------------------" End End Send WriteString of ghoApplication CS_UnusedVariables CS_NoOfErrorFiles iNoOfErrorFiles End_Procedure End_Class