Daniel Dunbar | 33b5118 | 2008-08-29 01:07:08 +0000 | [diff] [blame^] | 1 | #!/usr/bin/python |
| 2 | |
| 3 | import os |
| 4 | import re |
| 5 | import time |
| 6 | from pprint import pprint |
| 7 | |
| 8 | ### |
| 9 | |
| 10 | c99URL = 'http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf' |
| 11 | c99TOC = [('Foreword', 'xi'), |
| 12 | ('Introduction', 'xiv'), |
| 13 | ('1. Scope', '1'), |
| 14 | ('2. Normative references', '2'), |
| 15 | ('3. Terms, definitions, and symbols', '3'), |
| 16 | ('4. Conformance', '7'), |
| 17 | ('5. Environment', '9'), |
| 18 | ('5.1 Conceptual models', '9'), |
| 19 | ('5.1.1 Translation environment', '9'), |
| 20 | ('5.1.2 Execution environments', '11'), |
| 21 | ('5.2 Environmental considerations', '17'), |
| 22 | ('5.2.1 Character sets', '17'), |
| 23 | ('5.2.2 Character display semantics', '19'), |
| 24 | ('5.2.3 Signals and interrupts', '20'), |
| 25 | ('5.2.4 Environmental limits', '20'), |
| 26 | ('6. Language', '29'), |
| 27 | ('6.1 Notation', '29'), |
| 28 | ('6.2 Concepts', '29'), |
| 29 | ('6.2.1 Scopes of identifiers', '29'), |
| 30 | ('6.2.2 Linkages of identifiers', '30'), |
| 31 | ('6.2.3 Name spaces of identifiers', '31'), |
| 32 | ('6.2.4 Storage durations of objects', '32'), |
| 33 | ('6.2.5 Types', '33'), |
| 34 | ('6.2.6 Representations of types', '37'), |
| 35 | ('6.2.7 Compatible type and composite type', '40'), |
| 36 | ('6.3 Conversions', '42'), |
| 37 | ('6.3.1 Arithmetic operands', '42'), |
| 38 | ('6.3.2 Other operands', '46'), |
| 39 | ('6.4 Lexical elements', '49'), |
| 40 | ('6.4.1 Keywords', '50'), |
| 41 | ('6.4.2 Identifiers', '51'), |
| 42 | ('6.4.3 Universal character names', '53'), |
| 43 | ('6.4.4 Constants', '54'), |
| 44 | ('6.4.5 String literals', '62'), |
| 45 | ('6.4.6 Punctuators', '63'), |
| 46 | ('6.4.7 Header names', '64'), |
| 47 | ('6.4.8 Preprocessing numbers', '65'), |
| 48 | ('6.4.9 Comments', '66'), |
| 49 | ('6.5 Expressions', '67'), |
| 50 | ('6.5.1 Primary expressions', '69'), |
| 51 | ('6.5.2 Postfix operators', '69'), |
| 52 | ('6.5.3 Unary operators', '78'), |
| 53 | ('6.5.4 Cast operators', '81'), |
| 54 | ('6.5.5 Multiplicative operators', '82'), |
| 55 | ('6.5.6 Additive operators', '82'), |
| 56 | ('6.5.7 Bitwise shift operators', '84'), |
| 57 | ('6.5.8 Relational operators', '85'), |
| 58 | ('6.5.9 Equality operators', '86'), |
| 59 | ('6.5.10 Bitwise AND operator', '87'), |
| 60 | ('6.5.11 Bitwise exclusive OR operator', '88'), |
| 61 | ('6.5.12 Bitwise inclusive OR operator', '88'), |
| 62 | ('6.5.13 Logical AND operator', '89'), |
| 63 | ('6.5.14 Logical OR operator', '89'), |
| 64 | ('6.5.15 Conditional operator', '90'), |
| 65 | ('6.5.16 Assignment operators', '91'), |
| 66 | ('6.5.17 Comma operator', '94'), |
| 67 | ('6.6 Constant expressions', '95'), |
| 68 | ('6.7 Declarations', '97'), |
| 69 | ('6.7.1 Storage-class specifiers', '98'), |
| 70 | ('6.7.2 Type specifiers', '99'), |
| 71 | ('6.7.3 Type qualifiers', '108'), |
| 72 | ('6.7.4 Function specifiers', '112'), |
| 73 | ('6.7.5 Declarators', '114'), |
| 74 | ('6.7.6 Type names', '122'), |
| 75 | ('6.7.7 Type definitions', '123'), |
| 76 | ('6.7.8 Initialization', '125'), |
| 77 | ('6.8 Statements and blocks', '131'), |
| 78 | ('6.8.1 Labeled statements', '131'), |
| 79 | ('6.8.2 Compound statement', '132'), |
| 80 | ('6.8.3 Expression and null statements', '132'), |
| 81 | ('6.8.4 Selection statements', '133'), |
| 82 | ('6.8.5 Iteration statements', '135'), |
| 83 | ('6.8.6 Jump statements', '136'), |
| 84 | ('6.9 External definitions', '140'), |
| 85 | ('6.9.1 Function definitions', '141'), |
| 86 | ('6.9.2 External object definitions', '143'), |
| 87 | ('6.10 Preprocessing directives', '145'), |
| 88 | ('6.10.1 Conditional inclusion', '147'), |
| 89 | ('6.10.2 Source file inclusion', '149'), |
| 90 | ('6.10.3 Macro replacement', '151'), |
| 91 | ('6.10.4 Line control', '158'), |
| 92 | ('6.10.5 Error directive', '159'), |
| 93 | ('6.10.6 Pragma directive', '159'), |
| 94 | ('6.10.7 Null directive', '160'), |
| 95 | ('6.10.8 Predefined macro names', '160'), |
| 96 | ('6.10.9 Pragma operator', '161'), |
| 97 | ('6.11 Future language directions', '163'), |
| 98 | ('6.11.1 Floating types', '163'), |
| 99 | ('6.11.2 Linkages of identifiers', '163'), |
| 100 | ('6.11.3 External names', '163'), |
| 101 | ('6.11.4 Character escape sequences', '163'), |
| 102 | ('6.11.5 Storage-class specifiers', '163'), |
| 103 | ('6.11.6 Function declarators', '163'), |
| 104 | ('6.11.7 Function definitions', '163'), |
| 105 | ('6.11.8 Pragma directives', '163'), |
| 106 | ('6.11.9 Predefined macro names', '163'), |
| 107 | ('7. Library', '164'), |
| 108 | ('7.1 Introduction', '164'), |
| 109 | ('7.1.1 Definitions of terms', '164'), |
| 110 | ('7.1.2 Standard headers', '165'), |
| 111 | ('7.1.3 Reserved identifiers', '166'), |
| 112 | ('7.1.4 Use of library functions', '166'), |
| 113 | ('7.2 Diagnostics <assert.h>', '169'), |
| 114 | ('7.2.1 Program diagnostics', '169'), |
| 115 | ('7.3 Complex arithmetic <complex.h>', '170'), |
| 116 | ('7.3.1 Introduction', '170'), |
| 117 | ('7.3.2 Conventions', '170'), |
| 118 | ('7.3.3 Branch cuts', '171'), |
| 119 | ('7.3.4 The CX_LIMITED_RANGE pragma', '171'), |
| 120 | ('7.3.5 Trigonometric functions', '172'), |
| 121 | ('7.3.6 Hyperbolic functions', '174'), |
| 122 | ('7.3.7 Exponential and logarithmic functions', '176'), |
| 123 | ('7.3.8 Power and absolute-value functions', '177'), |
| 124 | ('7.3.9 Manipulation functions', '178'), |
| 125 | ('7.4 Character handling <ctype.h>', '181'), |
| 126 | ('7.4.1 Character classification functions', '181'), |
| 127 | ('7.4.2 Character case mapping functions', '184'), |
| 128 | ('7.5 Errors <errno.h>', '186'), |
| 129 | ('7.6 Floating-point environment <fenv.h>', '187'), |
| 130 | ('7.6.1 The FENV_ACCESS pragma', '189'), |
| 131 | ('7.6.2 Floating-point exceptions', '190'), |
| 132 | ('7.6.3 Rounding', '193'), |
| 133 | ('7.6.4 Environment', '194'), |
| 134 | ('7.7 Characteristics of floating types <float.h>', '197'), |
| 135 | ('7.8 Format conversion of integer types <inttypes.h>', '198'), |
| 136 | ('7.8.1 Macros for format specifiers', '198'), |
| 137 | ('7.8.2 Functions for greatest-width integer types', '199'), |
| 138 | ('7.9 Alternative spellings <iso646.h>', '202'), |
| 139 | ('7.10 Sizes of integer types <limits.h>', '203'), |
| 140 | ('7.11 Localization <locale.h>', '204'), |
| 141 | ('7.11.1 Locale control', '205'), |
| 142 | ('7.11.2 Numeric formatting convention inquiry', '206'), |
| 143 | ('7.12 Mathematics <math.h>', '212'), |
| 144 | ('7.12.1 Treatment of error conditions', '214'), |
| 145 | ('7.12.2 The FP_CONTRACT pragma', '215'), |
| 146 | ('7.12.3 Classification macros', '216'), |
| 147 | ('7.12.4 Trigonometric functions', '218'), |
| 148 | ('7.12.5 Hyperbolic functions', '221'), |
| 149 | ('7.12.6 Exponential and logarithmic functions', '223'), |
| 150 | ('7.12.7 Power and absolute-value functions', '228'), |
| 151 | ('7.12.8 Error and gamma functions', '230'), |
| 152 | ('7.12.9 Nearest integer functions', '231'), |
| 153 | ('7.12.10 Remainder functions', '235'), |
| 154 | ('7.12.11 Manipulation functions', '236'), |
| 155 | ('7.12.12 Maximum, minimum, and positive difference functions', '238'), |
| 156 | ('7.12.13 Floating multiply-add', '239'), |
| 157 | ('7.12.14 Comparison macros', '240'), |
| 158 | ('7.13 Nonlocal jumps <setjmp.h>', '243'), |
| 159 | ('7.13.1 Save calling environment', '243'), |
| 160 | ('7.13.2 Restore calling environment', '244'), |
| 161 | ('7.14 Signal handling <signal.h>', '246'), |
| 162 | ('7.14.1 Specify signal handling', '247'), |
| 163 | ('7.14.2 Send signal', '248'), |
| 164 | ('7.15 Variable arguments <stdarg.h>', '249'), |
| 165 | ('7.15.1 Variable argument list access macros', '249'), |
| 166 | ('7.16 Boolean type and values <stdbool.h>', '253'), |
| 167 | ('7.17 Common definitions <stddef.h>', '254'), |
| 168 | ('7.18 Integer types <stdint.h>', '255'), |
| 169 | ('7.18.1 Integer types', '255'), |
| 170 | ('7.18.2 Limits of specified-width integer types', '257'), |
| 171 | ('7.18.3 Limits of other integer types', '259'), |
| 172 | ('7.18.4 Macros for integer constants', '260'), |
| 173 | ('7.19 Input/output <stdio.h>', '262'), |
| 174 | ('7.19.1 Introduction', '262'), |
| 175 | ('7.19.2 Streams', '264'), |
| 176 | ('7.19.3 Files', '266'), |
| 177 | ('7.19.4 Operations on files', '268'), |
| 178 | ('7.19.5 File access functions', '270'), |
| 179 | ('7.19.6 Formatted input/output functions', '274'), |
| 180 | ('7.19.7 Character input/output functions', '296'), |
| 181 | ('7.19.8 Direct input/output functions', '301'), |
| 182 | ('7.19.9 File positioning functions', '302'), |
| 183 | ('7.19.10 Error-handling functions', '304'), |
| 184 | ('7.20 General utilities <stdlib.h>', '306'), |
| 185 | ('7.20.1 Numeric conversion functions', '307'), |
| 186 | ('7.20.2 Pseudo-random sequence generation functions', '312'), |
| 187 | ('7.20.3 Memory management functions', '313'), |
| 188 | ('7.20.4 Communication with the environment', '315'), |
| 189 | ('7.20.5 Searching and sorting utilities', '318'), |
| 190 | ('7.20.6 Integer arithmetic functions', '320'), |
| 191 | ('7.20.7 Multibyte/wide character conversion functions', '321'), |
| 192 | ('7.20.8 Multibyte/wide string conversion functions', '323'), |
| 193 | ('7.21 String handling <string.h>', '325'), |
| 194 | ('7.21.1 String function conventions', '325'), |
| 195 | ('7.21.2 Copying functions', '325'), |
| 196 | ('7.21.3 Concatenation functions', '327'), |
| 197 | ('7.21.4 Comparison functions', '328'), |
| 198 | ('7.21.5 Search functions', '330'), |
| 199 | ('7.21.6 Miscellaneous functions', '333'), |
| 200 | ('7.22 Type-generic math <tgmath.h>', '335'), |
| 201 | ('7.23 Date and time <time.h>', '338'), |
| 202 | ('7.23.1 Components of time', '338'), |
| 203 | ('7.23.2 Time manipulation functions', '339'), |
| 204 | ('7.23.3 Time conversion functions', '341'), |
| 205 | ('7.24 Extended multibyte and wide character utilities <wchar.h>', '348'), |
| 206 | ('7.24.1 Introduction', '348'), |
| 207 | ('7.24.2 Formatted wide character input/output functions', '349'), |
| 208 | ('7.24.3 Wide character input/output functions', '367'), |
| 209 | ('7.24.4 General wide string utilities', '371'), |
| 210 | ('7.24.5 Wide character time conversion functions', '385'), |
| 211 | ('7.24.6 Extended multibyte/wide character conversion utilities', '386'), |
| 212 | ('7.25 Wide character classification and mapping utilities <wctype.h>', |
| 213 | '393'), |
| 214 | ('7.25.1 Introduction', '393'), |
| 215 | ('7.25.2 Wide character classification utilities', '394'), |
| 216 | ('7.25.3 Wide character case mapping utilities', '399'), |
| 217 | ('7.26 Future library directions', '401'), |
| 218 | ('7.26.1 Complex arithmetic <complex.h>', '401'), |
| 219 | ('7.26.2 Character handling <ctype.h>', '401'), |
| 220 | ('7.26.3 Errors <errno.h>', '401'), |
| 221 | ('7.26.4 Format conversion of integer types <inttypes.h>', '401'), |
| 222 | ('7.26.5 Localization <locale.h>', '401'), |
| 223 | ('7.26.6 Signal handling <signal.h>', '401'), |
| 224 | ('7.26.7 Boolean type and values <stdbool.h>', '401'), |
| 225 | ('7.26.8 Integer types <stdint.h>', '401'), |
| 226 | ('7.26.9 Input/output <stdio.h>', '402'), |
| 227 | ('7.26.10 General utilities <stdlib.h>', '402'), |
| 228 | ('7.26.11 String handling <string.h>', '402'), |
| 229 | ('<wchar.h>', '402'), |
| 230 | ('<wctype.h>', '402'), |
| 231 | ('Annex A (informative) Language syntax summary', '403'), |
| 232 | ('A.1 Lexical grammar', '403'), |
| 233 | ('A.2 Phrase structure grammar', '409'), |
| 234 | ('A.3 Preprocessing directives', '416'), |
| 235 | ('Annex B (informative) Library summary', '418'), |
| 236 | ('B.1 Diagnostics <assert.h>', '418'), |
| 237 | ('B.2 Complex <complex.h>', '418'), |
| 238 | ('B.3 Character handling <ctype.h>', '420'), |
| 239 | ('B.4 Errors <errno.h>', '420'), |
| 240 | ('B.5 Floating-point environment <fenv.h>', '420'), |
| 241 | ('B.6 Characteristics of floating types <float.h>', '421'), |
| 242 | ('B.7 Format conversion of integer types <inttypes.h>', '421'), |
| 243 | ('B.8 Alternative spellings <iso646.h>', '422'), |
| 244 | ('B.9 Sizes of integer types <limits.h>', '422'), |
| 245 | ('B.10 Localization <locale.h>', '422'), |
| 246 | ('B.11 Mathematics <math.h>', '422'), |
| 247 | ('B.12 Nonlocal jumps <setjmp.h>', '427'), |
| 248 | ('B.13 Signal handling <signal.h>', '427'), |
| 249 | ('B.14 Variable arguments <stdarg.h>', '427'), |
| 250 | ('B.15 Boolean type and values <stdbool.h>', '427'), |
| 251 | ('B.16 Common definitions <stddef.h>', '428'), |
| 252 | ('B.17 Integer types <stdint.h>', '428'), |
| 253 | ('B.18 Input/output <stdio.h>', '428'), |
| 254 | ('B.19 General utilities <stdlib.h>', '430'), |
| 255 | ('B.20 String handling <string.h>', '432'), |
| 256 | ('B.21 Type-generic math <tgmath.h>', '433'), |
| 257 | ('B.22 Date and time <time.h>', '433'), |
| 258 | ('B.23 Extended multibyte/wide character utilities <wchar.h>', '434'), |
| 259 | ('B.24 Wide character classification and mapping utilities <wctype.h>', |
| 260 | '436'), |
| 261 | ('Annex C (informative) Sequence points', '438'), |
| 262 | ('Annex D (normative) Universal character names for identifiers', '439'), |
| 263 | ('Annex E (informative) Implementation limits', '441'), |
| 264 | ('Annex F (normative) IEC 60559 floating-point arithmetic', '443'), |
| 265 | ('F.1 Introduction', '443'), |
| 266 | ('F.2 Types', '443'), |
| 267 | ('F.3 Operators and functions', '444'), |
| 268 | ('F.4 Floating to integer conversion', '446'), |
| 269 | ('F.5 Binary-decimal conversion', '446'), |
| 270 | ('F.6 Contracted expressions', '447'), |
| 271 | ('F.7 Floating-point environment', '447'), |
| 272 | ('F.8 Optimization', '450'), |
| 273 | ('F.9 Mathematics <math.h>', '453'), |
| 274 | ('Annex G (informative) IEC 60559-compatible complex arithmetic', '466'), |
| 275 | ('G.1 Introduction', '466'), |
| 276 | ('G.2 Types', '466'), |
| 277 | ('G.3 Conventions', '466'), |
| 278 | ('G.4 Conversions', '467'), |
| 279 | ('G.5 Binary operators', '467'), |
| 280 | ('G.6 Complex arithmetic <complex.h>', '471'), |
| 281 | ('G.7 Type-generic math <tgmath.h>', '479'), |
| 282 | ('Annex H (informative) Language independent arithmetic', '480'), |
| 283 | ('H.1 Introduction', '480'), |
| 284 | ('H.2 Types', '480'), |
| 285 | ('H.3 Notification', '484'), |
| 286 | ('Annex I (informative) Common warnings', '486'), |
| 287 | ('Annex J (informative) Portability issues', '488'), |
| 288 | ('J.1 Unspecified behavior', '488'), |
| 289 | ('J.2 Undefined behavior', '491'), |
| 290 | ('J.3 Implementation-defined behavior', '504'), |
| 291 | ('J.4 Locale-specific behavior', '511'), |
| 292 | ('J.5 Common extensions', '512'), |
| 293 | ('Bibliography', '515'), |
| 294 | ('Index', '517')] |
| 295 | |
| 296 | kDocuments = { |
| 297 | 'C99' : (c99URL, c99TOC, 12) |
| 298 | } |
| 299 | |
| 300 | def findClosestTOCEntry(data, target): |
| 301 | offset = data[2] |
| 302 | best = None |
| 303 | for (name,page) in data[1]: |
| 304 | if ' ' in name: |
| 305 | section,name = name.split(' ',1) |
| 306 | if section == 'Annex': |
| 307 | section,name = name.split(' ',1) |
| 308 | section = 'Annex '+section |
| 309 | else: |
| 310 | section = None |
| 311 | try: |
| 312 | page = int(page) + offset |
| 313 | except: |
| 314 | page = 1 |
| 315 | try: |
| 316 | spec = SpecIndex.fromstring(section) |
| 317 | except: |
| 318 | spec = None |
| 319 | |
| 320 | # Meh, could be better... |
| 321 | if spec is not None: |
| 322 | dist = spec - target |
| 323 | if best is None or dist < best[0]: |
| 324 | best = (dist, (section, name, page)) |
| 325 | return best[1] |
| 326 | |
| 327 | # What a hack. Slow to boot. |
| 328 | doxyLineRefRE = re.compile(r"<a name=\"l([0-9]+)\"></a>") |
| 329 | def findClosestLineReference(clangRoot, doxyName, target): |
| 330 | try: |
| 331 | f = open(os.path.join(clangRoot, 'docs', 'doxygen', 'html', doxyName)) |
| 332 | except: |
| 333 | return None |
| 334 | |
| 335 | best = None |
| 336 | for m in doxyLineRefRE.finditer(f.read()): |
| 337 | line = int(m.group(1), 10) |
| 338 | dist = abs(line - target) |
| 339 | if best is None or dist < best[0]: |
| 340 | best = (dist,'l'+m.group(1)) |
| 341 | f.close() |
| 342 | if best is not None: |
| 343 | return best[1] |
| 344 | return None |
| 345 | |
| 346 | ### |
| 347 | |
| 348 | nameAndSpecRefRE = re.compile(r"(C99|C90|C\+\+|H\&S) (([0-9]+)(\.[0-9]+)*(p[0-9]+)?)") |
| 349 | loneSpecRefRE = re.compile(r" (([0-9]+)(\.[0-9]+){2,100}(p[0-9]+)?)") |
| 350 | def scanFile(path, filename): |
| 351 | try: |
| 352 | f = open(path) |
| 353 | except IOError: |
| 354 | print >>sys.stderr,'WARNING: Unable to open:',path |
| 355 | return |
| 356 | |
| 357 | try: |
| 358 | for i,ln in enumerate(f): |
| 359 | ignore = set() |
| 360 | for m in nameAndSpecRefRE.finditer(ln): |
| 361 | section = m.group(2) |
| 362 | name = m.group(1) |
| 363 | if section.endswith('.'): |
| 364 | section = section[:-1] |
| 365 | yield RefItem(name, section, filename, path, i+1) |
| 366 | ignore.add(section) |
| 367 | for m in loneSpecRefRE.finditer(ln): |
| 368 | section = m.group(1) |
| 369 | if section.endswith('.'): |
| 370 | section = section[:-1] |
| 371 | if section not in ignore: |
| 372 | yield RefItem(None, section, filename, path, i+1) |
| 373 | finally: |
| 374 | f.close() |
| 375 | |
| 376 | ### |
| 377 | |
| 378 | class SpecIndex: |
| 379 | @staticmethod |
| 380 | def fromstring(str): |
| 381 | secs = str.split('.') |
| 382 | paragraph = None |
| 383 | if 'p' in secs[-1]: |
| 384 | secs[-1],p = secs[-1].split('p',1) |
| 385 | paragraph = int(p) |
| 386 | indices = map(int, secs) |
| 387 | return SpecIndex(indices, paragraph) |
| 388 | |
| 389 | def __init__(self, indices, paragraph=None): |
| 390 | assert len(indices)>0 |
| 391 | self.indices = tuple(indices) |
| 392 | self.paragraph = paragraph |
| 393 | |
| 394 | def __str__(self): |
| 395 | s = '.'.join(map(str,self.indices)) |
| 396 | if self.paragraph is not None: |
| 397 | s += '.p%d'%(self.paragraph,) |
| 398 | return s |
| 399 | |
| 400 | def __repr__(self): |
| 401 | return 'SpecIndex(%s, %s)'%(self.indices, self.paragraph) |
| 402 | |
| 403 | def __cmp__(self, b): |
| 404 | return cmp((self.indices,self.paragraph), |
| 405 | (b.indices,b.paragraph)) |
| 406 | |
| 407 | def __hash__(self): |
| 408 | return hash((self.indices,self.paragraph)) |
| 409 | |
| 410 | def __sub__(self, indices): |
| 411 | def sub(a,b): |
| 412 | a = a or 0 |
| 413 | b = b or 0 |
| 414 | return abs(a-b) |
| 415 | return map(sub,self.indices,indices) |
| 416 | |
| 417 | class RefItem: |
| 418 | def __init__(self, name, section, filename, path, line): |
| 419 | self.name = name |
| 420 | self.section = SpecIndex.fromstring(section) |
| 421 | self.filename = filename |
| 422 | self.path = path |
| 423 | self.line = line |
| 424 | |
| 425 | def __str__(self): |
| 426 | if self.name is not None: |
| 427 | return '%s %s'%(self.name, self.section) |
| 428 | else: |
| 429 | return '--- %s'%(self.section,) |
| 430 | |
| 431 | def __repr__(self): |
| 432 | return 'RefItem(%s, %r, "%s", "%s", %d)'%(self.name, |
| 433 | self.section, |
| 434 | self.filename, |
| 435 | self.path, |
| 436 | self.line) |
| 437 | |
| 438 | def __cmp__(self, b): |
| 439 | return cmp((self.name,self.section,self.filename,self.path,self.line), |
| 440 | (b.name,b.section,self.filename,self.path,self.line)) |
| 441 | |
| 442 | def __hash__(self): |
| 443 | return hash((self.name,self.section,self.filename,self.path,self.line)) |
| 444 | |
| 445 | ### |
| 446 | |
| 447 | def sorted(l): |
| 448 | l = list(l) |
| 449 | l.sort() |
| 450 | return l |
| 451 | |
| 452 | def getRevision(path): |
| 453 | import svn, svn.core, svn.client |
| 454 | |
| 455 | revision = [None] |
| 456 | |
| 457 | def info_cb(path, info, pool): |
| 458 | revision[0] = info.rev |
| 459 | |
| 460 | try: |
| 461 | root = os.path.abspath(path) |
| 462 | svn.core.apr_initialize() |
| 463 | pool = svn.core.svn_pool_create(None) |
| 464 | ctx = svn.client.svn_client_ctx_t() |
| 465 | svn.client.svn_client_info(root, |
| 466 | None, |
| 467 | None, |
| 468 | info_cb, |
| 469 | False, |
| 470 | ctx, |
| 471 | pool) |
| 472 | svn.core.svn_pool_destroy(pool) |
| 473 | except: |
| 474 | pass |
| 475 | |
| 476 | return revision[0] |
| 477 | |
| 478 | def buildRefTree(references): |
| 479 | root = (None, {}, []) |
| 480 | |
| 481 | def getNode(keys): |
| 482 | if not keys: |
| 483 | return root |
| 484 | key,parent = keys[-1],getNode(keys[:-1]) |
| 485 | node = parent[1].get(key) |
| 486 | if node is None: |
| 487 | parent[1][key] = node = (key, {}, []) |
| 488 | return node |
| 489 | |
| 490 | for ref in references: |
| 491 | n = getNode((ref.name,) + ref.section.indices) |
| 492 | n[2].append(ref) |
| 493 | |
| 494 | def flatten((key, children, data)): |
| 495 | children = sorted(map(flatten,children.values())) |
| 496 | return (key, children, sorted(data)) |
| 497 | |
| 498 | return flatten(root) |
| 499 | |
| 500 | def preorder(node,parents=(),first=True): |
| 501 | (key,children,data) = node |
| 502 | if first: |
| 503 | yield parents+(node,) |
| 504 | for c in children: |
| 505 | for item in preorder(c, parents+(node,)): |
| 506 | yield item |
| 507 | |
| 508 | def main(): |
| 509 | global options |
| 510 | from optparse import OptionParser |
| 511 | parser = OptionParser("usage: %prog [options] CLANG_ROOT <output-dir>") |
| 512 | |
| 513 | (options, args) = parser.parse_args() |
| 514 | |
| 515 | if len(args) != 2: |
| 516 | parser.error("incorrect number of arguments") |
| 517 | |
| 518 | references = [] |
| 519 | root,outputDir = args |
| 520 | for (dirpath, dirnames, filenames) in os.walk(root): |
| 521 | for filename in filenames: |
| 522 | name,ext = os.path.splitext(filename) |
| 523 | if ext in ('.c', '.cpp', '.h', '.def'): |
| 524 | fullpath = os.path.join(dirpath, filename) |
| 525 | references.extend(list(scanFile(fullpath, filename))) |
| 526 | |
| 527 | refTree = buildRefTree(references) |
| 528 | |
| 529 | specs = {} |
| 530 | for ref in references: |
| 531 | spec = specs[ref.name] = specs.get(ref.name,{}) |
| 532 | items = spec[ref.section] = spec.get(ref.section,[]) |
| 533 | items.append(ref) |
| 534 | |
| 535 | print 'Found %d references.'%(len(references),) |
| 536 | |
| 537 | referencesPath = os.path.join(outputDir,'references.html') |
| 538 | print 'Writing: %s'%(referencesPath,) |
| 539 | f = open(referencesPath,'w') |
| 540 | print >>f, '<html><head><title>clang: Specification References</title></head>' |
| 541 | print >>f, '<body>' |
| 542 | print >>f, '\t<h2>Specification References</h2>' |
| 543 | for i,node in enumerate(refTree[1]): |
| 544 | specName = node[0] or 'Unknown' |
| 545 | print >>f, '<a href="#spec%d">%s</a><br>'%(i,specName) |
| 546 | for i,node in enumerate(refTree[1]): |
| 547 | specName = node[0] or 'Unknown' |
| 548 | print >>f, '<hr>' |
| 549 | print >>f, '<a name="spec%d">'%(i,) |
| 550 | print >>f, '<h3>Document: %s</h3>'%(specName or 'Unknown',) |
| 551 | print >>f, '<table border="1" cellspacing="2" width="80%">' |
| 552 | print >>f, '<tr><th width="20%">Name</th><th>References</th></tr>' |
| 553 | docData = kDocuments.get(specName) |
| 554 | for path in preorder(node,first=False): |
| 555 | if not path[-1][2]: |
| 556 | continue |
| 557 | components = '.'.join([str(p[0]) for p in path[1:]]) |
| 558 | print >>f, '\t<tr>' |
| 559 | tocEntry = None |
| 560 | if docData is not None: |
| 561 | tocEntry = findClosestTOCEntry(docData, [p[0] for p in path[1:]]) |
| 562 | if tocEntry is not None: |
| 563 | section,name,page = tocEntry |
| 564 | # If section is exact print the TOC name |
| 565 | if page is not None: |
| 566 | linkStr = '<a href="%s#page=%d">%s</a> (pg.%d)'%(docData[0],page,components,page) |
| 567 | else: |
| 568 | linkStr = components |
| 569 | if section == components: |
| 570 | print >>f, '\t\t<td valign=top>%s<br>%s</td>'%(linkStr,name) |
| 571 | else: |
| 572 | print >>f, '\t\t<td valign=top>%s</td>'%(linkStr,) |
| 573 | else: |
| 574 | print >>f, '\t\t<td valign=top>%s</td>'%(components,) |
| 575 | print >>f, '\t\t<td valign=top>' |
| 576 | for item in path[-1][2]: |
| 577 | # XXX total hack |
| 578 | relativePath = item.path[len(root):] |
| 579 | if relativePath.startswith('/'): |
| 580 | relativePath = relativePath[1:] |
| 581 | # XXX this is broken, how does doxygen mangle w/ multiple |
| 582 | # refs? Can we just read its map? |
| 583 | filename = os.path.basename(relativePath) |
| 584 | doxyName = '%s-source.html'%(filename.replace('.','_8'),) |
| 585 | # Grrr, why can't doxygen write line number references. |
| 586 | lineReference = findClosestLineReference(root,doxyName,item.line) |
| 587 | if lineReference is not None: |
| 588 | linkStr = 'http://clang.llvm.org/doxygen/%s#%s'%(doxyName,lineReference) |
| 589 | else: |
| 590 | linkStr = 'http://clang.llvm.org/doxygen/%s'%(doxyName,) |
| 591 | if item.section.paragraph is not None: |
| 592 | paraText = ' (p%d)'%(item.section.paragraph,) |
| 593 | else: |
| 594 | paraText = '' |
| 595 | print >>f,'<a href="%s">%s:%d</a>%s<br>'%(linkStr,relativePath,item.line,paraText) |
| 596 | print >>f, '\t\t</td>' |
| 597 | print >>f, '\t</tr>' |
| 598 | print >>f, '</table>' |
| 599 | print >>f, '<hr>' |
| 600 | print >>f, 'Generated: %s<br>'%(time.strftime('%Y-%m-%d %H:%M'),) |
| 601 | print >>f, 'SVN Revision: %s'%(getRevision(root),) |
| 602 | print >>f, '</body>' |
| 603 | f.close() |
| 604 | |
| 605 | if __name__=='__main__': |
| 606 | main() |