Code archives/File Utilities/Semi-automatic 2 variable replacement tool

This code has been declared by its author to be Public Domain code.

Download source code

Semi-automatic 2 variable replacement tool by ImmutableOctet2013
This utility goes through all files/folders in a directory, and checks if they're using the extensions specified. It then looks through each line of the file(s) it finds, and replaces all code meeting the specified criteria. In my situation, it'll replace all instances of ListAddLast(List, Object), with List.AddLast(Object). It's a bit limited, but pretty functional if you ask me.

Extra information:
This code will automatically generate a configuration file for you, so don't bother making one. All you'll need to do is edit the file it generates. Feel free to post your thoughts on this tool, and please report any bugs you find.

This software is provided as-is, I do not take any responsibility for any damage it may cause.
' The backstory(Look ahead for the actual code):
Rem
	A little while back I started working on a game in BlitzMax,
	I worked on it with full intent of porting it to Monkey later down the road.
	However, I used the ListAddLast command, which doesn't work in Monkey the same way.
	As far as I know, it's not possible to make a wrapper for this, so I decided to convert those bits from my Blitz Max code.
	
	Instead of doing it myself, I made this:
End Rem

' Enable strict or SuperStrict
SuperStrict

' Framework
Framework BRL.Blitz

' Imports:
Import BRL.Stream
Import BRL.StandardIO
Import BRL.FileSystem
Import BRL.Retro
Import BRL.PolledInput

' Globals:
Global ConsoleOutput:Byte = True

' Start the program
Main()

' The main program
Function Main:Int()
	' Read our configuration file(If it doesn't exist, it'll be created)
	Local ConfigData:String[] = ReadConfig("config.txt")

	' Define our original data's string:
	Local Data:String = ConfigData[0]
	
	' Define our new data's string:
	Local NewData:String = ConfigData[1]
	
	' Define our location data:
	Local Files:String = ConfigData[2]
	Local _Files:String[]
	
	' Check to see if our Files variable is set to the AppArgs:
	If (Upper(Files) = "APPARGS") Then
		_Files = AppArgs
	Else
		_Files = [Files]
	EndIf
	
	' Define our recursion variable, and check our recursion setting
	Local Recursive:Int
	
	If (Upper(ConfigData[3]) <> "FALSE" And ConfigData[3] <> "0") Then
		Recursive = 1
	EndIf
	
	' Create our white list object
	Local WhiteList:TList = New TList
	Local WListStr:String
	
	' Check if we have any data coming in
	If (ConfigData[4] <> "") Then
		' Check if we have more than one file whitelisted:
		If (Instr(ConfigData[4], ",")) Then
			' If there's more than one file extension, then do the following:
			
			' Repeat until we're to the last white listed file extension
			Repeat
				' Add the calculated string to the white list:
				WListStr = Left(ConfigData[4], Instr(ConfigData[4], ",")-1)
				WhiteList.AddLast(WListStr)
				
				' Modify ConfigData[4], so it doesn't hold the data we added to the white list.
				ConfigData[4] = Right(ConfigData[4], Len(ConfigData[4]) - Len(WListStr) - 1)
				WListStr = ""
			Until Not Instr(ConfigData[4], ",")
			
			' Find our last file extension, and add it to the white list:
			If (ConfigData[4] <> "") Then
				WListStr = ConfigData[4]
				WhiteList.AddLast(WListStr)
				WListStr = ""
			EndIf
		Else
			' If we only have one entry, add it to the white list.
			WhiteList.AddLast(ConfigData[4])
		EndIf
	Else
		' If we didn't find anything, just add a blank string to the list.
		WhiteList.AddLast("")
	EndIf
	
	Console("Beginning search routine...")
	
	' Grab the file(s) using the extracted config data:
	ReplaceFiles(_Files, Data, NewData, Recursive, True, 0, WhiteList)
	
	' We're done, now wait for as long as needed(If ConsoleOutput is enabled):
	Console("Search routine finished.")
	If (ConsoleOutput = True) Then WaitChar()
End Function

