#!/usr/bin/python3.14

THIS_VERSION = "1.1"

import sys
import os
import shutil
import linuxcnc
import re
import datetime

import tkinter
from tkinter import messagebox

def copysection(block):
    #Just makes a straight copy of blocks that don't need any work
    regex = r"^\s*\[%s\](.+?)(?:^\s*\[|\Z)" % block
    section = re.search(regex, inistring, re.M | re.DOTALL)
    newini.write("\n[%s]" % block)
    if section != None:
        newini.write(section.group(1))
        all_sections.remove(block)
    else:
         newini.write("\n#No Content\n")

def writeifexists(file, section, src_item, dest_item = "None"):
    #Writes a new entry to the file, but only if it exists
    if dest_item == 'None': dest_item = src_item
    val = ini.find(section, src_item)
    if val: file.write("%s = %s\n" % (dest_item, val))

force = 0
dialogs = 0
subs = {}
subs2 = {}

filename = None
for opt in sys.argv[1:]:
    if opt == '-d':
        dialogs = 1
        r = tkinter.Tk()
        r.option_add('*Dialog.msg.font', 'Times 12')
        r.option_add('*Dialog.msg.wrapLength', '6i')

    elif opt == '-f':
        force = 1
    elif opt[0] == '-':
        print ("Unknown command line option to update_ini, exiting")
        exit()
    elif os.path.isfile(opt):
        filename = opt

if filename == None:
    t = """Usage: update_ini [-d] [-f] filename.ini\n
If the -d flag is used then a dialog box will be displayed
describing the purpose of this script, and giving the user the option
to change their minds\nIf the -f flag is used then no questions will be
asked and the conversion will proceed blindly"""
    if dialogs:
        messagebox.showerror('invalid options', str(t))
    elif not force:
        print(t)
    exit()

if dialogs:
    ret = messagebox._show("Confirm automatic update",
                           "This version of LinuxCNC separates the concepts of Axes and "
                           "Joints which necessitates changes to the INI and HAL files. "
                           "The changes required are described here:\n"
                           "http://linuxcnc.org/docs/2.9/html/ in the section "
                           "'Getting Started with LinuxCNC' -> 'Updating LinuxCNC'\n"
                           "The [EMC]VERSION data in your INI file indicates that your "
                           "configuration requires update.\n"
                           "A script exists that can attempt to automatically "
                           "reconfigure your configuration files.\nPress 'Yes' to perform "
                           "the conversion, 'No' to continue with the current configuration "
                           "files or 'Cancel' to exit LinuxCNC.\n"
                           "The process can not be automatically reversed, though a "
                           "backup version of your entire existing config will be created.",
                           messagebox.QUESTION, messagebox.YESNOCANCEL)
    if ret == 'cancel': exit(42)
    elif ret == 'no': exit(0)

# We want to work with the base INI file here, not the expanded version if #include is used
filename = re.sub(r'\.expanded', '', filename)

try:
    ini = linuxcnc.ini(filename)
except:
    t =  "%s is not a valid INI file" % filename
    if dialogs:
        messagebox.showerror('invalid options', t)
    elif not force:
        print(t)
    exit()

version = ini.find('EMC', 'VERSION')
if not version:
    version = "0.0"

if version == "$Revision$":
    pass
elif version >= THIS_VERSION:
    t =  """The supplied INI file is already at version %s and should not need
    updating""" % version
    if dialogs:
        messagebox.showerror('conversion not needed', t)
    elif not force:
        print(t)
    exit()

if ini.find('KINS', 'JOINTS') and not force and not version == "1.0":
    if dialogs:
        if messagebox.askquestion("Already Converted",
        "The supplied INI file already has a [KINS] section. this probably "
        "means that it was previously converted by hand. Continue conversion?"
        "(Change [EMC]VERSION to %s to suppress these messages) "
        % THIS_VERSION) != 'yes':
            exit(0)
    else:
        if input("The supplied INI file already has a [KINS] section."
        "this probably means that it was previously converted by hand. "
        "Continue y/N? (Change [EMC]VERSION to %s to suppress these messages) "
        % THIS_VERSION) != "y":
            exit(0)

