Fixed non-funtional code, incorporated new file
parent
cb3c248335
commit
b39528af75
|
@ -123,16 +123,16 @@ def simpleHold(key, instruction):
|
||||||
return f'bind {key} "{instruction}"\n'
|
return f'bind {key} "{instruction}"\n'
|
||||||
|
|
||||||
def listHold(key, options):
|
def listHold(key, options):
|
||||||
press_str = options["press"]
|
pressStr = options["press"]
|
||||||
if isinstance(press_str, list):
|
if isinstance(pressStr, list):
|
||||||
press_str = ';'.join(press_str)
|
pressStr = ';'.join(pressStr)
|
||||||
|
|
||||||
release_str = options["release"]
|
releaseStr = options["release"]
|
||||||
if isinstance(release_str, list):
|
if isinstance(releaseStr, list):
|
||||||
release_str = ';'.join(release_str)
|
releaseStr = ';'.join(releaseStr)
|
||||||
|
|
||||||
ret = f'alias +{key}_bind "{press_str}"\n' +\
|
ret = f'alias +{key}_bind "{pressStr}"\n' +\
|
||||||
f'alias -{key}_bind "{release_str}"\n' +\
|
f'alias -{key}_bind "{releaseStr}"\n' +\
|
||||||
f'bind {key} "+{key}_bind"\n'
|
f'bind {key} "+{key}_bind"\n'
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
@ -148,29 +148,29 @@ def toggle(key, instruction):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def double(key, options):
|
def double(key, options):
|
||||||
prim_action = options["primary"]
|
primaryAction = options["primary"]
|
||||||
pBindType = firstTypeIn(prim_action.keys())
|
pBindType = firstTypeIn(primaryAction.keys())
|
||||||
pBindContent = prim_action[pBindType]
|
pBindContent = primaryAction[pBindType]
|
||||||
|
|
||||||
sec_action = options["secondary"]
|
secAction = options["secondary"]
|
||||||
sBindType = firstTypeIn(sec_action.keys())
|
sBindType = firstTypeIn(secAction.keys())
|
||||||
sBindContent = sec_action[sBindType]
|
sBindContent = secAction[sBindType]
|
||||||
|
|
||||||
main_str = f'{key}_main'
|
mainStr = f'{key}_main'
|
||||||
alt_str = f'{key}_alt'
|
altStr = f'{key}_alt'
|
||||||
tog_str = f'toggle_{key}'
|
togStr = f'toggle_{key}'
|
||||||
|
|
||||||
recursive_code = branch(main_str, pBindContent, pBindType) +\
|
recursiveCode = branch(mainStr, pBindContent, pBindType) +\
|
||||||
branch(alt_str, sBindContent, sBindType)
|
branch(altStr, sBindContent, sBindType)
|
||||||
|
|
||||||
newcode = []
|
newcode = []
|
||||||
for line in recursive_code.split('\n'):
|
for line in recursiveCode.split('\n'):
|
||||||
# For every line gotten by the recursive call, change all "bind"s to "alias",
|
# For every line gotten by the recursive call, change all "bind"s to "alias",
|
||||||
# since main_str and alt_str aren't valid bind targes
|
# since mainStr and altStr aren't valid bind targes
|
||||||
llist = line.split(' ')
|
llist = line.split(' ')
|
||||||
for i in range(len(llist)):
|
for i in range(len(llist)):
|
||||||
alphanum_chars = ''.join(c for c in llist[i] if c.isalnum())
|
alphanumChars = ''.join(c for c in llist[i] if c.isalnum())
|
||||||
if alphanum_chars == 'bind':
|
if alphanumChars == 'bind':
|
||||||
if llist[i][0].isalnum():
|
if llist[i][0].isalnum():
|
||||||
llist[i] = 'alias'
|
llist[i] = 'alias'
|
||||||
else:
|
else:
|
||||||
|
@ -180,23 +180,23 @@ def double(key, options):
|
||||||
newcode.append(' '.join(llist))
|
newcode.append(' '.join(llist))
|
||||||
|
|
||||||
ret = '\n'.join(newcode) +\
|
ret = '\n'.join(newcode) +\
|
||||||
f'alias +{tog_str} "bind {key} {alt_str}"\n' +\
|
f'alias +{togStr} "bind {key} {altStr}"\n' +\
|
||||||
f'alias -{tog_str} "bind {key} {main_str}"\n'+\
|
f'alias -{togStr} "bind {key} {mainStr}"\n'+\
|
||||||
f'bind {key} "{main_str}"\n'
|
f'bind {key} "{mainStr}"\n'
|
||||||
|
|
||||||
cond_name = options["condition"]
|
condName = options["condition"]
|
||||||
|
|
||||||
if cond_name in condDict:
|
if condName in condDict:
|
||||||
# If the condition key (like "mouse4") already has toggles,
|
# If the condition key (like "mouse4") already has toggles,
|
||||||
# just append another toggle string (it gets encased in quotes later)
|
# just append another toggle string (it gets encased in quotes later)
|
||||||
condDict[cond_name]["plus_toggles"] += f"; +{tog_str}"
|
condDict[condName]["plus_toggles"] += f"; +{togStr}"
|
||||||
condDict[cond_name]["minus_toggles"] += f"; -{tog_str}"
|
condDict[condName]["minus_toggles"] += f"; -{togStr}"
|
||||||
else:
|
else:
|
||||||
# If the condition key doesn't already exist, make it with the correct values
|
# If the condition key doesn't already exist, make it with the correct values
|
||||||
condDict.update( {
|
condDict.update( {
|
||||||
cond_name: {
|
condName: {
|
||||||
"plus_toggles": f"+{tog_str}",
|
"plus_toggles": f"+{togStr}",
|
||||||
"minus_toggles": f"-{tog_str}"
|
"minus_toggles": f"-{togStr}"
|
||||||
}
|
}
|
||||||
} )
|
} )
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ import os
|
||||||
import argparse
|
import argparse
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
import yaml
|
import yaml
|
||||||
from platform import system as OSName, OSRelease
|
from platform import system as GetOSName, release as GetOSRelease
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import winreg
|
import winreg
|
||||||
|
@ -26,79 +26,25 @@ except ModuleNotFoundError:
|
||||||
# Local libraries
|
# Local libraries
|
||||||
import tfscript
|
import tfscript
|
||||||
from tfscript import verify
|
from tfscript import verify
|
||||||
|
from tfscript import writing
|
||||||
|
|
||||||
args = {}
|
args = {}
|
||||||
targetDir = ""
|
targetDir = ""
|
||||||
|
|
||||||
def parseFile(inputFile):
|
def parseFile(inputFile) -> (dict, dict):
|
||||||
"""Parse, verify, and do the conversion."""
|
"""Parse, verify, and do the conversion."""
|
||||||
config = yaml.safe_load(inputFile)
|
config = yaml.safe_load(inputFile)
|
||||||
|
|
||||||
# See verify.py
|
# See verify.py
|
||||||
config = tfscript.verify.verifyConfig(config)
|
config, aliases = verify.verifyConfig(config)
|
||||||
if "errors" in config:
|
if "errors" in config:
|
||||||
for e in config["errors"]:
|
for e in config["errors"]:
|
||||||
print(e,file=sys.stderr)
|
print(e, file=sys.stderr)
|
||||||
|
return None, None
|
||||||
else:
|
else:
|
||||||
parseConfig(config)
|
return config, aliases
|
||||||
|
|
||||||
def writeOutput(data, className) -> dict:
|
def parseConfig(config, defaults):
|
||||||
"""
|
|
||||||
Write `data' to various files as needed, returning a dict of
|
|
||||||
the temporary file names and their target destination names,
|
|
||||||
not including the target directory
|
|
||||||
"""
|
|
||||||
global args
|
|
||||||
namesDict = {} # return dict
|
|
||||||
|
|
||||||
# Variables
|
|
||||||
lineList = [ l.encode('utf8') for l in data.splitlines() ]
|
|
||||||
fileNum = 1
|
|
||||||
bytesWritten = 0
|
|
||||||
|
|
||||||
# Constants
|
|
||||||
maxFileSize = 2 ** 20 # 1MiB maximum cfg file size
|
|
||||||
filesNeeded = 1 + int( len(data)/maxFileSize )
|
|
||||||
if args.debug:
|
|
||||||
print( f'DEBUG: need {filesNeeded} files for {className}', file=sys.stderr)
|
|
||||||
|
|
||||||
FilNedLen = len(str(filesNeeded))
|
|
||||||
reservedSpace = len(f'{className}_script_{filesNeeded}.cfg') + 4
|
|
||||||
|
|
||||||
# Initialize variables
|
|
||||||
outfile = NamedTemporaryFile(prefix=className, delete=False)
|
|
||||||
# I know % formatting is old-school and pylint hates it,
|
|
||||||
# but "%*d" is the easiest way to left-pad with zeros
|
|
||||||
# without hardcoding a number. The extra 4 bytes is just some leeway
|
|
||||||
namesDict.update({ outfile.name: '%s_script_%0*d.cfg' % (className, FilNedLen, fileNum) })
|
|
||||||
|
|
||||||
while (fileNum <= filesNeeded and len(lineList) > 0):
|
|
||||||
line = lineList.pop(0) + '\n'.encode('utf8')
|
|
||||||
lineLen = len(line) # nice
|
|
||||||
|
|
||||||
if bytesWritten + reservedSpace + lineLen > maxFileSize:
|
|
||||||
outfile.write( ('exec %s_script_%0*d' % (className, FilNedLen, fileNum+1)).encode('utf8') )
|
|
||||||
bytesWritten += reservedSpace
|
|
||||||
if args.debug:
|
|
||||||
print( f'DEBUG: Wrote {bytesWritten} bytes to {className} ({fileNum}/{filesNeeded})', file=sys.stderr)
|
|
||||||
|
|
||||||
outfile.close()
|
|
||||||
outfile = NamedTemporaryFile(prefix=className, delete=False)
|
|
||||||
|
|
||||||
fileNum += 1
|
|
||||||
namesDict.update({ outfile.name: '%s_script_%0*d.cfg' % (className, FilNedLen, fileNum) })
|
|
||||||
bytesWritten = 0
|
|
||||||
|
|
||||||
outfile.write(line)
|
|
||||||
bytesWritten += lineLen
|
|
||||||
|
|
||||||
outfile.close() # the most-recent tempfile will not have been closed
|
|
||||||
if args.debug:
|
|
||||||
print( f'DEBUG: Wrote {bytesWritten} bytes to {className} ({fileNum}/{filesNeeded})', file=sys.stderr)
|
|
||||||
|
|
||||||
return namesDict
|
|
||||||
|
|
||||||
def parseConfig(config):
|
|
||||||
"""With validated data structure, write out all the files."""
|
"""With validated data structure, write out all the files."""
|
||||||
global args
|
global args
|
||||||
global targetDir
|
global targetDir
|
||||||
|
@ -110,20 +56,18 @@ def parseConfig(config):
|
||||||
|
|
||||||
tempsAndReals = {}
|
tempsAndReals = {}
|
||||||
|
|
||||||
|
if defaults is not None:
|
||||||
|
stringToWrite = tfscript.makeCFG(defaults)
|
||||||
|
replaceDict = writing.writeOutput(stringToWrite, "default", args)
|
||||||
|
tempsAndReals.update(replaceDict)
|
||||||
|
|
||||||
for currentClass in config:
|
for currentClass in config:
|
||||||
classDict = config[currentClass]
|
classDict = config[currentClass]
|
||||||
stringToWrite = tfscript.makeCFG(classDict)
|
stringToWrite = tfscript.makeCFG(classDict)
|
||||||
replaceDict = writeOutput(stringToWrite, currentClass)
|
replaceDict = writing.writeOutput(stringToWrite, currentClass, args)
|
||||||
tempsAndReals.update(replaceDict)
|
tempsAndReals.update(replaceDict)
|
||||||
|
|
||||||
for tmpName, realName in tempsAndReals.items():
|
return tempsAndReals
|
||||||
if args.dry_run:
|
|
||||||
if args.debug:
|
|
||||||
print( f'DEBUG: {tmpName} would be {targetDir}/{realName}.cfg', file=sys.stderr)
|
|
||||||
else:
|
|
||||||
os.replace( tmpName, f'{targetDir}/{realName}' )
|
|
||||||
if args.debug:
|
|
||||||
print( f'DEBUG: Created {targetDir}/{realName}', file=sys.stderr)
|
|
||||||
|
|
||||||
def parseCLI():
|
def parseCLI():
|
||||||
# Handle command line
|
# Handle command line
|
||||||
|
@ -143,7 +87,33 @@ def parseCLI():
|
||||||
help='File containing YAML to convert.')
|
help='File containing YAML to convert.')
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def main():
|
def getTargetDir(systemName):
|
||||||
|
if systemName == "Darwin":
|
||||||
|
if float( '.'.join( GetOSRelease().split('.')[0:2] ) ) >= 10.15:
|
||||||
|
if not args.force:
|
||||||
|
print(
|
||||||
|
"As of macOS Catalina (v10.15), 32-bit applications "
|
||||||
|
"such as tf2 do not work, so tfscript does not function",
|
||||||
|
file=sys.stderr
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return os.path.expanduser("~/Library/Application Support/Steam/")
|
||||||
|
|
||||||
|
elif systemName == "Windows":
|
||||||
|
# oh god why do we have to use the registry
|
||||||
|
accessReg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
|
||||||
|
accessKey = winreg.OpenKey(accessReg, "SOFTWARE\\WOW6432Node\\Valve\\Steam\\")
|
||||||
|
return winreg.QueryValue(accessKey, "InstallPath")
|
||||||
|
|
||||||
|
elif systemName == "Linux":
|
||||||
|
return os.path.expanduser("~/.local/Steam")
|
||||||
|
|
||||||
|
elif systemName == "Java":
|
||||||
|
print("Java-based OSes are not supported yet by tfscript.", file=sys.stderr)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
""" Command line interface. """
|
""" Command line interface. """
|
||||||
global args
|
global args
|
||||||
global targetDir
|
global targetDir
|
||||||
|
@ -153,31 +123,9 @@ def main():
|
||||||
if args.directory is not None:
|
if args.directory is not None:
|
||||||
targetDir = args.directory
|
targetDir = args.directory
|
||||||
else:
|
else:
|
||||||
systemName = OSName()
|
systemName = GetOSName()
|
||||||
if systemName == "Darwin":
|
targetDir = getTargetDir(systemName)
|
||||||
if float( '.'.join(OSRelease().split('.')[0:2]) ) >= 10.15:
|
if targetDir is not None:
|
||||||
if not args.force:
|
|
||||||
print(
|
|
||||||
"As of macOS Catalina (v10.15), 32-bit applications "
|
|
||||||
"such as tf2 do not work, so tfscript does not function",
|
|
||||||
file=sys.stderr
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
targetDir = os.path.expanduser("~/Library/Application Support/Steam/")
|
|
||||||
|
|
||||||
elif systemName == "Windows":
|
|
||||||
# oh god why do we have to use the registry
|
|
||||||
accessReg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
|
|
||||||
accessKey = winreg.OpenKey(accessReg, "SOFTWARE\\WOW6432Node\\Valve\\Steam\\")
|
|
||||||
targetDir = winreg.QueryValue(accessKey, "InstallPath")
|
|
||||||
|
|
||||||
elif systemName == "Linux":
|
|
||||||
targetDir = os.path.expanduser("~/.local/Steam")
|
|
||||||
|
|
||||||
elif systemName == "Java":
|
|
||||||
print("Java-based OSes are not supported yet by tfscript.", file=sys.stderr)
|
|
||||||
|
|
||||||
if targetDir != "":
|
|
||||||
# Supported OS: add steamapps path
|
# Supported OS: add steamapps path
|
||||||
if targetDir[-1] != '/':
|
if targetDir[-1] != '/':
|
||||||
targetDir += '/'
|
targetDir += '/'
|
||||||
|
@ -191,7 +139,14 @@ def main():
|
||||||
# Unsupported OS and not forced to continue
|
# Unsupported OS and not forced to continue
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
parseFile(args.infile)
|
config, defaults = parseFile(args.infile)
|
||||||
|
if config is None:
|
||||||
|
return 2
|
||||||
|
|
||||||
|
fileNames = parseConfig(config, defaults)
|
||||||
|
fileList = writing.replaceFiles(targetDir, fileNames, args)
|
||||||
|
# writing.appendToActuals(targetDir, fileList)
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -1,34 +1,84 @@
|
||||||
"""Verify all the things that could go wrong."""
|
"""Verify all the things that could go wrong."""
|
||||||
def verifyConfig(cfg: dict):
|
def verifyConfig(cfg: dict) -> (dict, dict):
|
||||||
|
verifiedConfig = {}
|
||||||
|
|
||||||
# Do aliases first
|
# Do defaults first
|
||||||
aliasErrors = []
|
aliasErrors = []
|
||||||
|
|
||||||
aliases = None
|
defaults = None
|
||||||
|
|
||||||
if "aliases" in cfg:
|
if "default" in cfg:
|
||||||
aliases = cfg.pop("aliases")
|
defaults = cfg.pop("default")
|
||||||
for key, data in aliases.items():
|
for key, data in defaults.items():
|
||||||
errMessages = validBind(key, data, alias = True)
|
isAlias = ("alias" in data and data["alias"] == True)
|
||||||
|
errMessages = validBind(key, data, alias = isAlias)
|
||||||
if len(errMessages) > 0:
|
if len(errMessages) > 0:
|
||||||
for msg in errMessages:
|
for msg in errMessages:
|
||||||
aliasErrors.append(f"Error in aliases: {msg}")
|
aliasErrors.append(f"Error in defaults: {msg}")
|
||||||
|
|
||||||
errors = []
|
classList = [
|
||||||
|
"scout",
|
||||||
|
"soldier",
|
||||||
|
"pyro",
|
||||||
|
("demo","demoman"),
|
||||||
|
("engi","engineer"),
|
||||||
|
("heavy","heavyweapons"),
|
||||||
|
"medic",
|
||||||
|
"sniper",
|
||||||
|
"spy"
|
||||||
|
]
|
||||||
|
|
||||||
for cclass in cfg:
|
errors = aliasErrors.copy()
|
||||||
for key, data in cfg[cclass].items():
|
|
||||||
errMessages = validBind(key, data)
|
|
||||||
|
for cclass in classList:
|
||||||
|
classCFG = None
|
||||||
|
className = cclass
|
||||||
|
if isinstance(cclass, str) and cclass in cfg:
|
||||||
|
classCFG = cfg.pop(cclass)
|
||||||
|
elif isinstance(cclass, tuple):
|
||||||
|
for tupClass in cclass:
|
||||||
|
if tupClass in cfg:
|
||||||
|
classCFG = cfg.pop(tupClass)
|
||||||
|
className = cclass[0]
|
||||||
|
break
|
||||||
|
if classCFG is None:
|
||||||
|
# Invalid class, this gets caught later.
|
||||||
|
# It may be less efficient this way, but
|
||||||
|
# it makes for more descriptive error messages
|
||||||
|
continue
|
||||||
|
for key, data in classCFG.items():
|
||||||
|
errMessages = []
|
||||||
|
isAlias = False
|
||||||
|
if "alias" in data:
|
||||||
|
isAlias = data["alias"]
|
||||||
|
if not isinstance(isAlias, bool):
|
||||||
|
errMessages.append(f'Key "{key}" has alias not set to true or false. Did you accidentally put it in quotes?')
|
||||||
|
errMessages.extend( validBind(key, data, alias = isAlias) )
|
||||||
if len(errMessages) > 0:
|
if len(errMessages) > 0:
|
||||||
for msg in errMessages:
|
for msg in errMessages:
|
||||||
errors.append(f"Error in {cclass}: {msg}")
|
errors.append(f"Error in {cclass}: {msg}")
|
||||||
|
verifiedConfig.update({className: classCFG})
|
||||||
|
|
||||||
errors += aliasErrors
|
# Turn list into only strings by expanding tuples
|
||||||
|
for i, clss in enumerate(classList):
|
||||||
|
if isinstance(clss, tuple):
|
||||||
|
classList.insert(i+1, clss[0])
|
||||||
|
classList.insert(i+1, clss[1])
|
||||||
|
classList.pop(i)
|
||||||
|
|
||||||
|
for remainingClass in cfg:
|
||||||
|
if remainingClass not in classList:
|
||||||
|
errors.append(f'Error in {remainingClass}: "{remainingClass}" is not a valid class')
|
||||||
|
else:
|
||||||
|
otherName = findTwin(remainingClass)
|
||||||
|
if otherName is not None:
|
||||||
|
errors.append(f'Error in {remainingClass}: conflicting names for section: "{remainingClass}" and "{otherName}"')
|
||||||
|
|
||||||
if len(errors) > 0:
|
if len(errors) > 0:
|
||||||
cfg.update({"errors": errors})
|
verifiedConfig.update({"errors": errors})
|
||||||
|
|
||||||
return cfg
|
return verifiedConfig, defaults
|
||||||
|
|
||||||
def validBind(key, data, alias = False) -> list:
|
def validBind(key, data, alias = False) -> list:
|
||||||
"""Check for valid key and valid binding"""
|
"""Check for valid key and valid binding"""
|
||||||
|
@ -97,7 +147,7 @@ def removeRelaventFields(data, bindType):
|
||||||
# These types are simple, just the bind type and argument
|
# These types are simple, just the bind type and argument
|
||||||
if bindType in ["impulse", "toggle"]:
|
if bindType in ["impulse", "toggle"]:
|
||||||
data.pop(bindType)
|
data.pop(bindType)
|
||||||
|
|
||||||
elif bindType == "hold":
|
elif bindType == "hold":
|
||||||
content = data.pop("hold")
|
content = data.pop("hold")
|
||||||
if isinstance(content, dict):
|
if isinstance(content, dict):
|
||||||
|
@ -152,10 +202,28 @@ def removeRelaventFields(data, bindType):
|
||||||
if "interval" not in content:
|
if "interval" not in content:
|
||||||
errMsgs.append("Repeat requires interval")
|
errMsgs.append("Repeat requires interval")
|
||||||
else:
|
else:
|
||||||
interval = content["interval"]
|
intervalStr = content["interval"]
|
||||||
if interval < 0:
|
if unit == "s":
|
||||||
errMsgs.append("Repeat interval cannot be negative")
|
interval = float(intervalStr)
|
||||||
if (unit == "t" and not isinstance(interval, int)):
|
else:
|
||||||
errMsgs.append("Repeat interval must be integer if unit is ticks")
|
interval = int(intervalStr)
|
||||||
|
if interval <= 0:
|
||||||
|
errMsgs.append("Repeat interval must be positive")
|
||||||
|
elif interval <= 200/6:
|
||||||
|
errMsgs.append(f"Repeat interval must be greater than 1 tick (approx. {200/6:.3f}s)")
|
||||||
|
|
||||||
return data, errMsgs
|
return data, errMsgs
|
||||||
|
|
||||||
|
def findTwin(className):
|
||||||
|
classDict = {
|
||||||
|
"demo": "demoman",
|
||||||
|
"engi": "engineer",
|
||||||
|
"heavy": "heavyweapons"
|
||||||
|
}
|
||||||
|
for className1, className2 in classDict.items():
|
||||||
|
if className == className1:
|
||||||
|
return className2
|
||||||
|
elif className == className2:
|
||||||
|
return className1
|
||||||
|
|
||||||
|
return None
|
|
@ -0,0 +1,82 @@
|
||||||
|
import os
|
||||||
|
from os.path import exists
|
||||||
|
from tempfile import NamedTemporaryFile
|
||||||
|
|
||||||
|
def writeOutput(data, className, args) -> dict:
|
||||||
|
"""
|
||||||
|
Write `data' to various files as needed, returning a dict of
|
||||||
|
the temporary file names and their target destination names,
|
||||||
|
not including the target directory
|
||||||
|
"""
|
||||||
|
namesDict = {} # return dict
|
||||||
|
|
||||||
|
# Variables
|
||||||
|
lineList = [ l.encode('utf8') for l in data.splitlines() ]
|
||||||
|
fileNum = 1
|
||||||
|
bytesWritten = 0
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
maxFileSize = 2 ** 20 # 1MiB maximum cfg file size
|
||||||
|
filesNeeded = 1 + int( len(data)/maxFileSize )
|
||||||
|
if args.debug:
|
||||||
|
print( f'DEBUG: need {filesNeeded} files for {className}', file=sys.stderr)
|
||||||
|
|
||||||
|
FilNedLen = len(str(filesNeeded))
|
||||||
|
reservedSpace = len(f'{className}_script_{filesNeeded}.cfg') + 4
|
||||||
|
|
||||||
|
# Initialize variables
|
||||||
|
outfile = NamedTemporaryFile(prefix=className, delete=False)
|
||||||
|
# I know % formatting is old-school and pylint hates it,
|
||||||
|
# but "%*d" is the easiest way to left-pad with zeros
|
||||||
|
# without hardcoding a number. The extra 4 bytes is just some leeway
|
||||||
|
namesDict.update({ outfile.name: '%s_script_%0*d.cfg' % (className, FilNedLen, fileNum) })
|
||||||
|
|
||||||
|
while (fileNum <= filesNeeded and len(lineList) > 0):
|
||||||
|
line = lineList.pop(0) + '\n'.encode('utf8')
|
||||||
|
lineLen = len(line) # nice
|
||||||
|
|
||||||
|
if bytesWritten + reservedSpace + lineLen > maxFileSize:
|
||||||
|
outfile.write( ('exec %s_script_%0*d' % (className, FilNedLen, fileNum+1)).encode('utf8') )
|
||||||
|
bytesWritten += reservedSpace
|
||||||
|
if args.debug:
|
||||||
|
print( f'DEBUG: Wrote {bytesWritten} bytes to {className} ({fileNum}/{filesNeeded})', file=sys.stderr)
|
||||||
|
|
||||||
|
outfile.close()
|
||||||
|
outfile = NamedTemporaryFile(prefix=className, delete=False)
|
||||||
|
|
||||||
|
fileNum += 1
|
||||||
|
namesDict.update({ outfile.name: '%s_script_%0*d.cfg' % (className, FilNedLen, fileNum) })
|
||||||
|
bytesWritten = 0
|
||||||
|
|
||||||
|
outfile.write(line)
|
||||||
|
bytesWritten += lineLen
|
||||||
|
|
||||||
|
outfile.close() # the most-recent tempfile will not have been closed
|
||||||
|
if args.debug:
|
||||||
|
print( f'DEBUG: Wrote {bytesWritten} bytes to {className} ({fileNum}/{filesNeeded})', file=sys.stderr)
|
||||||
|
|
||||||
|
return namesDict
|
||||||
|
|
||||||
|
def replaceFiles(targetDir, fileNames, args):
|
||||||
|
for tmpName, realName in fileNames.items():
|
||||||
|
if args.dry_run:
|
||||||
|
if args.debug:
|
||||||
|
print( f'DEBUG: {tmpName} would be {targetDir}/{realName}.cfg', file=sys.stderr)
|
||||||
|
else:
|
||||||
|
os.replace( tmpName, f'{targetDir}/{realName}' )
|
||||||
|
if args.debug:
|
||||||
|
print( f'DEBUG: Created {targetDir}/{realName}', file=sys.stderr)
|
||||||
|
|
||||||
|
return fileNames.values()
|
||||||
|
|
||||||
|
def execStrInFile(fileName, f):
|
||||||
|
execStr = f'exec {fileName}'
|
||||||
|
|
||||||
|
def appendToActuals(targetDir, fileList):
|
||||||
|
for currFile in fileList:
|
||||||
|
cfgName = targetDir + '/' + open(currFile.split('_')[0] + '.cfg', 'r')
|
||||||
|
if exists(cfgName):
|
||||||
|
if execStrInFile(currFile, cfgFile):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
pass
|
Loading…
Reference in New Issue