1"""\ 2 3Tools for scanning header files in search of function prototypes. 4 5Often, the function prototypes in header files contain enough information 6to automatically generate (or reverse-engineer) interface specifications 7from them. The conventions used are very vendor specific, but once you've 8figured out what they are they are often a great help, and it sure beats 9manually entering the interface specifications. (These are needed to generate 10the glue used to access the functions from Python.) 11 12In order to make this class useful, almost every component can be overridden. 13The defaults are (currently) tuned to scanning Apple Macintosh header files, 14although most Mac specific details are contained in header-specific subclasses. 15""" 16 17import re 18import sys 19import os 20import fnmatch 21from types import * 22try: 23 import MacOS 24except ImportError: 25 MacOS = None 26 27try: 28 from bgenlocations import CREATOR, INCLUDEDIR 29except ImportError: 30 CREATOR = None 31 INCLUDEDIR = os.curdir 32 33Error = "scantools.Error" 34 35BEGINHTMLREPORT="""<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 36<html> 37<head> 38<style type="text/css"> 39.unmatched { } 40.commentstripping { color: grey; text-decoration: line-through } 41.comment { text-decoration: line-through } 42.notcomment { color: black } 43.incomplete { color: maroon } 44.constant { color: green } 45.pyconstant { background-color: yellow } 46.blconstant { background-color: yellow; color: red } 47.declaration { color: blue } 48.pydeclaration { background-color: yellow } 49.type { font-style: italic } 50.name { font-weight: bold } 51.value { font-style: italic } 52.arglist { text-decoration: underline } 53.blacklisted { background-color: yellow; color: red } 54</style> 55<title>Bgen scan report</title> 56</head> 57<body> 58<h1>Bgen scan report</h1> 59<h2>Legend</h2> 60<p>This scan report is intended to help you debug the regular expressions 61used by the bgen scanner. It consists of the original ".h" header file(s) 62marked up to show you what the regular expressions in the bgen parser matched 63for each line. NOTE: comments in the original source files may or may not be 64shown.</p> 65<p>The typographic conventions of this file are as follows:</p> 66<dl> 67<dt>comment stripping</dt> 68<dd><pre><span class="commentstripping"><span class="notcomment">comment stripping is </span><span class="comment">/* marked up */</span><span class="notcomment"> and the line is repeated if needed</span></span></pre> 69<p>If anything here does not appear to happen correctly look at 70<tt>comment1_pat</tt> and <tt>comment2_pat</tt>.</p> 71</dd> 72<dt>constant definitions</dt> 73<dd><pre><span class="constant">#define <span class="name">name</span> <span class="value">value</span></pre> 74<p>Highlights name and value of the constant. Governed by <tt>sym_pat</tt>.</p> 75</dd> 76<dt>function declaration</dt> 77<dd><pre><span class="declaration"><span class="type">char *</span><span class="name">rindex</span><span class="arglist">(<span class="type">const char *</span><span class="name">s</span>, <span class="type">int </span><span class="name">c</span>)</span>;</span></pre> 78<p>Highlights type, name and argument list. <tt>type_pat</tt>, 79<tt>name_pat</tt> and <tt>args_pat</tt> are combined into <tt>whole_pat</tt>, which 80is what is used here.</p></dd> 81</dd> 82<dt>incomplete match for function declaration</dt> 83<dd><pre><span class="incomplete"><span class="type">char *</span>foo;</span></pre> 84<p>The beginning of this looked promising, but it did not match a function declaration. 85In other words, it matched <tt>head_pat</tt> but not <tt>whole_pat</tt>. If the next 86declaration has also been gobbled up you need to look at <tt>end_pat</tt>.</p> 87</dd> 88<dt>unrecognized input</dt> 89<dd><pre><span class="unmatched">#include "type.h"</span></pre> 90<p>If there are function declarations the scanner has missed (i.e. things 91are in this class but you want them to be declarations) you need to adapt 92<tt>head_pat</tt>. 93</dd> 94</dl> 95<h2>Output</h2> 96<pre> 97<span class="unmatched"> 98""" 99ENDHTMLREPORT="""</span> 100</pre> 101</body> 102</html> 103""" 104 105class Scanner: 106 107 # Set to 1 in subclass to debug your scanner patterns. 108 debug = 0 109 110 def __init__(self, input = None, output = None, defsoutput = None): 111 self.initsilent() 112 self.initblacklists() 113 self.initrepairinstructions() 114 self.initpaths() 115 self.initfiles() 116 self.initpatterns() 117 self.compilepatterns() 118 self.initosspecifics() 119 self.initusedtypes() 120 if output: 121 self.setoutput(output, defsoutput) 122 if input: 123 self.setinput(input) 124 125 def initusedtypes(self): 126 self.usedtypes = {} 127 128 def typeused(self, type, mode): 129 if not self.usedtypes.has_key(type): 130 self.usedtypes[type] = {} 131 self.usedtypes[type][mode] = None 132 133 def reportusedtypes(self): 134 types = self.usedtypes.keys() 135 types.sort() 136 for type in types: 137 modes = self.usedtypes[type].keys() 138 modes.sort() 139 self.report("%s %s", type, " ".join(modes)) 140 141 def gentypetest(self, file): 142 fp = open(file, "w") 143 fp.write("types=[\n") 144 types = self.usedtypes.keys() 145 types.sort() 146 for type in types: 147 fp.write("\t'%s',\n"%type) 148 fp.write("]\n") 149 fp.write("""missing=0 150for t in types: 151 try: 152 tt = eval(t) 153 except NameError: 154 print "** Missing type:", t 155 missing = 1 156if missing: raise "Missing Types" 157""") 158 fp.close() 159 160 def initsilent(self): 161 self.silent = 1 162 163 def error(self, format, *args): 164 if self.silent >= 0: 165 print format%args 166 167 def report(self, format, *args): 168 if not self.silent: 169 print format%args 170 171 def writeinitialdefs(self): 172 pass 173 174 def initblacklists(self): 175 self.blacklistnames = self.makeblacklistnames() 176 self.blacklisttypes = ["unknown", "-"] + self.makeblacklisttypes() 177 self.greydictnames = self.greylist2dict(self.makegreylist()) 178 179 def greylist2dict(self, list): 180 rv = {} 181 for define, namelist in list: 182 for name in namelist: 183 rv[name] = define 184 return rv 185 186 def makeblacklistnames(self): 187 return [] 188 189 def makeblacklisttypes(self): 190 return [] 191 192 def makegreylist(self): 193 return [] 194 195 def initrepairinstructions(self): 196 self.repairinstructions = self.makerepairinstructions() 197 self.inherentpointertypes = self.makeinherentpointertypes() 198 199 def makerepairinstructions(self): 200 """Parse the repair file into repair instructions. 201 202 The file format is simple: 203 1) use \ to split a long logical line in multiple physical lines 204 2) everything after the first # on a line is ignored (as comment) 205 3) empty lines are ignored 206 4) remaining lines must have exactly 3 colon-separated fields: 207 functionpattern : argumentspattern : argumentsreplacement 208 5) all patterns use shell style pattern matching 209 6) an empty functionpattern means the same as * 210 7) the other two fields are each comma-separated lists of triples 211 8) a triple is a space-separated list of 1-3 words 212 9) a triple with less than 3 words is padded at the end with "*" words 213 10) when used as a pattern, a triple matches the type, name, and mode 214 of an argument, respectively 215 11) when used as a replacement, the words of a triple specify 216 replacements for the corresponding words of the argument, 217 with "*" as a word by itself meaning leave the original word 218 (no other uses of "*" is allowed) 219 12) the replacement need not have the same number of triples 220 as the pattern 221 """ 222 f = self.openrepairfile() 223 if not f: return [] 224 print "Reading repair file", repr(f.name), "..." 225 list = [] 226 lineno = 0 227 while 1: 228 line = f.readline() 229 if not line: break 230 lineno = lineno + 1 231 startlineno = lineno 232 while line[-2:] == '\\\n': 233 line = line[:-2] + ' ' + f.readline() 234 lineno = lineno + 1 235 i = line.find('#') 236 if i >= 0: line = line[:i] 237 words = [s.strip() for s in line.split(':')] 238 if words == ['']: continue 239 if len(words) <> 3: 240 print "Line", startlineno, 241 print ": bad line (not 3 colon-separated fields)" 242 print repr(line) 243 continue 244 [fpat, pat, rep] = words 245 if not fpat: fpat = "*" 246 if not pat: 247 print "Line", startlineno, 248 print "Empty pattern" 249 print repr(line) 250 continue 251 patparts = [s.strip() for s in pat.split(',')] 252 repparts = [s.strip() for s in rep.split(',')] 253 patterns = [] 254 for p in patparts: 255 if not p: 256 print "Line", startlineno, 257 print "Empty pattern part" 258 print repr(line) 259 continue 260 pattern = p.split() 261 if len(pattern) > 3: 262 print "Line", startlineno, 263 print "Pattern part has > 3 words" 264 print repr(line) 265 pattern = pattern[:3] 266 else: 267 while len(pattern) < 3: 268 pattern.append("*") 269 patterns.append(pattern) 270 replacements = [] 271 for p in repparts: 272 if not p: 273 print "Line", startlineno, 274 print "Empty replacement part" 275 print repr(line) 276 continue 277 replacement = p.split() 278 if len(replacement) > 3: 279 print "Line", startlineno, 280 print "Pattern part has > 3 words" 281 print repr(line) 282 replacement = replacement[:3] 283 else: 284 while len(replacement) < 3: 285 replacement.append("*") 286 replacements.append(replacement) 287 list.append((fpat, patterns, replacements)) 288 return list 289 290 def makeinherentpointertypes(self): 291 return [] 292 293 def openrepairfile(self, filename = "REPAIR"): 294 try: 295 return open(filename, "rU") 296 except IOError, msg: 297 print repr(filename), ":", msg 298 print "Cannot open repair file -- assume no repair needed" 299 return None 300 301 def initfiles(self): 302 self.specmine = 0 303 self.defsmine = 0 304 self.scanmine = 0 305 self.htmlmine = 0 306 self.specfile = sys.stdout 307 self.defsfile = None 308 self.scanfile = sys.stdin 309 self.htmlfile = None 310 self.lineno = 0 311 self.line = "" 312 313 def initpaths(self): 314 self.includepath = [os.curdir, INCLUDEDIR] 315 316 def initpatterns(self): 317 self.head_pat = r"^EXTERN_API[^_]" 318 self.tail_pat = r"[;={}]" 319 self.type_pat = r"EXTERN_API" + \ 320 r"[ \t\n]*\([ \t\n]*" + \ 321 r"(?P<type>[a-zA-Z0-9_* \t]*[a-zA-Z0-9_*])" + \ 322 r"[ \t\n]*\)[ \t\n]*" 323 self.name_pat = r"(?P<name>[a-zA-Z0-9_]+)[ \t\n]*" 324 self.args_pat = r"\((?P<args>([^\(;=\)]+|\([^\(;=\)]*\))*)\)" 325 self.whole_pat = self.type_pat + self.name_pat + self.args_pat 326 self.sym_pat = r"^[ \t]*(?P<name>[a-zA-Z0-9_]+)[ \t]*=" + \ 327 r"[ \t]*(?P<defn>[-0-9_a-zA-Z'\"\(][^\t\n,;}]*),?" 328 self.asplit_pat = r"^(?P<type>.*[^a-zA-Z0-9_])(?P<name>[a-zA-Z0-9_]+)(?P<array>\[\])?$" 329 self.comment1_pat = r"(?P<rest>.*)//.*" 330 # note that the next pattern only removes comments that are wholly within one line 331 self.comment2_pat = r"(?P<rest1>.*)/\*.*\*/(?P<rest2>.*)" 332 333 def compilepatterns(self): 334 for name in dir(self): 335 if name[-4:] == "_pat": 336 pat = getattr(self, name) 337 prog = re.compile(pat) 338 setattr(self, name[:-4], prog) 339 340 def initosspecifics(self): 341 if MacOS and CREATOR: 342 self.filetype = 'TEXT' 343 self.filecreator = CREATOR 344 else: 345 self.filetype = self.filecreator = None 346 347 def setfiletype(self, filename): 348 if MacOS and (self.filecreator or self.filetype): 349 creator, type = MacOS.GetCreatorAndType(filename) 350 if self.filecreator: creator = self.filecreator 351 if self.filetype: type = self.filetype 352 MacOS.SetCreatorAndType(filename, creator, type) 353 354 def close(self): 355 self.closefiles() 356 357 def closefiles(self): 358 self.closespec() 359 self.closedefs() 360 self.closescan() 361 self.closehtml() 362 363 def closespec(self): 364 tmp = self.specmine and self.specfile 365 self.specfile = None 366 if tmp: tmp.close() 367 368 def closedefs(self): 369 tmp = self.defsmine and self.defsfile 370 self.defsfile = None 371 if tmp: tmp.close() 372 373 def closescan(self): 374 tmp = self.scanmine and self.scanfile 375 self.scanfile = None 376 if tmp: tmp.close() 377 378 def closehtml(self): 379 if self.htmlfile: self.htmlfile.write(ENDHTMLREPORT) 380 tmp = self.htmlmine and self.htmlfile 381 self.htmlfile = None 382 if tmp: tmp.close() 383 384 def setoutput(self, spec, defs = None): 385 self.closespec() 386 self.closedefs() 387 if spec: 388 if type(spec) == StringType: 389 file = self.openoutput(spec) 390 mine = 1 391 else: 392 file = spec 393 mine = 0 394 self.specfile = file 395 self.specmine = mine 396 if defs: 397 if type(defs) == StringType: 398 file = self.openoutput(defs) 399 mine = 1 400 else: 401 file = defs 402 mine = 0 403 self.defsfile = file 404 self.defsmine = mine 405 406 def sethtmloutput(self, htmlfile): 407 self.closehtml() 408 if htmlfile: 409 if type(htmlfile) == StringType: 410 file = self.openoutput(htmlfile) 411 mine = 1 412 else: 413 file = htmlfile 414 mine = 0 415 self.htmlfile = file 416 self.htmlmine = mine 417 self.htmlfile.write(BEGINHTMLREPORT) 418 419 def openoutput(self, filename): 420 try: 421 file = open(filename, 'w') 422 except IOError, arg: 423 raise IOError, (filename, arg) 424 self.setfiletype(filename) 425 return file 426 427 def setinput(self, scan = sys.stdin): 428 if not type(scan) in (TupleType, ListType): 429 scan = [scan] 430 self.allscaninputs = scan 431 self._nextinput() 432 433 def _nextinput(self): 434 if not self.allscaninputs: 435 return 0 436 scan = self.allscaninputs[0] 437 self.allscaninputs = self.allscaninputs[1:] 438 self.closescan() 439 if scan: 440 if type(scan) == StringType: 441 file = self.openinput(scan) 442 mine = 1 443 else: 444 file = scan 445 mine = 0 446 self.scanfile = file 447 self.scanmine = mine 448 self.lineno = 0 449 return 1 450 451 def openinput(self, filename): 452 if not os.path.isabs(filename): 453 for dir in self.includepath: 454 fullname = os.path.join(dir, filename) 455 #self.report("trying full name %r", fullname) 456 try: 457 return open(fullname, 'rU') 458 except IOError: 459 pass 460 # If not on the path, or absolute, try default open() 461 try: 462 return open(filename, 'rU') 463 except IOError, arg: 464 raise IOError, (arg, filename) 465 466 def getline(self): 467 if not self.scanfile: 468 raise Error, "input file not set" 469 self.line = self.scanfile.readline() 470 if not self.line: 471 if self._nextinput(): 472 return self.getline() 473 raise EOFError 474 self.lineno = self.lineno + 1 475 return self.line 476 477 def scan(self): 478 if not self.scanfile: 479 self.error("No input file has been specified") 480 return 481 inputname = self.scanfile.name 482 self.report("scanfile = %r", inputname) 483 if not self.specfile: 484 self.report("(No interface specifications will be written)") 485 else: 486 self.report("specfile = %r", self.specfile.name) 487 self.specfile.write("# Generated from %r\n\n" % (inputname,)) 488 if not self.defsfile: 489 self.report("(No symbol definitions will be written)") 490 else: 491 self.report("defsfile = %r", (self.defsfile.name,)) 492 self.defsfile.write("# Generated from %r\n\n" % (os.path.split(inputname)[1],)) 493 self.writeinitialdefs() 494 self.alreadydone = [] 495 try: 496 while 1: 497 try: line = self.getline() 498 except EOFError: break 499 if self.debug: 500 self.report("LINE: %r" % (line,)) 501 match = self.comment1.match(line) 502 if match: 503 self.htmlreport(line, klass='commentstripping', ranges=[( 504 match.start('rest'), match.end('rest'), 'notcomment')]) 505 line = match.group('rest') 506 if self.debug: 507 self.report("\tafter comment1: %r" % (line,)) 508 match = self.comment2.match(line) 509 while match: 510 if match: 511 self.htmlreport(line, klass='commentstripping', ranges=[ 512 (match.start('rest1'), match.end('rest1'), 'notcomment'), 513 (match.start('rest2'), match.end('rest2'), 'notcomment')]) 514 line = match.group('rest1')+match.group('rest2') 515 if self.debug: 516 self.report("\tafter comment2: %r" % (line,)) 517 match = self.comment2.match(line) 518 if self.defsfile: 519 match = self.sym.match(line) 520 if match: 521 if self.debug: 522 self.report("\tmatches sym.") 523 self.dosymdef(match, line) 524 continue 525 match = self.head.match(line) 526 if match: 527 if self.debug: 528 self.report("\tmatches head.") 529 self.dofuncspec() 530 continue 531 self.htmlreport(line, klass='unmatched') 532 except EOFError: 533 self.error("Uncaught EOF error") 534 self.reportusedtypes() 535 536 def dosymdef(self, match, line): 537 name, defn = match.group('name', 'defn') 538 self.htmlreport(line, klass='constant', ranges=[ 539 (match.start('name'), match.end('name'), 'name'), 540 (match.start('defn'), match.end('defn'), 'value')]) 541 defn = escape8bit(defn) 542 if self.debug: 543 self.report("\tsym: name=%r, defn=%r" % (name, defn)) 544 if not name in self.blacklistnames: 545 oline = "%s = %s\n" % (name, defn) 546 self.defsfile.write(oline) 547 self.htmlreport(oline, klass="pyconstant") 548 else: 549 self.defsfile.write("# %s = %s\n" % (name, defn)) 550 self.htmlreport("** no output: name is blacklisted", klass="blconstant") 551 # XXXX No way to handle greylisted names 552 553 def dofuncspec(self): 554 raw = self.line 555 while not self.tail.search(raw): 556 line = self.getline() 557 if self.debug: 558 self.report("* CONTINUATION LINE: %r" % (line,)) 559 match = self.comment1.match(line) 560 if match: 561 line = match.group('rest') 562 if self.debug: 563 self.report("\tafter comment1: %r" % (line,)) 564 match = self.comment2.match(line) 565 while match: 566 line = match.group('rest1')+match.group('rest2') 567 if self.debug: 568 self.report("\tafter comment1: %r" % (line,)) 569 match = self.comment2.match(line) 570 raw = raw + line 571 if self.debug: 572 self.report("* WHOLE LINE: %r" % (raw,)) 573 self.processrawspec(raw) 574 return raw 575 576 def processrawspec(self, raw): 577 match = self.whole.search(raw) 578 if not match: 579 self.report("Bad raw spec: %r", raw) 580 if self.debug: 581 match = self.type.search(raw) 582 if not match: 583 self.report("(Type already doesn't match)") 584 self.htmlreport(raw, klass='incomplete', ranges=[( 585 match.start('type'), match.end('type'), 'type')]) 586 else: 587 self.report("(but type matched)") 588 self.htmlreport(raw, klass='incomplete') 589 return 590 type, name, args = match.group('type', 'name', 'args') 591 ranges=[ 592 (match.start('type'), match.end('type'), 'type'), 593 (match.start('name'), match.end('name'), 'name'), 594 (match.start('args'), match.end('args'), 'arglist')] 595 self.htmlreport(raw, klass='declaration', ranges=ranges) 596 modifiers = self.getmodifiers(match) 597 type = self.pythonizename(type) 598 name = self.pythonizename(name) 599 if self.checkduplicate(name): 600 self.htmlreport("*** no output generated: duplicate name", klass="blacklisted") 601 return 602 self.report("==> %s %s <==", type, name) 603 if self.blacklisted(type, name): 604 self.htmlreport("*** no output generated: function name or return type blacklisted", klass="blacklisted") 605 self.report("*** %s %s blacklisted", type, name) 606 return 607 returnlist = [(type, name, 'ReturnMode')] 608 returnlist = self.repairarglist(name, returnlist) 609 [(type, name, returnmode)] = returnlist 610 arglist = self.extractarglist(args) 611 arglist = self.repairarglist(name, arglist) 612 if self.unmanageable(type, name, arglist): 613 self.htmlreport("*** no output generated: some argument blacklisted", klass="blacklisted") 614 ##for arg in arglist: 615 ## self.report(" %r", arg) 616 self.report("*** %s %s unmanageable", type, name) 617 return 618 if modifiers: 619 self.generate(type, name, arglist, modifiers) 620 else: 621 self.generate(type, name, arglist) 622 623 def getmodifiers(self, match): 624 return [] 625 626 def checkduplicate(self, name): 627 if name in self.alreadydone: 628 self.report("Name has already been defined: %r", name) 629 return True 630 self.alreadydone.append(name) 631 return False 632 633 def pythonizename(self, name): 634 name = re.sub("\*", " ptr", name) 635 name = name.strip() 636 name = re.sub("[ \t]+", "_", name) 637 return name 638 639 def extractarglist(self, args): 640 args = args.strip() 641 if not args or args == "void": 642 return [] 643 parts = [s.strip() for s in args.split(",")] 644 arglist = [] 645 for part in parts: 646 arg = self.extractarg(part) 647 arglist.append(arg) 648 return arglist 649 650 def extractarg(self, part): 651 mode = "InMode" 652 part = part.strip() 653 match = self.asplit.match(part) 654 if not match: 655 self.error("Indecipherable argument: %r", part) 656 return ("unknown", part, mode) 657 type, name, array = match.group('type', 'name', 'array') 658 if array: 659 # array matches an optional [] after the argument name 660 type = type + " ptr " 661 type = self.pythonizename(type) 662 return self.modifyarg(type, name, mode) 663 664 def modifyarg(self, type, name, mode): 665 if type[:6] == "const_": 666 type = type[6:] 667 elif type[-4:] == "_ptr": 668 type = type[:-4] 669 mode = "OutMode" 670 elif type in self.inherentpointertypes: 671 mode = "OutMode" 672 if type[-4:] == "_far": 673 type = type[:-4] 674 return type, name, mode 675 676 def repairarglist(self, functionname, arglist): 677 arglist = arglist[:] 678 i = 0 679 while i < len(arglist): 680 for item in self.repairinstructions: 681 if len(item) == 2: 682 pattern, replacement = item 683 functionpat = "*" 684 else: 685 functionpat, pattern, replacement = item 686 if not fnmatch.fnmatchcase(functionname, functionpat): 687 continue 688 n = len(pattern) 689 if i+n > len(arglist): continue 690 current = arglist[i:i+n] 691 for j in range(n): 692 if not self.matcharg(pattern[j], current[j]): 693 break 694 else: # All items of the pattern match 695 new = self.substituteargs( 696 pattern, replacement, current) 697 if new is not None: 698 arglist[i:i+n] = new 699 i = i+len(new) # No recursive substitutions 700 break 701 else: # No patterns match 702 i = i+1 703 return arglist 704 705 def matcharg(self, patarg, arg): 706 return len(filter(None, map(fnmatch.fnmatchcase, arg, patarg))) == 3 707 708 def substituteargs(self, pattern, replacement, old): 709 new = [] 710 for k in range(len(replacement)): 711 item = replacement[k] 712 newitem = [item[0], item[1], item[2]] 713 for i in range(3): 714 if item[i] == '*': 715 newitem[i] = old[k][i] 716 elif item[i][:1] == '$': 717 index = int(item[i][1:]) - 1 718 newitem[i] = old[index][i] 719 new.append(tuple(newitem)) 720 ##self.report("old: %r", old) 721 ##self.report("new: %r", new) 722 return new 723 724 def generate(self, tp, name, arglist, modifiers=[]): 725 726 self.typeused(tp, 'return') 727 if modifiers: 728 classname, listname = self.destination(tp, name, arglist, modifiers) 729 else: 730 classname, listname = self.destination(tp, name, arglist) 731 if not classname or not listname: 732 self.htmlreport("*** no output generated: self.destination() returned None", klass="blacklisted") 733 return 734 if not self.specfile: 735 self.htmlreport("*** no output generated: no output file specified", klass="blacklisted") 736 return 737 self.specfile.write("f = %s(%s, %r,\n" % (classname, tp, name)) 738 for atype, aname, amode in arglist: 739 self.typeused(atype, amode) 740 self.specfile.write(" (%s, %r, %s),\n" % 741 (atype, aname, amode)) 742 if self.greydictnames.has_key(name): 743 self.specfile.write(" condition=%r,\n"%(self.greydictnames[name],)) 744 self.generatemodifiers(classname, name, modifiers) 745 self.specfile.write(")\n") 746 self.specfile.write("%s.append(f)\n\n" % listname) 747 if self.htmlfile: 748 oline = "Adding to %s:\n%s(returntype=%s, name=%r" % (listname, classname, tp, name) 749 for atype, aname, amode in arglist: 750 oline += ",\n (%s, %r, %s)" % (atype, aname, amode) 751 oline += ")\n" 752 self.htmlreport(oline, klass="pydeclaration") 753 754 def destination(self, type, name, arglist): 755 return "FunctionGenerator", "functions" 756 757 def generatemodifiers(self, classname, name, modifiers): 758 pass 759 760 def blacklisted(self, type, name): 761 if type in self.blacklisttypes: 762 ##self.report("return type %s is blacklisted", type) 763 return 1 764 if name in self.blacklistnames: 765 ##self.report("function name %s is blacklisted", name) 766 return 1 767 return 0 768 769 def unmanageable(self, type, name, arglist): 770 for atype, aname, amode in arglist: 771 if atype in self.blacklisttypes: 772 self.report("argument type %s is blacklisted", atype) 773 return 1 774 return 0 775 776 def htmlreport(self, line, klass=None, ranges=None): 777 if not self.htmlfile: return 778 if ranges is None: 779 ranges = [] 780 if klass: 781 ranges.insert(0, (0, len(line), klass)) 782 oline = '' 783 i = 0 784 for c in line: 785 for b, e, name in ranges: 786 if b == i: 787 oline += '<span class="%s">' % name 788 if e == i: 789 oline += '</span>' 790 i += 1 791 792 if c == '<': oline += '<' 793 elif c == '>': oline += '>' 794 else: oline += c 795 for b, e, name in ranges: 796 if b >= i: 797 oline += '<span class="%s">' % name 798 if e >= i: 799 oline += '</span>' 800 if not line or line[-1] != '\n': 801 oline += '\n' 802 self.htmlfile.write(oline) 803 804class Scanner_PreUH3(Scanner): 805 """Scanner for Universal Headers before release 3""" 806 def initpatterns(self): 807 Scanner.initpatterns(self) 808 self.head_pat = "^extern pascal[ \t]+" # XXX Mac specific! 809 self.type_pat = "pascal[ \t\n]+(?P<type>[a-zA-Z0-9_ \t]*[a-zA-Z0-9_])[ \t\n]+" 810 self.whole_pat = self.type_pat + self.name_pat + self.args_pat 811 self.sym_pat = "^[ \t]*(?P<name>[a-zA-Z0-9_]+)[ \t]*=" + \ 812 "[ \t]*(?P<defn>[-0-9'\"][^\t\n,;}]*),?" 813 814class Scanner_OSX(Scanner): 815 """Scanner for modern (post UH3.3) Universal Headers """ 816 def initpatterns(self): 817 Scanner.initpatterns(self) 818 self.head_pat = "^EXTERN_API(_C)?" 819 self.type_pat = "EXTERN_API(_C)?" + \ 820 "[ \t\n]*\([ \t\n]*" + \ 821 "(?P<type>[a-zA-Z0-9_* \t]*[a-zA-Z0-9_*])" + \ 822 "[ \t\n]*\)[ \t\n]*" 823 self.whole_pat = self.type_pat + self.name_pat + self.args_pat 824 self.sym_pat = "^[ \t]*(?P<name>[a-zA-Z0-9_]+)[ \t]*=" + \ 825 "[ \t]*(?P<defn>[-0-9_a-zA-Z'\"\(][^\t\n,;}]*),?" 826 827_8bit = re.compile(r"[\200-\377]") 828 829def escape8bit(s): 830 if _8bit.search(s) is not None: 831 out = [] 832 for c in s: 833 o = ord(c) 834 if o >= 128: 835 out.append("\\" + hex(o)[1:]) 836 else: 837 out.append(c) 838 s = "".join(out) 839 return s 840 841def test(): 842 input = "D:Development:THINK C:Mac #includes:Apple #includes:AppleEvents.h" 843 output = "@aespecs.py" 844 defsoutput = "@aedefs.py" 845 s = Scanner(input, output, defsoutput) 846 s.scan() 847 848if __name__ == '__main__': 849 test() 850