Added "split_one_spice.py", a companion script to "spectre_to_spice.py"
that splits up a SPICE file (output from "spectre_to_spice.py") that
has multiple subcircuits in it into multiple files, one file per
subcircuit.  This is much like "split_spice.py" except that is does not
try to break models out of subcircuits, and it is much more careful
about placing global parameters with the subcircuits that use them, and
finding global parameters and other parts of the file that are common
to more than one of the subcircuits, and retaining them in the original
file.
diff --git a/VERSION b/VERSION
index 8955a01..adb7b04 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.0.26
+1.0.27
diff --git a/common/split_one_spice.py b/common/split_one_spice.py
new file mode 100755
index 0000000..c454c8d
--- /dev/null
+++ b/common/split_one_spice.py
@@ -0,0 +1,425 @@
+#!/bin/env python3
+#
+# split_one_spice.py --
+#
+# Script that reads a SPICE file that contains multiple models and
+# subcircuits, and splits it into one file per subcircuit, with each
+# file containing any related in-lined models.
+#
+# The arguments are <path_to_input> and <path_to_output>.
+# <path_to_input> should be the path to a single file, while
+# <path_to_output> is the path to a directory where the split files will
+# be put.
+
+import os
+import sys
+import re
+import glob
+
+def usage():
+    print('split_one_spice.py <path_to_input> <path_to_output>')
+
+def convert_file(in_file, out_path):
+
+    # Regexp patterns
+    paramrex = re.compile('\.param[ \t]+(.*)')
+    subrex = re.compile('\.subckt[ \t]+([^ \t]+)[ \t]+([^ \t]*)')
+    modelrex = re.compile('\.model[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+(.*)')
+    endsubrex = re.compile('\.ends[ \t]+(.+)')
+    increx = re.compile('\.include[ \t]+')
+
+    with open(in_file, 'r') as ifile:
+        inplines = ifile.read().splitlines()
+
+    insubckt = False
+    inparam = False
+    inmodel = False
+    inpinlist = False
+    subname = ''
+    modname = ''
+    modtype = ''
+
+    # Keep track of what the subcircuit names are
+    subnames = []
+    filenos = {}
+
+    # Keep track of what parameters are used by what subcircuits
+    paramlist = {}
+
+    # Enumerate which lines go to which files
+    linedest = [-1]*len(inplines)
+    fileno = -1;
+    lineno = -1;
+
+    for line in inplines:
+        lineno += 1
+
+        # Item 1.  Handle comment lines
+        if line.startswith('*'):
+            linedest[lineno] = fileno
+            continue
+
+        # Item 2.  Flag continuation lines
+        if line.startswith('+'):
+            contline = True
+        else:
+            contline = False
+            if line.strip() != '':
+                if inparam:
+                    inparam = False
+                if inpinlist:
+                    inpinlist = False
+
+        # Item 3.  Handle blank lines like comment lines
+        if line.strip() == '':
+            linedest[lineno] = fileno
+            continue
+
+        # Item 4.  Handle continuation lines
+        if contline:
+            if inparam:
+                # Continue handling parameters
+                linedest[lineno] = fileno
+                if not insubckt:
+                    # Find (global) parameters and record what line they were found on
+                    ptok = list(item for item in line[1:].strip().split() if item != '=')
+                    for param, value in zip(*[iter(ptok)]*2):
+                        paramlist[param] = lineno
+                else:
+                    # Find if a global parameter was used.  Assign it to this
+                    # subcircuit.  If it has already been used, assign it to
+                    # be a common parameter
+                    for param in paramlist:
+                        if param in line[1:]:
+                            checkfile = linedest[paramlist[param]]
+                            if checkfile == -1:
+                                linedest[paramlist[param]] = fileno
+                            elif checkfile != fileno:
+                                linedest[paramlist[param]] = -3
+                continue
+
+        # Item 5.  Regexp matching
+
+        # parameters
+        pmatch = paramrex.match(line)
+        if pmatch:
+            inparam = True
+            linedest[lineno] = fileno
+            if not insubckt:
+                # Find (global) parameters and record what line they were found on
+                ptok = list(item for item in pmatch.group(1).split() if item != '=')
+                for param, value in zip(*[iter(ptok)]*2):
+                    paramlist[param] = lineno
+            else:
+                # Find if a global parameter was used.  Assign it to this
+                # subcircuit.  If it has already been used, assign it to
+                # be a common parameter
+                for param in paramlist:
+                    if param in pmatch.group(1):
+                        checkfile = linedest[paramlist[param]]
+                        if checkfile == -1:
+                            linedest[paramlist[param]] = fileno
+                        if checkfile != fileno:
+                            linedest[paramlist[param]] = -3
+            continue
+
+        # model
+        mmatch = modelrex.match(line)
+        if mmatch:
+            modname = mmatch.group(1)
+            modtype = mmatch.group(2)
+
+            linedest[lineno] = fileno
+            inmodel = 2
+            continue
+
+        if not insubckt:
+            # Things to parse if not in a subcircuit
+
+            imatch = subrex.match(line)
+            if imatch:
+                insubckt = True
+                subname = imatch.group(1)
+                fileno = len(subnames)
+                subnames.append(subname)
+                filenos[subname] = fileno
+
+                if fileno > 0:
+                    # If this is not the first subcircuit, then add all blank
+                    # and comment lines above it to the same file
+
+                    lastno = -1
+                    tline = lineno - 1
+                    while tline >= 0:
+                        tinp = inplines[tline]
+                        # Backup through all comment and blank lines
+                        if not tinp.startswith('*') and not tinp.strip() == '':
+                            lastno = linedest[tline]
+                            tline += 1;
+                            break;
+                        tline -= 1;
+
+                    while tline < lineno:
+                        # Forward through all blank lines, and assign them to
+                        # the previous subcell.
+                        tinp = inplines[tline]
+                        if tinp.strip() != '':
+                            break;
+                        if linedest[tline] == -1:
+                            linedest[tline] = lastno
+                        tline += 1;
+
+                    while tline < lineno:
+                        linedest[tline] = fileno
+                        tline += 1;
+                else:
+                    # If this is the first subcircuit encountered, then assign
+                    # to it the nearest block of comment lines before it.  If
+                    # those comment lines include a parameter or statistics
+                    # block, then abandon the effort.
+
+                    # Backup through blank lines immediately above
+                    abandon = False
+                    tline = lineno - 1
+                    while tline >= 0:
+                        tinp = inplines[tline]
+                        if not tinp.strip() == '':
+                            break;
+                        tline -= 1;
+
+                    while tline > 0:
+                        # Backup through the next comment block above
+                        tinp = inplines[tline]
+                        if not tinp.startswith('*'):
+                            tline += 1;
+                            break;
+                        elif tinp.strip('*').strip().startswith('statistics'):
+                            abandon = True
+                        tline -= 1;
+
+                    if tline == 0:
+                        abandon = True
+
+                    if not abandon:
+                        while tline < lineno:
+                            linedest[tline] = fileno
+                            tline += 1;
+ 
+                devrex = re.compile(subname + '[ \t]*([^ \t]+)[ \t]*([^ \t]+)[ \t]*(.*)', re.IGNORECASE)
+                inpinlist = True
+                linedest[lineno] = fileno
+                continue
+
+        else:
+            # Things to parse when inside of a ".subckt" block
+
+            if inpinlist:
+                # Watch for pin list continuation line.
+                linedest[lineno] = fileno
+                continue
+                
+            else:
+                ematch = endsubrex.match(line)
+                if ematch:
+                    if ematch.group(1) != subname:
+                        print('Error:  "ends" name does not match "subckt" name!')
+                        print('"ends" name = ' + ematch.group(1))
+                        print('"subckt" name = ' + subname)
+
+                    linedest[lineno] = fileno
+                    fileno = -1
+
+                    insubckt = False
+                    inmodel = False
+                    subname = ''
+                    continue
+                else:
+                    linedest[lineno] = fileno
+                    continue
+
+    # Sort subcircuit names
+    subnames.sort(reverse=True)
+
+    # Look for any lines containing parameters in paramlist.  If those lines
+    # are unassigned (-1), then assign them to the same cell that the parameter
+    # was assigned to.  NOTE:  Assumes that there will never be two parameters
+    # on the same line that were from two different subcircuits that is not
+    # already marked as a common parameter.
+
+    lineno = -1
+    for line in inplines:
+        lineno += 1
+        if linedest[lineno] == -1:
+            for param in paramlist:
+                if param in line:
+                    linedest[lineno] = linedest[paramlist[param]]
+                    break
+
+    # Ad hoc method:  Look for any lines containing each cell name, and assign
+    # that line to the cell.  That isolates parameters that belong to only one
+    # cell.  Ignore comment lines from line 1 down to the first non-comment line.
+    # Since all parameters and comment blocks have been handled, this is not
+    # likely to change anything.
+
+    lineno = -1
+    for line in inplines:
+        lineno = -1
+        if not line.startswith('*'):
+            break
+
+    topcomm = True
+    for line in inplines:
+        lineno += 1
+        if topcomm and not line.startswith('*'):
+            topcomm = False
+
+        if not topcomm:
+            if linedest[lineno] == -1:
+                for subname in subnames:
+                    subno = filenos[subname]
+                    if subname in line:
+                        linedest[lineno] = subno
+                        break
+
+    # All lines marked -1 except for comment lines should be remarked -3
+    # (go into the common file only)
+
+    lineno = -1
+    for line in inplines:
+        lineno += 1
+        if linedest[lineno] == -1:
+            if not line.startswith('*'):
+                linedest[lineno] = -3
+
+    # All comment lines that are surrounded by lines marked -3 should
+    # also be marked -3.  This keeps comments that are completely inside
+    # blocks that are only in the common file out of the individual files.
+
+    lineno = 0
+    for line in inplines[1:]:
+        lineno += 1
+        if linedest[lineno] == -1 and linedest[lineno - 1] == -3:
+            testline = lineno + 1
+            while linedest[testline] == -1:
+                testline += 1
+            if linedest[testline] == -3:
+                testline = lineno
+                while linedest[testline] == -1:
+                    linedest[testline] = -3
+                    testline += 1
+
+    froot = os.path.split(in_file)[1]
+    for subname in subnames:
+        subno = filenos[subname]
+        fext = os.path.splitext(in_file)[1]
+
+        # Guard against one of the split files having the same name as
+        # the original, since we need to keep the original file.
+        if subname == os.path.splitext(froot)[0]:
+            fext = '_split' + fext
+
+        # Output the result to out_file.
+        with open(out_path + '/' + subname + fext, 'w') as ofile:
+            firstline = True
+            lineno = -1
+            for line in inplines:
+                lineno += 1
+                if linedest[lineno] == subno or linedest[lineno] == -1:
+                    if firstline:
+                        print('* File ' + subname + fext + ' split from ' + froot + ' by split_one_spice.py', file=ofile)
+                        firstline = False
+                    print(line, file=ofile)
+
+    # Debug:  Print one diagnostic file (do this before messing with the
+    # linedest[] entries in the next step).  This debug file shows which
+    # lines of the file are split into which file, and which lines are
+    # common.
+
+    ffile = os.path.split(in_file)[1]
+    froot = os.path.splitext(ffile)[0]
+    fext = os.path.splitext(ffile)[1]
+
+    with open(out_path + '/' + froot + '_debug' + fext, 'w') as ofile:
+        for subname in subnames:
+            subno = filenos[subname]
+            print(str(subno) + '\t' + subname, file=ofile)
+
+        print('\n', file=ofile)
+
+        lineno = -1
+        for line in inplines:
+            lineno += 1
+            print(str(linedest[lineno]) + '\t' + line, file=ofile)
+            
+    # Reset all linedest[] entries except the bottommost entry for each subcircuit.
+    lineno = len(inplines)
+    subrefs = [0] * len(subnames)
+    while lineno > 0:
+        lineno -= 1
+        if linedest[lineno] >= 0:
+            if subrefs[linedest[lineno]] == 0:
+                subrefs[linedest[lineno]] = 1
+            else:
+                linedest[lineno] = -2
+
+    # Print the original file, including each of the new files.
+    # Also print out all lines marked "-1" or "-3"
+
+    with open(out_path + '/' + froot +  fext, 'w') as ofile:
+        lineno = -1
+        subno = -1
+        for line in inplines:
+            lineno += 1
+            if linedest[lineno] == -1 or linedest[lineno] == -3 :
+                print(line, file=ofile)
+            elif linedest[lineno] >= 0:
+                for subname in subnames:
+                    if filenos[subname] == linedest[lineno]:
+                        fext = os.path.splitext(in_file)[1]
+                        if subname == os.path.splitext(froot)[0]:
+                            fext = '_split' + fext
+                        break
+                print('.include ' + subname + fext, file=ofile)
+                subno = linedest[lineno]
+
+if __name__ == '__main__':
+    debug = False
+
+    if len(sys.argv) == 1:
+        print("No options given to split_one_spice.py.")
+        usage()
+        sys.exit(0)
+
+    optionlist = []
+    arguments = []
+
+    for option in sys.argv[1:]:
+        if option.find('-', 0) == 0:
+            optionlist.append(option)
+        else:
+            arguments.append(option)
+
+    if len(arguments) != 2:
+        print("Wrong number of arguments given to split_one_spice.py.")
+        usage()
+        sys.exit(0)
+
+    if '-debug' in optionlist:
+        debug = True
+
+    inpath = arguments[0]
+    outpath = arguments[1]
+    do_one_file = False
+
+    if not os.path.exists(inpath):
+        print('No such source file ' + inpath)
+        sys.exit(1)
+
+    if not os.path.isfile(inpath):
+        print('Input path ' + inpath + ' is not a file.')
+        sys.exit(1)
+ 
+    convert_file(inpath, outpath)
+
+    print('Done.')
+    exit(0)