blob: 7ebfb288e582ed5e88d0e785c41e023bc3a0bb17 [file] [log] [blame]
Tim Edwards9d3debb2020-10-20 20:52:18 -04001#!/usr/bin/env python3
Tim Edwards55f4d0e2020-07-05 15:41:02 -04002# Script to read a GDS file, modify the given string, and rewrite the GDS file.
3# The string may be a substring; the GDS file will be parsed completely for
4# library name, structure name, instance name, and other strings, and the
5# replacement made everywhere it occurs, finding the bounds of the entire
6# string around the search text, and adjusting the record bounds accordingly.
7
8import os
Tim Edwardscc82b602021-06-06 16:35:34 -04009import re
Tim Edwards55f4d0e2020-07-05 15:41:02 -040010import sys
11
12def usage():
Tim Edwardscc82b602021-06-06 16:35:34 -040013 print('change_gds_string.py <old_string> <new_string> [...] <path_to_gds_in> [<path_to_gds_out>]')
Tim Edwards55f4d0e2020-07-05 15:41:02 -040014
15if __name__ == '__main__':
Tim Edwardscc82b602021-06-06 16:35:34 -040016 debug = 0
17 verbatim = False
Tim Edwards55f4d0e2020-07-05 15:41:02 -040018
19 if len(sys.argv) == 1:
20 print("No options given to change_gds_string.py.")
21 usage()
22 sys.exit(0)
23
24 optionlist = []
25 arguments = []
26
27 for option in sys.argv[1:]:
28 if option.find('-', 0) == 0:
29 optionlist.append(option)
30 else:
31 arguments.append(option)
32
Tim Edwardscc82b602021-06-06 16:35:34 -040033 if len(arguments) < 3:
Tim Edwards55f4d0e2020-07-05 15:41:02 -040034 print("Wrong number of arguments given to change_gds_string.py.")
35 usage()
36 sys.exit(0)
37
Tim Edwardscc82b602021-06-06 16:35:34 -040038 for option in optionlist:
39 opval = option.split('=')
40 if opval[0] == '-debug':
41 if len(opval) == 2:
42 debug = int(opval[1])
43 else:
44 debug = 1
45 elif opval[0] == '-verbatim':
46 verbatim = True
Tim Edwards55f4d0e2020-07-05 15:41:02 -040047
Tim Edwardscc82b602021-06-06 16:35:34 -040048 # If next-to-last argument is a valid path, then the last argument should
49 # be the path to GDS out. Otherwise, overwrite the source file.
50
51 if os.path.isfile(arguments[-2]):
52 dest = arguments[-1]
53 source = arguments[-2]
54 oldstrings = arguments[0:-2:2]
55 newstrings = arguments[1:-2:2]
Tim Edwards55f4d0e2020-07-05 15:41:02 -040056 else:
Tim Edwardscc82b602021-06-06 16:35:34 -040057 dest = arguments[-1]
58 source = arguments[-1]
59 oldstrings = arguments[0:-1:2]
60 newstrings = arguments[1:-1:2]
61
62 if len(oldstrings) != len(newstrings):
63 print('Error: List of strings and replacements is not in pairs.')
64 sys.exit(1)
Tim Edwards55f4d0e2020-07-05 15:41:02 -040065
66 sourcedir = os.path.split(source)[0]
67 gdsinfile = os.path.split(source)[1]
68
69 destdir = os.path.split(dest)[0]
70 gdsoutfile = os.path.split(dest)[1]
71
72 with open(source, 'rb') as ifile:
73 gdsdata = ifile.read()
74
75 # To be done: Allow the user to select a specific record type or types
76 # in which to restrict the string substitution. If no restrictions are
77 # specified, then substitue in library name, structure name, and strings.
78
79 recordtypes = ['libname', 'strname', 'sname', 'string']
80 recordfilter = [2, 6, 18, 25]
Tim Edwardscc82b602021-06-06 16:35:34 -040081 bsearchlist = list(bytes(item, 'ascii') for item in oldstrings)
82 breplist = list(bytes(item, 'ascii') for item in newstrings)
83
84 if debug > 1:
85 print('Search list = ' + str(bsearchlist))
86 print('Replace list = ' + str(breplist))
Tim Edwards55f4d0e2020-07-05 15:41:02 -040087
88 datalen = len(gdsdata)
Tim Edwardscc82b602021-06-06 16:35:34 -040089 if debug > 0:
Tim Edwards55f4d0e2020-07-05 15:41:02 -040090 print('Original data length = ' + str(datalen))
91 dataptr = 0
92 while dataptr < datalen:
93 # Read stream records up to any string, then search for search text.
94 bheader = gdsdata[dataptr:dataptr + 2]
95 reclen = int.from_bytes(bheader, 'big')
96 newlen = reclen
97 if newlen == 0:
98 print('Error: found zero-length record at position ' + str(dataptr))
99 break
100
101 rectype = gdsdata[dataptr + 2]
102 datatype = gdsdata[dataptr + 3]
103
104 if rectype in recordfilter:
105 # Datatype 6 is STRING
106 if datatype == 6:
Tim Edwards55f4d0e2020-07-05 15:41:02 -0400107 bstring = gdsdata[dataptr + 4: dataptr + reclen]
Tim Edwardscc82b602021-06-06 16:35:34 -0400108 if debug > 1:
109 idx = recordfilter.index(rectype)
110 print(recordtypes[idx] + ' string = ' + str(bstring))
111
112 for bsearch,brep in zip(bsearchlist, breplist):
113 # Verbatim option: search string must match GDS string exactly
114 if verbatim:
115 blen = reclen - 4
Tim Edwards55f4d0e2020-07-05 15:41:02 -0400116 if bstring[-1] == 0:
Tim Edwardscc82b602021-06-06 16:35:34 -0400117 blen = blen - 1
118 if len(bsearch) != blen:
119 continue
120 repstring = re.sub(bsearch, brep, bstring)
121 if repstring != bstring:
122 before = gdsdata[0:dataptr]
123 after = gdsdata[dataptr + reclen:]
124 newlen = len(repstring) + 4
125 # Record sizes must be even
126 if newlen % 2 != 0:
127 # Was original string padded with null byte? If so,
128 # remove the null byte and reduce newlen. Otherwise,
129 # add a null byte and increase newlen.
130 if bstring[-1] == 0:
131 repstring = repstring[0:-1]
132 newlen -= 1
133 else:
134 repstring += b'\x00'
135 newlen += 1
Tim Edwards55f4d0e2020-07-05 15:41:02 -0400136
Tim Edwardscc82b602021-06-06 16:35:34 -0400137 bnewlen = newlen.to_bytes(2, byteorder='big')
138 brectype = rectype.to_bytes(1, byteorder='big')
139 bdatatype = datatype.to_bytes(1, byteorder='big')
Tim Edwards55f4d0e2020-07-05 15:41:02 -0400140
Tim Edwardscc82b602021-06-06 16:35:34 -0400141 # Assemble the new record
142 newrecord = bnewlen + brectype + bdatatype + repstring
143 # Reassemble the GDS data around the new record
144 gdsdata = before + newrecord[0:newlen] + after
145 # Adjust the data end location
146 datalen += (newlen - reclen)
Tim Edwards55f4d0e2020-07-05 15:41:02 -0400147
Tim Edwardscc82b602021-06-06 16:35:34 -0400148 if debug > 0:
149 print('Replaced ' + str(bstring) + ' with ' + str(repstring))
150 else:
151 if debug > 1:
152 idx = recordfilter.index(rectype)
153 print(recordtypes[idx] + ' record = ' + str(datatype) + ' is not a string')
Tim Edwards55f4d0e2020-07-05 15:41:02 -0400154
155 # Advance the pointer past the data
156 dataptr += newlen
157
158 with open(dest, 'wb') as ofile:
159 ofile.write(gdsdata)
160
161 exit(0)