# Looks like we are good to go, so first let's put the original configs
# somewhere safe.

basedir = os.path.dirname(os.path.abspath(filename))
backupdir = os.path.join(basedir, os.path.splitext(os.path.basename(filename))[0] + ".old")

while os.path.isdir(backupdir):
    backupdir += ".old"
os.mkdir(backupdir)
old_ini =  os.path.join(backupdir, os.path.basename(filename))
for f in os.listdir(basedir):
    if os.path.isdir(os.path.join(basedir, os.path.basename(f))):
        pass
    else:
        shutil.copy(os.path.join(basedir, os.path.basename(f)), 
                    os.path.join(backupdir, os.path.basename(f)))

#From now on, we use the backup copy as the reference
ini = linuxcnc.ini(old_ini)

#And the hal files too.
halfiles = ini.findall('HAL', 'HALFILE')
halfiles += ini.findall('HAL', 'POSTGUI_HALFILE')
halfiles += ['touchy.hal']
print("halfiles = ", halfiles)

halpaths = []
for halfile in halfiles:
    if os.path.isfile(os.path.join(basedir, halfile)):
        halpaths.append(os.path.join(basedir, halfile))
    elif (os.path.isfile(os.path.join(basedir, "hallib", halfile))):
        os.system('cp ' + os.path.join(basedir, "hallib", halfile) + ' ' +
                   os.path.join(basedir, halfile))
        halpaths.append(os.path.join(basedir, halfile))
    elif halfile == "touchy.hal":
        pass
    else:
        print("halfile %s not found\n" % halfile)

print("halpaths = ", halpaths)

if version == "1.0":
    #Just update the version in the INI
    inistring = open(filename,'r').read()
    newini = open(filename, 'w')
    inistring = re.sub("VERSION *= *(.*)", "VERSION = %s" % THIS_VERSION, inistring)
    newini.write(inistring)
    newini.close()

