Initial commit of public repository open_pdks.
diff --git a/common/change_gds_string.py b/common/change_gds_string.py
new file mode 100755
index 0000000..b8a0321
--- /dev/null
+++ b/common/change_gds_string.py
@@ -0,0 +1,127 @@
+#!/bin/env python3
+# Script to read a GDS file, modify the given string, and rewrite the GDS file.
+# The string may be a substring; the GDS file will be parsed completely for
+# library name, structure name, instance name, and other strings, and the
+# replacement made everywhere it occurs, finding the bounds of the entire
+# string around the search text, and adjusting the record bounds accordingly.
+
+import os
+import sys
+
+def usage():
+ print('change_gds_string.py <old_string> <new_string> <path_to_gds_in> [<path_to_gds_out>]')
+
+if __name__ == '__main__':
+ debug = False
+
+ if len(sys.argv) == 1:
+ print("No options given to change_gds_string.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) < 3 or len(arguments) > 4:
+ print("Wrong number of arguments given to change_gds_string.py.")
+ usage()
+ sys.exit(0)
+
+ if '-debug' in optionlist:
+ debug = True
+
+ oldstring = arguments[0]
+ newstring = arguments[1]
+ source = arguments[2]
+
+ # If only three arguments are provided, then overwrite the source file.
+ if len(arguments) == 4:
+ dest = arguments[3]
+ else:
+ dest = arguments[2]
+
+ sourcedir = os.path.split(source)[0]
+ gdsinfile = os.path.split(source)[1]
+
+ destdir = os.path.split(dest)[0]
+ gdsoutfile = os.path.split(dest)[1]
+
+ with open(source, 'rb') as ifile:
+ gdsdata = ifile.read()
+
+ # To be done: Allow the user to select a specific record type or types
+ # in which to restrict the string substitution. If no restrictions are
+ # specified, then substitue in library name, structure name, and strings.
+
+ recordtypes = ['libname', 'strname', 'sname', 'string']
+ recordfilter = [2, 6, 18, 25]
+ bsearch = bytes(oldstring, 'ascii')
+ brep = bytes(newstring, 'ascii')
+
+ datalen = len(gdsdata)
+ if debug:
+ print('Original data length = ' + str(datalen))
+ dataptr = 0
+ while dataptr < datalen:
+ # Read stream records up to any string, then search for search text.
+ bheader = gdsdata[dataptr:dataptr + 2]
+ reclen = int.from_bytes(bheader, 'big')
+ newlen = reclen
+ if newlen == 0:
+ print('Error: found zero-length record at position ' + str(dataptr))
+ break
+
+ rectype = gdsdata[dataptr + 2]
+ datatype = gdsdata[dataptr + 3]
+
+ if rectype in recordfilter:
+ # Datatype 6 is STRING
+ if datatype == 6:
+ if debug:
+ print('Record type = ' + str(rectype) + ' data type = ' + str(datatype) + ' length = ' + str(reclen))
+
+ bstring = gdsdata[dataptr + 4: dataptr + reclen]
+ repstring = bstring.replace(bsearch, brep)
+ if repstring != bstring:
+ before = gdsdata[0:dataptr]
+ after = gdsdata[dataptr + reclen:]
+ newlen = len(repstring) + 4
+ # Record sizes must be even
+ if newlen % 2 != 0:
+ # Was original string padded with null byte? If so,
+ # remove the null byte and reduce newlen. Otherwise,
+ # add a null byte and increase newlen.
+ if bstring[-1] == 0:
+ repstring = repstring[0:-1]
+ newlen -= 1
+ else:
+ repstring += b'\x00'
+ newlen += 1
+
+ bnewlen = newlen.to_bytes(2, byteorder='big')
+ brectype = rectype.to_bytes(1, byteorder='big')
+ bdatatype = datatype.to_bytes(1, byteorder='big')
+
+ # Assemble the new record
+ newrecord = bnewlen + brectype + bdatatype + repstring
+ # Reassemble the GDS data around the new record
+ gdsdata = before + newrecord[0:newlen] + after
+ # Adjust the data end location
+ datalen += (newlen - reclen)
+
+ if debug:
+ print('Replaced ' + str(bstring) + ' with ' + str(repstring))
+
+ # Advance the pointer past the data
+ dataptr += newlen
+
+ with open(dest, 'wb') as ofile:
+ ofile.write(gdsdata)
+
+ exit(0)