Function ReadConfig:String[](Location:String)
	' Create an array holding all 5 return strings
	Local ReturnData:String[5]
	
	' Attempt to open a stream for the configuration.
	Local Stream:TStream = ReadStream(Location)
	
	' If we can't open the stream, create the file(s) we need, then close the program:
	If (Not Stream) Then
		Stream = WriteStream(Location)
		
		Stream.WriteLine("File = AppArgs")
		Stream.WriteLine("Original = " + Chr(34) + "(*1, *2)" + Chr(34))
		Stream.WriteLine("Replaced = " + Chr(34) + "(*2, *1)" + Chr(34))
		Stream.WriteLine("Recursive = True")
		Stream.WriteLine("WhiteList = " + Chr(34) + "txt" + Chr(34))
		
		CloseStream(Stream)

		End
	EndIf
	
	' Define the needed local variables:
	Local InLine:String
	Local File:String = "AppArgs"
	Local Original:String = "(*1, *2)"
	Local Replaced:String = "(*2, *1)"
	Local Recursive:String = "1"
	Local WList:String = "txt"
	
	' Look through each line of the file:
	Repeat
		' Read the current line.
		InLine = Stream.ReadLine()
		
		' Check if we're dealing with a setting, or unneeded text:
		If (Instr(InLine, "=")) Then
			' Apply the settings as needed(I'm too lazy to give this section comments):
			
			If (Instr(Upper(InLine), "FILE") Or Instr(Upper(InLine), "FILES")) Then
				File = Right(InLine, Len(InLine) - Instr(InLine, "="))
				If (Instr(File, Chr(34))) Then
					File = Right(File, Len(File) - Len(Left(File, Instr(File, Chr(34))-1)))
					File = Replace(File, Chr(34), "")
				Else
					File = "AppArgs"
				EndIf
			EndIf
			
			If (Instr(Upper(InLine), "ORIGINAL")) Then
				Local _Original:String = Right(InLine, Len(InLine) - Instr(InLine, "="))
				If (Instr(_Original, Chr(34))) Then
					Original = _Original
					
					Original = Right(Original, Len(Original) - Len(Left(Original, Instr(Original, Chr(34))-1)))
					Original = Replace(Original, Chr(34), "")
				Else
					RuntimeError("Please use quotes on 'Original' & Replaced")
				EndIf
			EndIf
			
			If (Instr(Upper(InLine), "REPLACED")) Then
				Local _Replaced:String = Right(InLine, Len(InLine) - Instr(InLine, "="))
				If (Instr(_Replaced, Chr(34))) Then
					Replaced = _Replaced
					
					Replaced = Right(Replaced, Len(Replaced) - Len(Left(Replaced, Instr(Replaced, Chr(34))-1)))
					Replaced = Replace(Replaced, Chr(34), "")
				Else
					RuntimeError("Please use quotes on 'Replaced' & Original")
				EndIf
			EndIf
			
			If (Instr(Upper(InLine), "RECURSIVE")) Then
				Recursive = Right(InLine, Len(InLine) - Instr(InLine, "="))
				Recursive = Replace(Recursive, " ", "")
				Recursive = Replace(Recursive, Chr(34), "")
			EndIf
			
			If (Instr(Upper(InLine), "WHITELIST")) Then
				If (Instr(InLine, Chr(34))) Then
					WList = Right(InLine, Len(InLine) - Instr(InLine, "="))
					WList = Right(WList, Len(WList) - Instr(WList, Chr(34)))
					WList = Replace(WList, Chr(34), "")
					WList = Replace(WList, " ", "")
				Else
					RuntimeError("Please use quotes with the white list. Example: " + Chr(34) + "txt, xml" + Chr(34))
				EndIf
			EndIf
		EndIf
		
		InLine = ""
	Until Stream.Eof()
	
	' Close the configuration file's stream
	CloseStream(Stream)
	
	' Assign each ID in array to the needed information:
	ReturnData[0] = Original
	ReturnData[1] = Replaced
	ReturnData[2] = File
	ReturnData[3] = Recursive
	ReturnData[4] = WList
	
	' Return the array:
	Return ReturnData
End Function

Function ReplaceFiles:Int(S:String[], Data:String, NewData:String, Recursive:Int=1, SkipDots:Byte=True, Branch:Int=0, WhiteList:TList=Null)
	' A boolean used to check if we've found a file.
	Local FileFound:Byte = False
	
	' The number of files found.
	Local FileCount:Int = 0
	
	For Local File:String = EachIn S
		' If we found nothing, this file, or another EXE file, continue.
		If (File = AppFile Or File = "" Or Right(Lower(File), 4) = ".exe") Then Continue
		
		' Check if the file/directory/other exists:
		If (FileType(File) <> 0) Then
			' Select the file type of the 'File':
			Select FileType(File)
				Case FILETYPE_FILE ' 1
					' Run the ReplaceData function, and if it isn't false, set the FileFound variable to true, and add to the file-count:
					If (ReplaceData(File, Data, NewData, WhiteList)) Then
						FileFound = True
						FileCount :+ 1
					EndIf
				Default ' FILETYPE_DIR
					' Check for recursion:s
					If (Recursive = 1 Or Recursive = 0) Then
						' Nothing to see here:
						If (Branch <> 0) Then
							Console("Branching to more directories... (Branch " + (Branch+1) + " -> Branch " + (Branch+2) + ")")
						Else
							Console("Branching to more directories... (Origin -> Branch " + (Branch+1) + ")")
							Console("Branching to more directories... (Branch " + (Branch+1) + " -> Branch " + (Branch+2) + ")")
						EndIf
						
						' Run this command again:
						ReplaceFiles(LoadDir2(File, SkipDots), Data, NewData, 1-Recursive, SkipDots, Branch+1, WhiteList)
					EndIf
			End Select
		EndIf
	Next
	
	If (Branch <> 0) Then
		Console("")
		Console("Branch " + (Branch+1) + "'s results:")
	Else
		Console("")
		Console("First search branch's results:")
	EndIf
	
	If (FileFound = True) Then
		If (FileCount <> 1) Then
			Console(FileCount + " files have been found.")
		Else
			Console("Only " + FileCount + " file was found.")
		EndIf
	Else
		Console("No files were found...")
	EndIf
	
	Console("")
	If (Branch <> 0) Then
		Console("(Branch " + (Branch+1) + " -> Branch " + (Branch) + ")")
	Else
		Console("(Branch " + (Branch+1) + " -> Origin)")
	EndIf