if version == "$Revision$" or version < "1.0":
    
    inistring = open(filename,'r').read()
    newini = open(filename, 'w')
    # Get a list of all sections
    all_sections = re.findall(r"^\s*\[(.+)\]", inistring, re.MULTILINE)

    # A C-style Switch would be nice here, to allow us to fall through successive
    # version updates.
    # At the moment there is only one update, but any future updates should be
    # a second "if version == 1.0" and so on. The first "if" needs to change the
    # version string, though.

    newini.write("# This config file was created %s by the update_ini script\n" % datetime.datetime.now())
    newini.write("# The original config files may be found in the %s directory\n\n" % backupdir)

    # reproduce everything before the first [section] verbatim
    section = re.match(r"(.*?)^\[", inistring, re.DOTALL | re.MULTILINE)
    if section !=None:
        newini.write(section.group(1))

    #[EMC] Section, change the version number
    all_sections.remove("EMC")
    section = re.search(r"\[EMC\](.+?)\n\[", inistring, re.DOTALL)
    if section: section = section.group(1)
    newini.write("[EMC]\n")
    if section != None:
        if version != "0.0":
            section = re.sub("VERSION (.+)", "VERSION = %s" % THIS_VERSION, section)
        else:
            newini.write("# The version string for this INI file.\n")
            newini.write("VERSION = %s\n" % THIS_VERSION)
        newini.write(section)
    else:
         newini.write("VERSION = %s\n" % THIS_VERSION)

    #These sections don't need any work.
    copysection("DISPLAY")
    copysection("FILTER")

    #[RS274NGC] Section, change FEATURES to separate entries
    all_sections.remove("RS274NGC")
    section = re.search(r"\[RS274NGC\](.+?)\n\[", inistring, re.DOTALL)
    if section: section = section.group(1)
    newini.write("\n[RS274NGC]\n")
    if section != None:
        features = ini.find('RS274NGC', 'FEATURES')
        if features != None:
            features = int(features)
            section = re.sub("FEATURES.*?\n", "", section)
            section += ("RETAIN_G43 = %s\n"   % ("1" if features & 0x1 else "0"))
            section += ("INI_VARS = %s\n"     % ("1" if features & 0x4 else "0"))
            section += ("HAL_PIN_VARS = %s\n" % ("1" if features & 0x8 else "0"))
            if features & 0x2: section  += ("OWORD_NARGS = 1\n" )
            if features & 0x10: section += ("NO_DOWNCASE_OWORD = 1\n" )
            if features & 0x20: section += ("OWORD_WARNONLY = 1\n" )
    section += "\n"
    newini.write(section)

    all_sections.remove("EMCMOT")
    section = re.search(r"\[EMCMOT\](.+?)\n\[", inistring, re.DOTALL)
    if section: section = section.group(1)
    newini.write("[EMCMOT]\n")
    section = re.sub("# Interval between tries to emcmot.*?\n", "", section)
    section = re.sub("COMM_WAIT.*?\n", "", section)
    newini.write(section)

    copysection("TASK")
    copysection("HAL")
    copysection("HALUI")

    # We need info from TRAJ to get KINS right

    joints = ini.find("TRAJ", "JOINTS")
    coordinates = ini.find("TRAJ", "COORDINATES").replace(" ","")
    if coordinates != None: joints = len(coordinates)
    if joints == None: joints = ini.find("TRAJ", "AXES")
    if joints == None: joints = "3"
    joints = int(joints)
    if coordinates == None: coordinates = "XYZABCUVW"

    coordinates = list(coordinates)

    # Search the Halfiles to find the kinematics.
    kins = None
    kinstype = None
    coords_entry = False
    if coordinates != "XYZABCUVW"[:joints]: coords_entry = True
    for halfile in halpaths:
        hal = open(os.path.join(os.path.dirname(filename), halfile), 'r')
        for line in hal.readlines():
            match = re.match(r'(?:#autoconverted|loadrt) +(\w+kins)', line)
            if match:
                kins = match.group(1)
                match = re.search(r'coordinates *= *(\w+)', line)
                if match:
                    coordinates = list(match.group(1))
                    coords_entry = 1
                match = re.search(r'kinstype *= *(\w+)', line)
                if kinstype: kinstype = match.group(1)
                break
        if kins: break
    if not kins: kins = "trivkins"

    #gantrykins and gentrivkins are gone, so need special treatment

    if  kins == "gantrykins":
        kins = "trivkins"
        kinstype = "BOTH"
        coords_entry = True
        for halfile in halpaths:
            hal = open(os.path.join(os.path.dirname(filename), halfile), 'r')
            for line in hal.readlines():
                match = re.match(r'setp +gantrykins.joint-(\d) +(\d)', line)
                if match:
                    j = int(match.group(1))
                    if j > joints: joints = j
                    a = int(match.group(2))
                    coordinates[j] = 'XYZABCUVW'[a]

    if kins == "gentrivkins":
        kins = "trivkins" #trivkins has the same defaults as gentrivkins

    # In JA [TRAJ] expects MAX_LINEAR_VELOCITY not MAX_VELOCITY
    all_sections.remove("TRAJ")
    section = re.search(r"\[TRAJ\](.+?)\n\[", inistring, re.DOTALL)
    if section: section = section.group(1)
    newini.write("\n[TRAJ]\n")
    if section != None:
        if not re.search("MAX_LINEAR_VELOCITY", section):
            if re.search("MAX_VELOCITY", section):
                section = re.sub("MAX_VELOCITY", "MAX_LINEAR_VELOCITY", section)
            else:
                mv = re.findall(r"MAX_VELOCITY[\s=]+(\d*(\.\d+)?)", inistring, re.MULTILINE)
                section = ("\n# this value based on fastest single axis" +
                          "\nMAX_LINEAR_VELOCITY = %s" % max(mv)[0] + section)
        if not re.search("DEFAULT_LINEAR_VELOCITY", section):
            section = re.sub("DEFAULT_VELOCITY", "DEFAULT_LINEAR_VELOCITY", section)
        if not re.search("MAX_LINEAR_ACCELERATION", section):
            section = re.sub("MAX_ACCELERATION", "MAX_LINEAR_ACCELERATION", section)
        if not re.search("DEFAULT_ACCELERATION", section):
            section = re.sub("DEFAULT_ACCELERATION", "DEFAULT_LINEAR_ACCELERATION", section)
        print("COORDINATES = %s\n" % ''.join(coordinates))
        section = re.sub("COORDINATES.*", "COORDINATES = %s" % ''.join(coordinates[: joints]), section)
        section = re.sub("CYCLE_TIME.*?\n", "", section)
        section = re.sub("AXES *=.*\n", "", section)
        newini.write(section)

    copysection("EMCIO")

    # Insert the new-fangled [KINS] section

    newini.write("\n\n[KINS]\n")
    newini.write("KINEMATICS = %s" % kins)
    if coords_entry: newini.write(" coordinates=%s" % ''.join(coordinates[: joints]))
    if kinstype: newini.write(" kinstype=%s" % kinstype)
    newini.write("\n")
    newini.write("#This is a best-guess at the number of joints, it should be checked\n")
    newini.write("JOINTS = %i\n" % joints)

    j = 0
    lock_mask = 0x0
    L2J={}
    while 1:
         # Search preferentially in "[JOINT_N] in case the file is part-converted already
        if re.search(r"^(\[JOINT_%i\])"%j, inistring, re.MULTILINE):
            if re.search(r"^(\[AXIS_%s\])" % "XYZABCUVW"[j], inistring, re.MULTILINE):
                copysection("AXIS_%s" % "XYZABCUVW"[j])
            #    copysection("JOINT_%i" % j)
            elif j < len(coordinates):
                newini.write("\n[AXIS_%s]\n" % coordinates[j])
                writeifexists(newini, "JOINT_%i" % j, "HOME")
                writeifexists(newini, "JOINT_%i" % j, "MIN_LIMIT")
                writeifexists(newini, "JOINT_%i" % j, "MAX_LIMIT")
                writeifexists(newini, "JOINT_%i" % j, "MAX_VELOCITY")
                writeifexists(newini, "JOINT_%i" % j, "MAX_ACCELERATION")
                copysection("[JOINT_%i]" % j)
        elif j < len(coordinates):
            # in this "elif" j is an index in to coordinates. 
            if coordinates[j] in L2J: # duplicate axis letter
                L2J[coordinates[j]].append(j) # = [L2J[coordinates[j]], j]
            else:
                L2J.update({coordinates[j] : [j]})
        elif j >= 9:
            break
        else:
            pass

        j += 1

    for L in list("XYZABCUVW"):
        if L in L2J:
            axisnum = "XYZABCUVW".index(L)
            newini.write("\n[AXIS_%s]\n" % L)
            writeifexists(newini, "AXIS_%i" % axisnum, "MIN_LIMIT")
            writeifexists(newini, "AXIS_%i" % axisnum, "MAX_LIMIT")
            writeifexists(newini, "AXIS_%i" % axisnum, "MAX_VELOCITY")
            writeifexists(newini, "AXIS_%i" % axisnum, "MAX_ACCELERATION")
            if ini.find("AXIS_%i" % j, "LOCKING_INDEXER"):
                lock_mask |= 1 << j
                newini.write("LOCKING_INDEXER_JOINT = %i\n" % j)
                
            hs = ini.find("AXIS_%i" % axisnum, "HOME_SEQUENCE")
            if hs == "-1" or hs == None: # -1 used to exclude a joint now we use no entry
                sequence = ""
            elif len(L2J[L]) > 1:  # tandem axis
                sequence = "HOME_SEQUENCE = -%s" % hs
            else:
                sequence = "HOME_SEQUENCE = %s" % hs
            for J in L2J[L]:
                # Take the coordinates index as the JOINT_Number
                newini.write("\n[JOINT_%i]" % J)
                section = re.search(r"\[AXIS_%i\](.+?)(\n\[|$)" % J, inistring, re.DOTALL)
                if not section:
                    section = re.search(r"\[AXIS_%i\](.+?)(\n\[|$)" % "XYZABCUVW".index(coordinates[J]), inistring, re.DOTALL)
                if section:
                    section = re.sub("HOME_SEQUENCE.*", sequence, section.group(1))
                    newini.write(section)
                    if not r'\[AXIS_%i\]' % axisnum in subs:
                        subs.update({r'\[AXIS_%i\]' % axisnum : '[JOINT_%i]' % J})
                        subs2.update({r'joint\.%i\.' % axisnum : 'joint.%i.' % J})
                    else:
                        subs.update({r'\[AXIS_%i\]' % J : '[JOINT_%i]' % J})
                else:
                    print("File parsing error, found an [AXIS_%i] section, but no content" % J)
                    exit()
    # We no longer need the [AXIS_N] data
    for j in range(0,8):
         if ("AXIS_%i" % j) in all_sections: all_sections.remove("AXIS_%i" % j)

    # If there were any custom sections, tag them on the end.
    while all_sections:
        copysection(all_sections[0])

    # and turn the locking mask into a string
    if lock_mask:
        lock_string = 'unlock_joints_mask=%i' % lock_mask
    else:
        lock_string = ""

    #That's the INI file done:
    newini.close()



    # Now change all the pin names etc in the linked HAL files.
    # Any machine can be jogged in world mode (in theory) but joint-mode jog-enable
    # is not auto-linked for safety.

