Debug on stderr, changed output file splitting
parent
a4e9e466dd
commit
d967f1de17
|
@ -13,7 +13,7 @@ __copyright__ = "Copyright © 2022 Nicholas Hope. See LICENSE for details."
|
|||
import sys
|
||||
import os
|
||||
import argparse
|
||||
import tempfile
|
||||
from tempfile import NamedTemporaryFile
|
||||
import yaml
|
||||
from platform import system as getOS, release
|
||||
|
||||
|
@ -42,80 +42,88 @@ def parseFile(inputFile):
|
|||
else:
|
||||
parseConfig(config)
|
||||
|
||||
def writeOutput(scriptString, className):
|
||||
"""Given the string of stuff to write, write it out to the given handle."""
|
||||
def writeOutput(data, className) -> 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
|
||||
"""
|
||||
global args
|
||||
global targetDir
|
||||
chunksize = 2**20 # 1Mb maximum cfg file size
|
||||
chunk = 1
|
||||
# If the string is more than 1048576 bytes, we need divide it into files that each
|
||||
# are less than 1048576 bytes
|
||||
chunksneeded = int( 1 + len(scriptString) / chunksize )
|
||||
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 {chunksneeded} files for {className}')
|
||||
print( f'DEBUG: need {filesNeeded} files for {className}', file=sys.stderr)
|
||||
|
||||
if( chunksneeded == 1):
|
||||
# If it can be done in one chunk, do it in one chunk.
|
||||
outfile = tempfile.NamedTemporaryFile( prefix=className, delete=False )
|
||||
outfile.write(scriptString.encode("utf8"))
|
||||
outfile.close()
|
||||
if( args.dry_run != True ):
|
||||
os.replace(outfile.name, f'{targetDir}/{className}_script_{chunk:02d}.cfg')
|
||||
FilNedLen = len(str(filesNeeded))
|
||||
# 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
|
||||
reservedSpace = len('%s_script_%0*d.cfg' % (className, FilNedLen, fileNum)) + 4
|
||||
|
||||
# Initialize the variables
|
||||
outfile = NamedTemporaryFile(prefix=className, delete=False)
|
||||
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)).encode('utf8') )
|
||||
bytesWritten += reservedSpace
|
||||
if args.debug:
|
||||
print( f'DEBUG: Created {targetDir}/{className}_script_{chunk:02d}.cfg')
|
||||
else:
|
||||
if args.debug:
|
||||
print( f'DEBUG: {outfile.name} would be {targetDir}/{className}_script_{chunk:02d}.cfg')
|
||||
else:
|
||||
# Gotta do it in multiple chunks
|
||||
classLines = scriptString.splitlines()
|
||||
execString = f'exec {className}_script_{chunk:02d}'.encode("utf8")
|
||||
# extra 4 bytes is just a little buffer so we don't get exactly chunksize bytes
|
||||
reservedSpace = len(execString) + 4
|
||||
n = 0
|
||||
pieces = {}
|
||||
while( chunk <= chunksneeded ):
|
||||
outfile = tempfile.NamedTemporaryFile( prefix=className, delete=False )
|
||||
pieces[outfile.name] = f'{targetDir}/{className}_script_{chunk:02d}.cfg'
|
||||
byteswritten = 0
|
||||
while( n < len(classLines) and (byteswritten + len(classLines[n]) + reservedSpace) < chunksize ):
|
||||
line = classLines[n].encode("utf8") + os.linesep.encode("utf8")
|
||||
outfile.write(line)
|
||||
byteswritten += len(line)
|
||||
n+=1
|
||||
if( chunk < chunksneeded ):
|
||||
line = f'exec {className}_script_{chunk+1:02d}'.encode("utf8") + os.linesep.encode("utf8")
|
||||
outfile.write(line)
|
||||
byteswritten += len(line)
|
||||
outfile.close()
|
||||
print( f'DEBUG: Wrote {bytesWritten} bytes to {className} ({fileNum}/{filesNeeded})', file=sys.stderr)
|
||||
|
||||
if args.debug:
|
||||
print( f'DEBUG: Wrote {byteswritten} bytes to {className} ({chunk}/{chunksneeded})' )
|
||||
chunk += 1
|
||||
for tmpname, realname in pieces.items():
|
||||
if( args.dry_run ):
|
||||
if( args.debug ):
|
||||
print( f'DEBUG: {outfile.name} would be {targetDir}/{className}_script_{chunk:02d}.cfg')
|
||||
else:
|
||||
os.replace(tmpname, realname)
|
||||
print( f'DEBUG: Created {targetDir}/{className}_script_{chunk:02d}.cfg')
|
||||
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."""
|
||||
global args
|
||||
global targetDir
|
||||
# Make sure the target exists before we try to use it
|
||||
if os.path.isdir( targetDir ) == False:
|
||||
try:
|
||||
os.mkdir( targetDir )
|
||||
if args.debug:
|
||||
print( f'DEBUG: created {targetDir}')
|
||||
except Exception as fileExcept:
|
||||
print( f'WARN: Failed to create {targetDir}: {fileExcept.strerror}\nUsing current directory instead.' )
|
||||
targetDir = '.'
|
||||
|
||||
if os.path.isdir(targetDir) == False:
|
||||
os.mkdir(targetDir)
|
||||
if args.debug:
|
||||
print(f"DEBUG: Created", file=sys.stderr)
|
||||
|
||||
tempsAndReals = {}
|
||||
|
||||
for currentClass in config:
|
||||
classDict = config[currentClass]
|
||||
stringToWrite = tfscript.makeCFG(classDict)
|
||||
writeOutput(stringToWrite, currentClass)
|
||||
replaceDict = writeOutput(stringToWrite, currentClass)
|
||||
tempsAndReals.update(replaceDict)
|
||||
|
||||
for tmpName, realName in tempsAndReals.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)
|
||||
|
||||
def parseCLI():
|
||||
# Handle command line
|
||||
|
@ -178,6 +186,8 @@ def main():
|
|||
targetDir += "steamapps/common/Team Fortress 2/tf/cfg"
|
||||
elif args.force:
|
||||
# Unsupported OS but -f specified
|
||||
if args.debug:
|
||||
print("DEBUG: forced to continue, output set to current directory", file=sys.stderr)
|
||||
targetDir = '.'
|
||||
else:
|
||||
# Unsupported OS and not forced to continue
|
||||
|
|
Loading…
Reference in New Issue