End Function

' Decided to add to the LoadDir command for the recursion system:
Function LoadDir2:String[](File:String, SkipDots:Byte=True)
	If (Right(File, 1)<>"/" Or Right(File, 1)<>"\") Then
		If (Instr(File, "/")) Then
			File :+ "/"
		ElseIf (Instr(File, "\")) Then
			File :+ "\"
		EndIf
	EndIf
	
	Local Dir:String[] = LoadDir(File, SkipDots)
	
	Local ID:Int
	For Local S:String = EachIn Dir
		Dir[ID] = File + S
		ID :+ 1
	Next
	
	Return Dir
End Function

Function ReplaceData:Byte(File:String, Data:String, NewData:String, WhiteList:TList=Null)
	' Start our in and out streams:
	
	' Nothing to see here:
	If (WhiteList) Then
		Local WListResponse:Byte
		For Local WListString:String = EachIn WhiteList
			If (WListString <> "") Then
				If (Left(WListString, 1) = ".") Then
					If (Instr(File, WListString)) Then
						WListResponse = True
						Exit
					EndIf
				Else
					If (Instr(File, "." + WListString)) Then
						WListResponse = True
						Exit
					EndIf
				EndIf
			Else
				WListResponse = True
				Exit
			EndIf
		Next
		
		If (WListResponse = False) Then Return False
	EndIf
	
	' Open the 'InStream' for our file:
	Local InStream:TStream = ReadStream(File)
	
	If (Not InStream) Then RuntimeError("Unable to open '" + File + "'")
	
	' If this file has "_Replaced" in it, skip it.
	If (Instr(File, "_Replaced")) Then CloseStream(InStream) ; Return False
	
	' Open the 'OutStream'(This also adds an _Replaced to our filename)
	Local OutStream:TStream = WriteStream(Replace(File, Left(StripDir(File), Instr(StripDir(File), ".")), Left(StripDir(File), Instr(StripDir(File), ".")-1) + "_Replaced."))
	
	' Our Inline and Outline variables:
	Local InLine:String, OutLine:String
	
	' Variables holding the first, last, splitting characters for our variables:
	Local VarBeginner:String = "("
	Local VarSplitter:String = ","
	Local VarEnder:String = ")"

	' Grab the needed data:
	VarBeginner = Left(Right(Left(Data, Instr(Data, "*1")), 2), 1)
	VarSplitter = Left(Replace(Right(Data, Len(Data) - Instr(Data, "*1") - 1), " ", ""), 1)
	VarEnder = Left(Replace(Right(Data, Len(Data) - Instr(Data, "*2") - 1), " ", ""), 1)
	
	' Loop until the end of the 'InStream'
	Repeat
		If (InStream.Eof()) Then Exit
		
		' Read each line in the file:
		InLine = InStream.ReadLine()
		
		' Too annoying to explain, I don't suggest looking into this:
		If (Instr(InLine, Left(Data, Instr(Data, VarBeginner)))) Then
			Local Var1:String = Right(InLine, Len(InLine) - Instr(InLine, Left(Data, Instr(Data, VarBeginner))) + Len(Left(Data, Instr(Data, VarBeginner))))
			Var1 = Left(Var1, Instr(Var1, VarSplitter) - 1)
			Var1 = Right(Var1, Len(Var1) - Instr(Var1, VarBeginner))
			
			Local Var2:String = Replace(Right(InLine, Len(InLine) - Instr(InLine, Var1+VarSplitter)+Len(Var1)), " ", "")
			Var2 = Left(Var2, Instr(Var2, VarEnder) - 1)
			Var2 = Right(Var2, Len(Var2) - Instr(Var2, Var1+",") + 1)
			Var2 = Replace(Var2, Var1+VarSplitter, "")
			
			Local FinalData:String = Replace(Replace(NewData, "*1", Var1), "*2", Var2)
			
			OutLine = InLine
			OutLine = Replace(OutLine, Replace(Replace(Data, "*1", Var1), "*2", Var2), FinalData)
		Else
			OutLine = InLine
		EndIf
		
		' Write the edited, or non-edited line to the 'OutStream'
		OutStream.WriteLine(OutLine)
		
		InLine = ""
		OutLine = ""
	Forever
	
	' Close the in and out streams:
	CloseStream(InStream)
	CloseStream(OutStream)
	
	' Return True:
	Return True
End Function

Function Console:Int(S:String)
	' If we have it enabled, print:
	If (ConsoleOutput = True) Then Print(S)
End Function

Comments

Brucey2013
Cool, so it does something similar to :

perl -pi -w -e 's/ListAddLast\((.*)\,(.*)\)/$1.AddLast($2)/g' *.bmx



Code Archives Forum