if version == "$Revision$" or version < "1.0":
    
    subs.update({'axis.(.).active':'joint.\\1.active',
    'axis.(.).amp-enable-out':    'joint.\\1.amp-enable-out',
    'axis.(.).amp-fault-in':      'joint.\\1.amp-fault-in',
    'axis.(.).backlash-corr':     'joint.\\1.backlash-corr',
    'axis.(.).backlash-filt':     'joint.\\1.backlash-filt',
    'axis.(.).backlash-vel':      'joint.\\1.backlash-vel',
    'axis.(.).coarse-pos-cmd':    'joint.\\1.coarse-pos-cmd',
    'axis.(.).error':             'joint.\\1.error',
    'axis.(.).f-error':           'joint.\\1.f-error',
    'axis.(.).f-error-lim':       'joint.\\1.f-error-lim',
    'axis.(.).f-errored':         'joint.\\1.f-errored',
    'axis.(.).faulted':           'joint.\\1.faulted',
    'axis.(.).free-pos-cmd':      'joint.\\1.free-pos-cmd',
    'axis.(.).free-tp-enable':    'joint.\\1.free-tp-enable',
    'axis.(.).free-vel-lim':      'joint.\\1.free-vel-lim',
    'axis.(.).home-state':        'joint.\\1.home-state',
    'axis.(.).home-sw-in':        'joint.\\1.home-sw-in',
    'axis.(.).homed':             'joint.\\1.homed',
    'axis.(.).homing':            'joint.\\1.homing',
    'axis.(.).in-position':       'joint.\\1.in-position',
    'axis.(.).index-enable':      'joint.\\1.index-enable',
    'axis.(.).joint-pos-cmd':     'joint.\\1.pos-cmd',
    'axis.(.).joint-pos-fb':      'joint.\\1.pos-fb',
    'axis.(.).joint-vel-cmd':     'joint.\\1.vel-cmd',
    'axis.(.).kb-jog-active':     'joint.\\1.kb-jog-active',
    'axis.(.).motor-offset':      'joint.\\1.motor-offset',
    'axis.(.).motor-pos-cmd':     'joint.\\1.motor-pos-cmd',
    'axis.(.).motor-pos-fb':      'joint.\\1.motor-pos-fb',
    'axis.(.).neg-hard-limit':    'joint.\\1.neg-hard-limit',
    'axis.(.).neg-lim-sw-in':     'joint.\\1.neg-lim-sw-in',
    'axis.(.).pos-hard-limit':    'joint.\\1.pos-hard-limit',
    'axis.(.).pos-lim-sw-in':     'joint.\\1.pos-lim-sw-in',
    'axis.(.).wheel-jog-active':  'joint.\\1.wheel-jog-active',
    'axis.(.).unlock':            'joint.\\1.unlock',
    'axis.(.).is-unlocked':       'joint.\\1.is-unlocked',
    'axis.(.).jog-enable':        '@ax\\1@.jog-enable',
    'axis.(.).jog-counts':        'joint.\\1.jog-counts @ax\\1@.jog-counts',
    'axis.(.).jog-scale':         'joint.\\1.jog-scale @ax\\1@.jog-scale',
    'axis.(.).jog-vel-mode':      'joint.\\1.jog-vel-mode @ax\\1@.jog-vel-mode',
    'halui.axis.(.).pos-commanded':'halui.@ax\\1@.pos-commanded',
    'halui.axis.(.).pos-feedback':'halui.@ax\\1@.pos-feedback',
    'halui.axis.(.).pos-relative':'halui.@ax\\1@.pos-relative',
    'halui.joint.(.).is-selected':'halui.@ax\\1@.is-selected',
    'halui.jog.(.).analog':       'halui.@ax\\1@.analog',
    'halui.jog.(.).increment':    'halui.@ax\\1@.increment',
    'halui.jog.(.).increment-minus':'halui.@ax\\1@.increment-minus',
    'halui.jog.(.).increment-plus':'halui.@ax\\1@.increment-plus',
    'halui.jog.(.).minus':        'halui.@ax\\1@.minus',
    'halui.jog.(.).plus':         'halui.@ax\\1@.plus',
    'halui.joint.(.).select':     'halui.@ax\\1@.select',
    'halui.jog.selected.increment':'halui.axis.selected.increment',
    'halui.jog.selected.increment-minus':'halui.axis.selected.increment-minus',
    'halui.jog.selected.increment-plus':'halui.axis.selected.increment-plus',
    'halui.jog.selected.minus':   'halui.axis.selected.minus',
    'halui.jog.selected.plus':    'halui.axis.selected.plus',
    'halui.jog-deadband':         'halui.axis.jog-deadband',
    'halui.jog-speed':            'halui.axis.jog-speed',
    'halui.joint.selected':       'halui.joint.selected',
    'halui.joint.selected.is_homed':'halui.joint.selected.is-homed',
    'halui.joint.selected.on-soft-limit':'halui.joint.selected.on-soft-max-limit',
    'num_joints=[TRAJ]AXES':      'num_joints=[KINS]JOINTS',
    'loadrt(.+kins)':             'loadrt [KINS]KINEMATICS\n#autoconverted \\1',
    'shuttlexpress':              'shuttle',
    'shuttlepro':                 'shuttle',

    '(setp +gantrykins.+)':       '# \\1',
    '(.+servo_period_ns.+)':      '\\1 %s' % lock_string
    })

    # converts @axN@ pattern to axis.L and splits any malformed setp from mpg
    subs2.update({
    r'^\s*setp\s+(joint\S+)\s+(axis\S+)\s+(\S+)\s*$':
                                 'setp \\1 \\3\nsetp \\2 \\3\n',
    '@ax0@':                     'axis.x',
    '@ax1@':                     'axis.y',
    '@ax2@':                     'axis.z',
    '@ax3@':                     'axis.a',
    '@ax4@':                     'axis.b',
    '@ax5@':                     'axis.c',
    '@ax6@':                     'axis.u',
    '@ax7@':                     'axis.v',
    '@ax8@':                     'axis.w',
    })
    
if version < "1.1":
    subs.update({
        'motion.spindle-brake'                  : 'spindle.0.brake',
        'motion.spindle-forward'                : 'spindle.0.forward',
        'motion.spindle-index-enable'           : 'spindle.0.index-enable',
        'motion.spindle-inhibit'                : 'spindle.0.inhibit',
        'motion.spindle-on'                     : 'spindle.0.on',
        'motion.spindle-reverse'                : 'spindle.0.reverse',
        'motion.spindle-revs'                   : 'spindle.0.revs',
        'motion.spindle-speed-in'               : 'spindle.0.speed-in',
        'motion.spindle-speed-out'              : 'spindle.0.speed-out',
        'motion.spindle-speed-out-abs'          : 'spindle.0.speed-out-abs',
        'motion.spindle-speed-out-rps'          : 'spindle.0.speed-out-rps',
        'motion.spindle-speed-out-rps-abs'      : 'spindle.0.speed-out-rps-abs',
        'motion.spindle-orient-angle'           : 'spindle.0.orient-angle', 
        'motion.spindle-orient-mode'            : 'spindle.0.orient-mode',
        'motion.spindle-orient'                 : 'spindle.0.orient',
        'motion.spindle-is-oriented'            : 'spindle.0.is-oriented',
        'motion.spindle-orient-fault'           : 'spindle.0.orient-fault',
        'motion.spindle-locked'                 : 'spindle.0.locked',
        'motion.spindle-at-speed'               : 'spindle.0.at-speed',
        'halui.spindle.brake-is-on'             : 'halui.spindle.0.brake-is-on',
        'halui.spindle.brake-off'               : 'halui.spindle.0.brake-off',
        'halui.spindle.brake-on'                : 'halui.spindle.0.brake-on',
        'halui.spindle.decrease'                : 'halui.spindle.0.decrease',
        'halui.spindle.forward'                 : 'halui.spindle.0.forward',
        'halui.spindle.increase'                : 'halui.spindle.0.increase',
        'halui.spindle.is-on'                   : 'halui.spindle.0.is-on',
        'halui.spindle.reverse'                 : 'halui.spindle.0.reverse',
        'halui.spindle.runs-backward'           : 'halui.spindle.0.runs-backward',
        'halui.spindle.runs-forward'            : 'halui.spindle.0.runs-forward',
        'halui.spindle.start'                   : 'halui.spindle.0.start',
        'halui.spindle.stop'                    : 'halui.spindle.0.stop',
        'halui.spindle.override.count-enable'   : 'halui.spindle.0.override.count-enable',
        'halui.spindle.override.counts'         : 'halui.spindle.0.override.counts',
        'halui.spindle.override.decrease'       : 'halui.spindle.0.override.decrease',
        'halui.spindle.override.direct-value'   : 'halui.spindle.0.override.direct-value',
        'halui.spindle.override.increase'       : 'halui.spindle.0.override.increase',
        'halui.spindle.override.scale'          : 'halui.spindle.0.override.scale',
        'halui.spindle.override.value'          : 'halui.spindle.0.override.value'
        })

if subs == {}:
    print("""This file does not need converting, and furthermore execution
    should never have reached this spot""")
else:
    for halfile in halpaths:
        halstring = open(halfile,'r').read()
        for sub in subs:
            halstring = re.sub(sub, subs[sub], halstring, flags=re.M)
        for sub in subs2:
            halstring = re.sub(sub, subs2[sub], halstring, flags=re.M)
        newhal = open(halfile, 'w')
        newhal.write(halstring)
        newhal.close()

if force:
    shutil.rmtree(backupdir)
