Coverage for /builds/ahmed.baizid.0/gtk-doc/gtkdoc/mkdb.py: 8%

2684 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2024-11-05 19:25 +0000

1# -*- python; coding: utf-8 -*- 

2# 

3# gtk-doc - GTK DocBook documentation generator. 

4# Copyright (C) 1998 Damon Chaplin 

5# 2007-2016 Stefan Sauer 

6# 

7# This program is free software; you can redistribute it and/or modify 

8# it under the terms of the GNU General Public License as published by 

9# the Free Software Foundation; either version 2 of the License, or 

10# (at your option) any later version. 

11# 

12# This program is distributed in the hope that it will be useful, 

13# but WITHOUT ANY WARRANTY; without even the implied warranty of 

14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

15# GNU General Public License for more details. 

16# 

17# You should have received a copy of the GNU General Public License 

18# along with this program; if not, write to the Free Software 

19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 

20# 

21 

22""" 

23Creates the DocBook files from the source comments. 

24""" 

25 

26import argparse 

27from collections import OrderedDict 

28import logging 

29import os 

30import re 

31import string 

32 

33from . import common, config, md_to_db 

34 

35# Options 

36MODULE = None 

37DB_OUTPUT_DIR = None 

38INLINE_MARKUP_MODE = None 

39NAME_SPACE = '' 

40ROOT_DIR = '.' 

41 

42# These global arrays store information on signals. Each signal has an entry 

43# in each of these arrays at the same index, like a multi-dimensional array. 

44SignalObjects = [] # The GtkObject which emits the signal. 

45SignalNames = [] # The signal name. 

46SignalReturns = [] # The return type. 

47SignalFlags = [] # Flags for the signal 

48SignalPrototypes = [] # The rest of the prototype of the signal handler. 

49 

50# These global arrays store information on Args. Each Arg has an entry 

51# in each of these arrays at the same index, like a multi-dimensional array. 

52ArgObjects = [] # The GtkObject which has the Arg. 

53ArgNames = [] # The Arg name. 

54ArgTypes = [] # The Arg type - gint, GtkArrowType etc. 

55ArgFlags = [] # How the Arg can be used - readable/writable etc. 

56ArgNicks = [] # The nickname of the Arg. 

57ArgBlurbs = [] # Docstring of the Arg. 

58ArgDefaults = [] # Default value of the Arg. 

59ArgRanges = [] # The range of the Arg type 

60 

61ActionObjects = [] 

62ActionNames = [] 

63ActionParams = [] 

64ActionProperties = [] 

65 

66# These global hashes store declaration info keyed on a symbol name. 

67Declarations = {} 

68DeclarationTypes = {} 

69DeclarationConditional = {} 

70DeclarationOutput = {} 

71Deprecated = {} 

72Since = {} 

73StabilityLevel = {} 

74StructHasTypedef = {} 

75 

76# These global hashes store the existing documentation. 

77SymbolDocs = {} 

78SymbolParams = {} 

79SymbolAnnotations = {} 

80 

81# These global hashes store documentation scanned from the source files. 

82SourceSymbolDocs = {} 

83SourceSymbolParams = {} 

84SymbolSourceLocation = {} 

85 

86# all documentation goes in here, so we can do coverage analysis 

87AllSymbols = {} 

88AllIncompleteSymbols = {} 

89AllUnusedSymbols = {} 

90AllDocumentedSymbols = {} 

91 

92# Undeclared yet documented symbols 

93UndeclaredSymbols = {} 

94 

95# These global arrays store GObject, subclasses and the hierarchy (also of 

96# non-object derived types). 

97Objects = [] 

98ObjectLevels = [] 

99ObjectRoots = {} 

100 

101Interfaces = {} 

102Prerequisites = {} 

103 

104# holds the symbols which are mentioned in <MODULE>-sections.txt and in which 

105# section they are defined 

106KnownSymbols = {} # values are 1 for public symbols and 0 otherwise 

107SymbolSection = {} 

108SymbolSectionId = {} 

109 

110# collects index entries 

111IndexEntriesFull = {} 

112IndexEntriesSince = {} 

113IndexEntriesDeprecated = {} 

114 

115# Standard C preprocessor directives, which we ignore for '#' abbreviations. 

116PreProcessorDirectives = { 

117 'assert', 'define', 'elif', 'else', 'endif', 'error', 'if', 'ifdef', 'ifndef', 

118 'include', 'line', 'pragma', 'unassert', 'undef', 'warning' 

119} 

120 

121# remember used annotation (to write minimal glossary) 

122AnnotationsUsed = {} 

123 

124# the regexp that parses the annotation is in ScanSourceFile() 

125AnnotationDefinition = { 

126 # the GObjectIntrospection annotations are defined at: 

127 # https://gi.readthedocs.io/en/latest/annotations/giannotations.html 

128 'allow-none': "NULL is OK, both for passing and for returning.", 

129 'nullable': "NULL may be passed as the value in, out, in-out; or as a return value.", 

130 'not nullable': "NULL must not be passed as the value in, out, in-out; or as a return value.", 

131 'optional': "NULL may be passed instead of a pointer to a location.", 

132 'not optional': "NULL must not be passed as the pointer to a location.", 

133 'array': "Parameter points to an array of items.", 

134 'attribute': "Deprecated free-form custom annotation, replaced by (attributes) annotation.", 

135 'attributes': "Free-form key-value pairs.", 

136 'closure': "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.", 

137 'constructor': "This symbol is a constructor, not a static method.", 

138 'destroy': "This parameter is a 'destroy_data', for callbacks.", 

139 'default': "Default parameter value (in case a function which shadows this one via <acronym>rename-to</acronym> has fewer parameters).", 

140 'element-type': "Generics and defining elements of containers and arrays.", 

141 'error-domains': "Typed errors. Similar to throws in Java.", 

142 'foreign': "This is a foreign struct.", 

143 'get-value-func': "The specified function is used to convert a struct from a GValue, must be a GTypeInstance.", 

144 'in': "Parameter for input. Default is <acronym>transfer none</acronym>.", 

145 'inout': "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.", 

146 'in-out': "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.", 

147 'method': "This is a method", 

148 'not-error': "A GError parameter is not to be handled like a normal GError.", 

149 'out': "Parameter for returning results. Default is <acronym>transfer full</acronym>.", 

150 'out caller-allocates': "Out parameter, where caller must allocate storage.", 

151 'out callee-allocates': "Out parameter, where callee must allocate storage.", 

152 'ref-func': "The specified function is used to ref a struct, must be a GTypeInstance.", 

153 'rename-to': "Rename the original symbol's name to SYMBOL.", 

154 'scope call': "The callback is valid only during the call to the method.", 

155 'scope async': "The callback is valid until first called.", 

156 'scope notified': "The callback is valid until the GDestroyNotify argument is called.", 

157 'set-value-func': "The specified function is used to convert from a struct to a GValue, must be a GTypeInstance.", 

158 'skip': "Exposed in C code, not necessarily available in other languages.", 

159 'transfer container': "The caller owns the data container, but not the data inside it.", 

160 'transfer floating': "Alias for <acronym>transfer none</acronym>, used for objects with floating refs.", 

161 'transfer full': "The caller owns the data, and is responsible for free it.", 

162 'transfer none': "The data is owned by the callee, which is responsible of freeing it.", 

163 'type': "Override the parsed C type with given type.", 

164 'unref-func': "The specified function is used to unref a struct, must be a GTypeInstance.", 

165 'virtual': "This is the invoker for a virtual method.", 

166 'value': "The specified value overrides the evaluated value of the constant.", 

167 # Stability Level definition 

168 # https://bugzilla.gnome.org/show_bug.cgi?id=170860 

169 'Stable': '''The intention of a Stable interface is to enable arbitrary third parties to 

170develop applications to these interfaces, release them, and have confidence that 

171they will run on all minor releases of the product (after the one in which the 

172interface was introduced, and within the same major release). Even at a major 

173release, incompatible changes are expected to be rare, and to have strong 

174justifications. 

175''', 

176 'Unstable': '''Unstable interfaces are experimental or transitional. They are typically used to 

177give outside developers early access to new or rapidly changing technology, or 

178to provide an interim solution to a problem where a more general solution is 

179anticipated. No claims are made about either source or binary compatibility from 

180one minor release to the next. 

181 

182The Unstable interface level is a warning that these interfaces are subject to 

183change without warning and should not be used in unbundled products. 

184 

185Given such caveats, customer impact need not be a factor when considering 

186incompatible changes to an Unstable interface in a major or minor release. 

187Nonetheless, when such changes are introduced, the changes should still be 

188mentioned in the release notes for the affected release. 

189''', 

190 'Private': '''An interface that can be used within the GNOME stack itself, but that is not 

191documented for end-users. Such functions should only be used in specified and 

192documented ways. 

193''', 

194} 

195 

196# Function and other declaration output settings. 

197RETURN_TYPE_FIELD_WIDTH = 20 

198MAX_SYMBOL_FIELD_WIDTH = 40 

199 

200# XML header 

201doctype_header = None 

202 

203# docbook templates 

204DB_REFENTRY = string.Template('''${header} 

205<refentry id="${section_id}"> 

206<refmeta> 

207<refentrytitle role="top_of_page" id="${section_id}.top_of_page">${title}</refentrytitle> 

208<manvolnum>3</manvolnum> 

209<refmiscinfo>${MODULE} Library${image}</refmiscinfo> 

210</refmeta> 

211<refnamediv> 

212<refname>${title}</refname> 

213<refpurpose>${short_desc}</refpurpose> 

214</refnamediv> 

215${stability} 

216${functions_synop}${args_synop}${signals_synop}${actions_synop}${object_anchors}${other_synop}${hierarchy}${prerequisites}${derived}${interfaces}${implementations} 

217${include_output} 

218<refsect1 id="${section_id}.description" role="desc"> 

219<title role="desc.title">Description</title> 

220${extralinks}${long_desc} 

221</refsect1> 

222<refsect1 id="${section_id}.functions_details" role="details"> 

223<title role="details.title">Functions</title> 

224${functions_details} 

225</refsect1> 

226${other_desc}${args_desc}${signals_desc}${actions_desc}${see_also} 

227</refentry> 

228''') 

229 

230DB_REFSECT1_SYNOPSIS3 = string.Template('''<refsect1 id="${section_id}.${type}" role="${role}"> 

231<title role="${role}.title">${title}</title> 

232<informaltable frame="none"> 

233<tgroup cols="3"> 

234<colspec colname="${role}_type" colwidth="150px"/> 

235<colspec colname="${role}_name" colwidth="300px"/> 

236<colspec colname="${role}_flags" colwidth="200px"/> 

237<tbody> 

238${content} 

239</tbody> 

240</tgroup> 

241</informaltable> 

242</refsect1> 

243''') 

244 

245DB_REFSECT1_SYNOPSIS2 = string.Template('''<refsect1 id="${section_id}.${type}" role="${role}"> 

246<title role="${role}.title">${title}</title> 

247<informaltable pgwide="1" frame="none"> 

248<tgroup cols="2"> 

249<colspec colname="${role}_type" colwidth="150px"/> 

250<colspec colname="${role}_name"/> 

251<tbody> 

252${content} 

253</tbody> 

254</tgroup> 

255</informaltable> 

256</refsect1> 

257''') 

258 

259DB_REFSECT1_DESC = string.Template('''<refsect1 id="${section_id}.${type}" role="${role}"> 

260<title role="${role}.title">${title}</title> 

261${content} 

262</refsect1> 

263''') 

264 

265 

266def Run(options): 

267 global MODULE, INLINE_MARKUP_MODE, NAME_SPACE, DB_OUTPUT_DIR, doctype_header 

268 

269 logging.info('options: %s', str(options.__dict__)) 

270 

271 # We should pass the options variable around instead of this global variable horror 

272 # but too much of the code expects these to be around. Fix this once the transition is done. 

273 MODULE = options.module 

274 INLINE_MARKUP_MODE = options.xml_mode or options.sgml_mode 

275 NAME_SPACE = options.name_space 

276 DB_OUTPUT_DIR = options.output_dir or os.path.join(ROOT_DIR, "xml") 

277 

278 main_sgml_file = options.main_sgml_file 

279 if not main_sgml_file: 

280 # backwards compatibility 

281 if os.path.exists(MODULE + "-docs.sgml"): 

282 main_sgml_file = MODULE + "-docs.sgml" 

283 else: 

284 main_sgml_file = MODULE + "-docs.xml" 

285 

286 # -- phase 1: read files produced by previous tools and scane sources 

287 

288 # extract docbook header or define default 

289 doctype_header = GetDocbookHeader(main_sgml_file) 

290 

291 ReadKnownSymbols(os.path.join(ROOT_DIR, MODULE + "-sections.txt")) 

292 ReadSignalsFile(os.path.join(ROOT_DIR, MODULE + ".signals")) 

293 ReadArgsFile(os.path.join(ROOT_DIR, MODULE + ".args")) 

294 ReadActionsFile(os.path.join(ROOT_DIR, MODULE + ".actions")) 

295 obj_tree = ReadObjectHierarchy(os.path.join(ROOT_DIR, MODULE + ".hierarchy")) 

296 ReadInterfaces(os.path.join(ROOT_DIR, MODULE + ".interfaces")) 

297 ReadPrerequisites(os.path.join(ROOT_DIR, MODULE + ".prerequisites")) 

298 

299 ReadDeclarationsFile(os.path.join(ROOT_DIR, MODULE + "-decl.txt"), 0) 

300 if os.path.isfile(os.path.join(ROOT_DIR, MODULE + "-overrides.txt")): 

301 ReadDeclarationsFile(os.path.join(ROOT_DIR, MODULE + "-overrides.txt"), 1) 

302 

303 logging.info("Data files read") 

304 

305 # -- phase 2: scan sources 

306 

307 # TODO: move this to phase 3 once we fixed the call to OutputProgramDBFile() 

308 if not os.path.isdir(DB_OUTPUT_DIR): 

309 os.mkdir(DB_OUTPUT_DIR) 

310 

311 # Scan sources 

312 if options.source_suffixes: 

313 suffix_list = ['.' + ext for ext in options.source_suffixes.split(',')] 

314 else: 

315 suffix_list = ['.c', '.h'] 

316 

317 source_dirs = options.source_dir 

318 ignore_files = options.ignore_files 

319 logging.info(" ignore files: " + ignore_files) 

320 for sdir in source_dirs: 

321 ReadSourceDocumentation(sdir, suffix_list, source_dirs, ignore_files) 

322 

323 logging.info("Sources scanned") 

324 

325 # -- phase 3: write docbook files 

326 

327 changed, book_top, book_bottom = OutputDB(os.path.join(ROOT_DIR, MODULE + "-sections.txt"), options) 

328 OutputBook(main_sgml_file, book_top, book_bottom, obj_tree) 

329 

330 # If any of the DocBook files have changed, update the timestamp file (so 

331 # it can be used for Makefile dependencies). 

332 if changed or not os.path.exists(os.path.join(ROOT_DIR, "sgml.stamp")): 

333 # TODO: MakeIndexterms() uses NAME_SPACE, but also fills IndexEntriesFull 

334 # which DetermineNamespace is using 

335 # Can we use something else? 

336 # no: AllSymbols, KnownSymbols 

337 # IndexEntriesFull: consist of all symbols from sections file + signals and properties 

338 # 

339 # logging.info('# index_entries_full=%d, # declarations=%d', 

340 # len(IndexEntriesFull), len(Declarations)) 

341 # logging.info('known_symbols - index_entries_full: ' + str(Declarations.keys() - IndexEntriesFull.keys())) 

342 

343 # try to detect the common prefix 

344 # GtkWidget, GTK_WIDGET, gtk_widget -> gtk 

345 if NAME_SPACE == '': 

346 NAME_SPACE = DetermineNamespace(IndexEntriesFull.keys()) 

347 

348 logging.info('namespace prefix ="%s"', NAME_SPACE) 

349 

350 OutputObjectTree(obj_tree) 

351 OutputObjectList(Objects) 

352 

353 OutputIndex("api-index-full", IndexEntriesFull) 

354 OutputIndex("api-index-deprecated", IndexEntriesDeprecated) 

355 OutputSinceIndexes() 

356 OutputAnnotationGlossary() 

357 

358 with open(os.path.join(ROOT_DIR, 'sgml.stamp'), 'w') as h: 

359 h.write('timestamp') 

360 

361 logging.info("All files created: %d", changed) 

362 

363 

364def OutputObjectList(obj_list): 

365 """This outputs the alphabetical list of objects, in a columned table.""" 

366 # FIXME: Currently this also outputs ancestor objects which may not actually 

367 # be in this module. 

368 

369 # TODO(ensonic): consider not writing this unconditionally 

370 if not obj_list: 

371 return 

372 

373 cols = 3 

374 

375 # FIXME: use .xml 

376 old_object_index = os.path.join(DB_OUTPUT_DIR, "object_index.sgml") 

377 new_object_index = os.path.join(DB_OUTPUT_DIR, "object_index.new") 

378 

379 OUTPUT = open(new_object_index, 'w', encoding='utf-8') 

380 

381 OUTPUT.write('''%s 

382<informaltable pgwide="1" frame="none"> 

383<tgroup cols="%s"> 

384<colspec colwidth="1*"/> 

385<colspec colwidth="1*"/> 

386<colspec colwidth="1*"/> 

387<tbody> 

388''' % (MakeDocHeader("informaltable"), cols)) 

389 

390 count = 0 

391 object = None 

392 for object in sorted(Objects): 

393 xref = MakeXRef(object) 

394 if count % cols == 0: 

395 OUTPUT.write("<row>\n") 

396 OUTPUT.write("<entry>%s</entry>\n" % xref) 

397 if count % cols == cols - 1: 

398 OUTPUT.write("</row>\n") 

399 count += 1 

400 

401 if count % cols > 0: 

402 OUTPUT.write("</row>\n") 

403 

404 OUTPUT.write('''</tbody></tgroup></informaltable>\n''') 

405 OUTPUT.close() 

406 

407 common.UpdateFileIfChanged(old_object_index, new_object_index, 0) 

408 

409 

410def trim_leading_and_trailing_nl(text): 

411 """Trims extra newlines. 

412 

413 Leaves a single trailing newline. 

414 

415 Args: 

416 text (str): the text block to trim. May contain newlines. 

417 

418 Returns: 

419 (str): trimmed text 

420 """ 

421 text = re.sub(r'^\n*', '', text) 

422 return re.sub(r'\n+$', '\n', text) 

423 

424 

425def trim_white_spaces(text): 

426 """Trims extra whitespace. 

427 

428 Empty lines inside a block are preserved. All whitespace and the end is 

429 replaced with a single newline. 

430 

431 Args: 

432 text (str): the text block to trim. May contain newlines. 

433 

434 Returns: 

435 (str): trimmed text 

436 """ 

437 

438 # strip trailing spaces on every line 

439 return re.sub(r'\s+$', '\n', text.lstrip(), flags=re.MULTILINE) 

440 

441 

442def make_refsect1_synopsis(tmpl, content, title, section_id, section_type, role=None): 

443 # TODO(ensonic): canonicalize xml to use the same string for section_type 

444 # and role. Needs fixes on gtk-doc.xsl 

445 if role is None: 

446 role = section_type.replace('-', '_') 

447 

448 return tmpl.substitute({ 

449 'content': content, 

450 'role': role, 

451 'section_id': section_id, 

452 'title': title, 

453 'type': section_type, 

454 }) 

455 

456 

457def make_refsect1_synopsis2(content, title, section_id, section_type, role=None): 

458 return make_refsect1_synopsis(DB_REFSECT1_SYNOPSIS2, content, title, section_id, section_type, role) 

459 

460 

461def make_refsect1_synopsis3(content, title, section_id, section_type, role=None): 

462 return make_refsect1_synopsis(DB_REFSECT1_SYNOPSIS3, content, title, section_id, section_type, role) 

463 

464 

465def make_refsect1_desc(content, title, section_id, section_type, role=None): 

466 content = trim_white_spaces(content) 

467 if content == '': 

468 return '' 

469 

470 # TODO(ensonic): canonicalize xml to use the same string for section_type 

471 # and role. Needs fixes on gtk-doc.xsl 

472 if role is None: 

473 role = section_type.replace('-', '_') 

474 

475 return DB_REFSECT1_DESC.substitute({ 

476 'content': content, 

477 'role': role, 

478 'section_id': section_id, 

479 'title': title, 

480 'type': section_type, 

481 }) 

482 

483 

484def OutputDB(file, options): 

485 """Generate docbook files. 

486 

487 This collects the output for each section of the docs, and outputs each file 

488 when the end of the section is found. 

489 

490 Args: 

491 file (str): the $MODULE-sections.txt file which contains all of the 

492 functions/macros/structs etc. being documented, organised 

493 into sections and subsections. 

494 options: commandline options 

495 """ 

496 

497 logging.info("Reading: %s", file) 

498 INPUT = open(file, 'r', encoding='utf-8') 

499 filename = '' 

500 book_top = '' 

501 book_bottom = '' 

502 includes = options.default_includes or '' 

503 section_includes = '' 

504 in_section = 0 

505 title = '' 

506 section_id = '' 

507 subsection = '' 

508 num_symbols = 0 

509 changed = 0 

510 functions_synop = '' 

511 other_synop = '' 

512 functions_details = '' 

513 other_details = '' 

514 other_desc = '' 

515 signals_synop = '' 

516 signals_desc = '' 

517 args_synop = '' 

518 child_args_synop = '' 

519 style_args_synop = '' 

520 args_desc = '' 

521 child_args_desc = '' 

522 style_args_desc = '' 

523 actions_synop = '' 

524 actions_desc = '' 

525 hierarchy_str = '' 

526 hierarchy = [] 

527 interfaces = '' 

528 implementations = '' 

529 prerequisites = '' 

530 derived = '' 

531 file_objects = [] 

532 file_def_line = {} 

533 symbol_def_line = {} 

534 

535 MergeSourceDocumentation() 

536 

537 line_number = 0 

538 for line in INPUT: 

539 line_number += 1 

540 

541 if line.startswith('#'): 

542 continue 

543 

544 logging.info("section file data: %d: %s", line_number, line) 

545 

546 m1 = re.search(r'^<SUBSECTION\s*(.*)>', line, re.I) 

547 m2 = re.search(r'^<TITLE>(.*)<\/TITLE', line) 

548 m3 = re.search(r'^<FILE>(.*)<\/FILE>', line) 

549 m4 = re.search(r'^<INCLUDE>(.*)<\/INCLUDE>', line) 

550 m5 = re.search(r'^(\S+)', line) 

551 

552 if line.startswith('<SECTION>'): 

553 num_symbols = 0 

554 in_section = False 

555 file_objects = [] 

556 symbol_def_line = {} 

557 

558 elif m1: 

559 other_synop += "\n" 

560 functions_synop += "\n" 

561 subsection = m1.group(1) 

562 

563 elif line.startswith('<SUBSECTION>'): 

564 continue 

565 elif m2: 

566 title = m2.group(1) 

567 logging.info("Section: %s", title) 

568 

569 # We don't want warnings if object & class structs aren't used. 

570 DeclarationOutput[title] = 1 

571 DeclarationOutput["%sClass" % title] = 1 

572 DeclarationOutput["%sIface" % title] = 1 

573 DeclarationOutput["%sInterface" % title] = 1 

574 

575 elif m3: 

576 filename = m3.group(1) 

577 if filename not in file_def_line: 

578 file_def_line[filename] = line_number 

579 else: 

580 common.LogWarning(file, line_number, "Double <FILE>%s</FILE> entry. Previous occurrence on line %s." % 

581 (filename, file_def_line[filename])) 

582 if title == '': 

583 key = filename + ":title" 

584 if key in SourceSymbolDocs: 

585 title = SourceSymbolDocs[key].rstrip() 

586 

587 elif m4: 

588 if in_section: 

589 section_includes = m4.group(1) 

590 else: 

591 if options.default_includes: 

592 common.LogWarning(file, line_number, "Default <INCLUDE> being overridden by command line option.") 

593 else: 

594 includes = m4.group(1) 

595 

596 elif re.search(r'^<\/SECTION>', line): 

597 logging.info("End of section: %s", title) 

598 # TODO: also output if we have sections docs? 

599 # long_desc = SymbolDocs.get(filename + ":long_description") 

600 if num_symbols > 0: 

601 # collect documents 

602 book_bottom += " <xi:include href=\"xml/%s.xml\"/>\n" % filename 

603 

604 key = filename + ":include" 

605 if key in SourceSymbolDocs: 

606 if section_includes: 

607 common.LogWarning(file, line_number, "Section <INCLUDE> being overridden by inline comments.") 

608 section_includes = SourceSymbolDocs[key] 

609 

610 if section_includes == '': 

611 section_includes = includes 

612 

613 signals_synop = trim_leading_and_trailing_nl(signals_synop) 

614 if signals_synop != '': 

615 signals_synop = make_refsect1_synopsis3( 

616 signals_synop, 'Signals', section_id, 'signals', 'signal_proto') 

617 signals_desc = make_refsect1_desc(signals_desc, 'Signal Details', 

618 section_id, 'signal-details', 'signals') 

619 

620 args_synop = trim_leading_and_trailing_nl(args_synop) 

621 if args_synop != '': 

622 args_synop = make_refsect1_synopsis3(args_synop, 'Properties', section_id, 'properties') 

623 args_desc = make_refsect1_desc(args_desc, 'Property Details', section_id, 'property-details') 

624 

625 child_args_synop = trim_leading_and_trailing_nl(child_args_synop) 

626 if child_args_synop != '': 

627 args_synop += make_refsect1_synopsis3(child_args_synop, 

628 'Child Properties', section_id, 'child-properties') 

629 args_desc += make_refsect1_desc(child_args_desc, 'Child Property Details', 

630 section_id, 'child-property-details') 

631 

632 style_args_synop = trim_leading_and_trailing_nl(style_args_synop) 

633 if style_args_synop != '': 

634 args_synop += make_refsect1_synopsis3(style_args_synop, 

635 'Style Properties', section_id, 'style-properties') 

636 args_desc += make_refsect1_desc(style_args_desc, 'Style Property Details', 

637 section_id, 'style-property-details') 

638 

639 actions_synop = re.sub(r'^\n*', '', actions_synop) 

640 actions_synop = re.sub(r'\n+$', '\n', actions_synop) 

641 if actions_synop != '': 

642 actions_synop = '''<refsect1 id="%s.actions" role="actions"> 

643<title role="actions.title">Actions</title> 

644<informaltable frame="none"> 

645<tgroup cols="3"> 

646<colspec colname="actions_none" colwidth="150px"/> 

647<colspec colname="actions_name" colwidth="300px"/> 

648<colspec colname="actions_param" colwidth="200px"/> 

649<tbody> 

650%s 

651</tbody> 

652</tgroup> 

653</informaltable> 

654</refsect1> 

655''' % (section_id, actions_synop) 

656 actions_desc = trim_leading_and_trailing_nl(actions_desc) 

657 actions_desc = '''<refsect1 id="%s.action-details" role="action_details"> 

658<title role="action_details.title">Action Details</title> 

659%s 

660</refsect1> 

661''' % (section_id, actions_desc) 

662 

663 hierarchy_str = AddTreeLineArt(hierarchy) 

664 if hierarchy_str != '': 

665 hierarchy_str = make_refsect1_desc('<screen>' + hierarchy_str + '\n</screen>', 

666 'Object Hierarchy', section_id, 'object-hierarchy') 

667 

668 interfaces = make_refsect1_desc(interfaces, 'Implemented Interfaces', section_id, 

669 'implemented-interfaces', 'impl_interfaces') 

670 implementations = make_refsect1_desc( 

671 implementations, 'Known Implementations', section_id, 'implementations') 

672 prerequisites = make_refsect1_desc(prerequisites, 'Prerequisites', section_id, 'prerequisites') 

673 derived = make_refsect1_desc(derived, 'Known Derived Interfaces', section_id, 'derived-interfaces') 

674 

675 functions_synop = trim_leading_and_trailing_nl(functions_synop) 

676 if functions_synop != '': 

677 functions_synop = make_refsect1_synopsis2( 

678 functions_synop, 'Functions', section_id, 'functions', 'functions_proto') 

679 

680 other_synop = trim_leading_and_trailing_nl(other_synop) 

681 if other_synop != '': 

682 other_synop = make_refsect1_synopsis2( 

683 other_synop, 'Types and Values', section_id, 'other', 'other_proto') 

684 other_desc += make_refsect1_desc(other_details, 'Types and Values', 

685 section_id, 'other_details', 'details') 

686 

687 file_changed = OutputDBFile(filename, title, section_id, 

688 section_includes, 

689 functions_synop, other_synop, 

690 functions_details, other_desc, 

691 signals_synop, signals_desc, 

692 args_synop, args_desc, 

693 actions_synop, actions_desc, 

694 hierarchy_str, interfaces, 

695 implementations, 

696 prerequisites, derived, 

697 file_objects, 

698 options.default_stability) 

699 if file_changed: 

700 changed = True 

701 

702 title = '' 

703 section_id = '' 

704 subsection = '' 

705 in_section = 0 

706 section_includes = '' 

707 functions_synop = '' 

708 other_synop = '' 

709 functions_details = '' 

710 other_details = '' 

711 other_desc = '' 

712 signals_synop = '' 

713 signals_desc = '' 

714 args_synop = '' 

715 child_args_synop = '' 

716 style_args_synop = '' 

717 args_desc = '' 

718 child_args_desc = '' 

719 style_args_desc = '' 

720 actions_synop = '' 

721 actions_desc = '' 

722 hierarchy_str = '' 

723 hierarchy = [] 

724 interfaces = '' 

725 implementations = '' 

726 prerequisites = '' 

727 derived = '' 

728 

729 elif m5: 

730 symbol = m5.group(1) 

731 logging.info(' Symbol: "%s" in subsection: "%s"', symbol, subsection) 

732 

733 # check for duplicate entries 

734 if symbol not in symbol_def_line: 

735 declaration = Declarations.get(symbol) 

736 # FIXME: with this we'll output empty declaration 

737 if declaration is not None: 

738 if CheckIsObject(symbol): 

739 file_objects.append(symbol) 

740 

741 # We don't want standard macros/functions of GObjects, 

742 # or private declarations. 

743 if subsection != "Standard" and subsection != "Private": 

744 synop, desc = OutputDeclaration(symbol, declaration) 

745 type = DeclarationTypes[symbol] 

746 

747 if type == 'FUNCTION' or type == 'USER_FUNCTION': 

748 functions_synop += synop 

749 functions_details += desc 

750 elif type == 'MACRO' and re.search(symbol + r'\(', declaration): 

751 functions_synop += synop 

752 functions_details += desc 

753 else: 

754 other_synop += synop 

755 other_details += desc 

756 

757 sig_synop, sig_desc = GetSignals(symbol) 

758 arg_synop, child_arg_synop, style_arg_synop, arg_desc, child_arg_desc, style_arg_desc = GetArgs( 

759 symbol) 

760 action_synop, action_desc = GetActions(symbol) 

761 ifaces = GetInterfaces(symbol) 

762 impls = GetImplementations(symbol) 

763 prereqs = GetPrerequisites(symbol) 

764 der = GetDerived(symbol) 

765 hierarchy = GetHierarchy(symbol, hierarchy) 

766 

767 signals_synop += sig_synop 

768 signals_desc += sig_desc 

769 args_synop += arg_synop 

770 child_args_synop += child_arg_synop 

771 style_args_synop += style_arg_synop 

772 args_desc += arg_desc 

773 child_args_desc += child_arg_desc 

774 style_args_desc += style_arg_desc 

775 actions_synop += action_synop 

776 actions_desc += action_desc 

777 interfaces += ifaces 

778 implementations += impls 

779 prerequisites += prereqs 

780 derived += der 

781 

782 # Note that the declaration has been output. 

783 DeclarationOutput[symbol] = True 

784 elif subsection != "Standard" and subsection != "Private": 

785 UndeclaredSymbols[symbol] = True 

786 common.LogWarning(file, line_number, "No declaration found for %s." % symbol) 

787 

788 num_symbols += 1 

789 symbol_def_line[symbol] = line_number 

790 

791 if section_id == '': 

792 if title == '' and filename == '': 

793 common.LogWarning(file, line_number, "Section has no title and no file.") 

794 

795 # FIXME: one of those would be enough 

796 # filename should be an internal detail for gtk-doc 

797 if title == '': 

798 title = filename 

799 elif filename == '': 

800 filename = title 

801 

802 filename = filename.replace(' ', '_') 

803 

804 section_id = SourceSymbolDocs.get(filename + ":section_id") 

805 if section_id and section_id.strip() != '': 

806 # Remove trailing blanks and use as is 

807 section_id = section_id.rstrip() 

808 elif CheckIsObject(title): 

809 # GObjects use their class name as the ID. 

810 section_id = common.CreateValidSGMLID(title) 

811 else: 

812 section_id = common.CreateValidSGMLID(MODULE + '-' + title) 

813 

814 SymbolSection[symbol] = title 

815 SymbolSectionId[symbol] = section_id 

816 

817 else: 

818 common.LogWarning(file, line_number, "Double symbol entry for %s. " 

819 "Previous occurrence on line %d." % (symbol, symbol_def_line[symbol])) 

820 INPUT.close() 

821 

822 OutputMissingDocumentation() 

823 OutputUndeclaredSymbols() 

824 OutputUnusedSymbols() 

825 

826 if options.outputallsymbols: 

827 OutputAllSymbols() 

828 

829 if options.outputsymbolswithoutsince: 

830 OutputSymbolsWithoutSince() 

831 

832 for filename in options.expand_content_files.split(): 

833 file_changed = OutputExtraFile(filename) 

834 if file_changed: 

835 changed = True 

836 

837 return (changed, book_top, book_bottom) 

838 

839 

840def DetermineNamespace(symbols): 

841 """Find common set of characters. 

842 

843 Args: 

844 symbols (list): a list of symbols to scan for a common prefix 

845 

846 Returns: 

847 str: a common namespace prefix (might be empty) 

848 """ 

849 name_space = '' 

850 pos = 0 

851 ratio = 0.0 

852 while True: 

853 prefix = {} 

854 letter = '' 

855 for symbol in symbols: 

856 if name_space == '' or name_space.lower() in symbol.lower(): 

857 if len(symbol) > pos: 

858 letter = symbol[pos:pos + 1] 

859 # stop prefix scanning 

860 if letter == "_": 

861 # stop on "_" 

862 break 

863 # Should we also stop on a uppercase char, if last was lowercase 

864 # GtkWidget, if we have the 'W' and had the 't' before 

865 # or should we count upper and lowercase, and stop one 2nd uppercase, if we already had a lowercase 

866 # GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had lowercase chars before 

867 # need to recound each time as this is per symbol 

868 ul = letter.upper() 

869 if ul in prefix: 

870 prefix[ul] += 1 

871 else: 

872 prefix[ul] = 1 

873 

874 if letter != '' and letter != "_": 

875 maxletter = '' 

876 maxsymbols = 0 

877 for letter in prefix.keys(): 

878 logging.debug("ns prefix: %s: %s", letter, prefix[letter]) 

879 if prefix[letter] > maxsymbols: 

880 maxletter = letter 

881 maxsymbols = prefix[letter] 

882 

883 ratio = float(len(symbols)) / prefix[maxletter] 

884 logging.debug('most symbols start with %s, that is %f', maxletter, (100 * ratio)) 

885 if ratio > 0.9: 

886 # do another round 

887 name_space += maxletter 

888 

889 pos += 1 

890 

891 else: 

892 ratio = 0.0 

893 

894 if ratio < 0.9: 

895 break 

896 return name_space 

897 

898 

899def OutputIndex(basename, apiindex): 

900 """Writes an index that can be included into the main-document into an <index> tag. 

901 

902 Args: 

903 basename (str): name of the index file without extension 

904 apiindex (dict): the index data 

905 """ 

906 old_index = os.path.join(DB_OUTPUT_DIR, basename + '.xml') 

907 new_index = os.path.join(DB_OUTPUT_DIR, basename + '.new') 

908 lastletter = " " 

909 divopen = 0 

910 symbol = None 

911 short_symbol = None 

912 

913 OUTPUT = open(new_index, 'w') 

914 

915 OUTPUT.write(MakeDocHeader("indexdiv") + "\n<indexdiv id=\"%s\">\n" % basename) 

916 

917 logging.info("generate %s index (%d entries) with namespace %s", basename, len(apiindex), NAME_SPACE) 

918 

919 # do a case insensitive sort while chopping off the prefix 

920 mapped_keys = [ 

921 { 

922 'original': x, 

923 'short': re.sub(r'^' + NAME_SPACE + r'\_?(.*)', r'\1', x.upper(), flags=re.I), 

924 } for x in apiindex.keys()] 

925 sorted_keys = sorted(mapped_keys, key=lambda d: (d['short'], d['original'])) 

926 

927 for key in sorted_keys: 

928 symbol = key['original'] 

929 short = key['short'] 

930 if short != '': 

931 short_symbol = short 

932 else: 

933 short_symbol = symbol 

934 

935 # generate a short symbol description 

936 symbol_desc = '' 

937 symbol_section = '' 

938 symbol_section_id = '' 

939 symbol_type = '' 

940 if symbol in DeclarationTypes: 

941 symbol_type = DeclarationTypes[symbol].lower() 

942 

943 if symbol_type == '': 

944 logging.info("trying symbol %s", symbol) 

945 m1 = re.search(r'(.*)::(.*)', symbol) 

946 m2 = re.search(r'(.*):(.*)', symbol) 

947 m3 = re.search(r'(.*)\|(.*)', symbol) 

948 if m1: 

949 oname = m1.group(1) 

950 osym = m1.group(2) 

951 logging.info(" trying object signal %s:%s in %d signals", oname, osym, len(SignalNames)) 

952 for name in SignalNames: 

953 logging.info(" " + name) 

954 if name == osym: 

955 symbol_type = "object signal" 

956 if oname in SymbolSection: 

957 symbol_section = SymbolSection[oname] 

958 symbol_section_id = SymbolSectionId[oname] 

959 break 

960 elif m2: 

961 oname = m2.group(1) 

962 osym = m2.group(2) 

963 logging.info(" trying object property %s::%s in %d properties", oname, osym, len(ArgNames)) 

964 for name in ArgNames: 

965 logging.info(" " + name) 

966 if name == osym: 

967 symbol_type = "object property" 

968 if oname in SymbolSection: 

969 symbol_section = SymbolSection[oname] 

970 symbol_section_id = SymbolSectionId[oname] 

971 break 

972 elif m3: 

973 oname = m3.group(1) 

974 osym = m3.group(2) 

975 logging.info(" trying action %s|%s in %d actions", oname, osym, len(ActionNames)) 

976 for name in ActionNames: 

977 logging.info(" " + name) 

978 if name == osym: 

979 symbol_type = "action" 

980 if oname in SymbolSection: 

981 symbol_section = SymbolSection[oname] 

982 symbol_section_id = SymbolSectionId[oname] 

983 break 

984 else: 

985 if symbol in SymbolSection: 

986 symbol_section = SymbolSection[symbol] 

987 symbol_section_id = SymbolSectionId[symbol] 

988 

989 if symbol_type != '': 

990 symbol_desc = ", " + symbol_type 

991 if symbol_section != '': 

992 symbol_desc += " in <link linkend=\"%s\">%s</link>" % (symbol_section_id, symbol_section) 

993 # symbol_desc +=" in " + ExpandAbbreviations(symbol, "#symbol_section") 

994 

995 curletter = short_symbol[0].upper() 

996 ixid = apiindex[symbol] 

997 

998 logging.info(" add symbol %s with %s to index in section '%s' (derived from %s)", 

999 symbol, ixid, curletter, short_symbol) 

1000 

1001 if curletter != lastletter: 

1002 lastletter = curletter 

1003 

1004 if divopen: 

1005 OUTPUT.write("</indexdiv>\n") 

1006 

1007 OUTPUT.write("<indexdiv><title>%s</title>\n" % curletter) 

1008 divopen = True 

1009 

1010 OUTPUT.write('<indexentry><primaryie linkends="%s"><link linkend="%s">%s</link>%s</primaryie></indexentry>\n' % 

1011 (ixid, ixid, symbol, symbol_desc)) 

1012 

1013 if divopen: 

1014 OUTPUT.write("</indexdiv>\n") 

1015 

1016 OUTPUT.write("</indexdiv>\n") 

1017 OUTPUT.close() 

1018 

1019 common.UpdateFileIfChanged(old_index, new_index, 0) 

1020 

1021 

1022def OutputSinceIndexes(): 

1023 """Generate the 'since' api index files.""" 

1024 for version in sorted(set(Since.values())): 

1025 logging.info("Since : [%s]", version) 

1026 index = {x: IndexEntriesSince[x] for x in IndexEntriesSince.keys() if Since[x] == version} 

1027 OutputIndex("api-index-" + version, index) 

1028 

1029 

1030def OutputAnnotationGlossary(): 

1031 """Writes a glossary of the used annotation terms. 

1032 

1033 The glossary file can be included into the main document. 

1034 """ 

1035 # if there are no annotations used return 

1036 if not AnnotationsUsed: 

1037 return 

1038 

1039 old_glossary = os.path.join(DB_OUTPUT_DIR, "annotation-glossary.xml") 

1040 new_glossary = os.path.join(DB_OUTPUT_DIR, "annotation-glossary.new") 

1041 lastletter = " " 

1042 divopen = False 

1043 

1044 # add acronyms that are referenced from acronym text 

1045 rerun = True 

1046 while rerun: 

1047 rerun = False 

1048 for annotation in AnnotationsUsed: 

1049 if annotation not in AnnotationDefinition: 

1050 continue 

1051 m = re.search(r'<acronym>([\w ]+)<\/acronym>', AnnotationDefinition[annotation]) 

1052 if m and m.group(1) not in AnnotationsUsed: 

1053 AnnotationsUsed[m.group(1)] = 1 

1054 rerun = True 

1055 break 

1056 

1057 OUTPUT = open(new_glossary, 'w', encoding='utf-8') 

1058 

1059 OUTPUT.write('''%s 

1060<glossary id="annotation-glossary"> 

1061 <title>Annotation Glossary</title> 

1062''' % MakeDocHeader("glossary")) 

1063 

1064 for annotation in sorted(AnnotationsUsed.keys(), key=str.lower): 

1065 if annotation in AnnotationDefinition: 

1066 definition = AnnotationDefinition[annotation] 

1067 curletter = annotation[0].upper() 

1068 

1069 if curletter != lastletter: 

1070 lastletter = curletter 

1071 

1072 if divopen: 

1073 OUTPUT.write("</glossdiv>\n") 

1074 

1075 OUTPUT.write("<glossdiv><title>%s</title>\n" % curletter) 

1076 divopen = True 

1077 

1078 OUTPUT.write(''' <glossentry> 

1079 <glossterm><anchor id="annotation-glossterm-%s"/>%s</glossterm> 

1080 <glossdef> 

1081 <para>%s</para> 

1082 </glossdef> 

1083 </glossentry> 

1084''' % (annotation, annotation, definition)) 

1085 

1086 if divopen: 

1087 OUTPUT.write("</glossdiv>\n") 

1088 

1089 OUTPUT.write("</glossary>\n") 

1090 OUTPUT.close() 

1091 

1092 common.UpdateFileIfChanged(old_glossary, new_glossary, 0) 

1093 

1094 

1095def OutputObjectTree(tree): 

1096 if not tree: 

1097 return 

1098 

1099 # FIXME: use xml 

1100 old_tree_index = os.path.join(DB_OUTPUT_DIR, "tree_index.sgml") 

1101 new_tree_index = os.path.join(DB_OUTPUT_DIR, "tree_index.new") 

1102 

1103 with open(new_tree_index, 'w', encoding='utf-8') as out: 

1104 out.write(MakeDocHeader("screen")) 

1105 out.write("\n<screen>\n") 

1106 out.write(AddTreeLineArt(tree)) 

1107 out.write("\n</screen>\n") 

1108 

1109 common.UpdateFileIfChanged(old_tree_index, new_tree_index, 0) 

1110 

1111 

1112def ReadKnownSymbols(file): 

1113 """Collect the names of non-private symbols from the $MODULE-sections.txt file. 

1114 

1115 Args: 

1116 file: the $MODULE-sections.txt file 

1117 """ 

1118 

1119 subsection = '' 

1120 

1121 logging.info("Reading: %s", file) 

1122 INPUT = open(file, 'r', encoding='utf-8') 

1123 for line in INPUT: 

1124 if line.startswith('#'): 

1125 continue 

1126 

1127 if line.startswith('<SECTION>'): 

1128 subsection = '' 

1129 continue 

1130 

1131 m = re.search(r'^<SUBSECTION\s*(.*)>', line, flags=re.I) 

1132 if m: 

1133 subsection = m.group(1) 

1134 continue 

1135 

1136 if line.startswith('<SUBSECTION>'): 

1137 continue 

1138 

1139 if re.search(r'^<TITLE>(.*)<\/TITLE>', line): 

1140 continue 

1141 

1142 m = re.search(r'^<FILE>(.*)<\/FILE>', line) 

1143 if m: 

1144 KnownSymbols[m.group(1) + ":long_description"] = 1 

1145 KnownSymbols[m.group(1) + ":short_description"] = 1 

1146 continue 

1147 

1148 m = re.search(r'^<INCLUDE>(.*)<\/INCLUDE>', line) 

1149 if m: 

1150 continue 

1151 

1152 m = re.search(r'^<\/SECTION>', line) 

1153 if m: 

1154 continue 

1155 

1156 m = re.search(r'^(\S+)', line) 

1157 if m: 

1158 symbol = m.group(1) 

1159 if subsection != "Standard" and subsection != "Private": 

1160 KnownSymbols[symbol] = 1 

1161 else: 

1162 KnownSymbols[symbol] = 0 

1163 INPUT.close() 

1164 

1165 

1166def OutputDeclaration(symbol, declaration): 

1167 """Returns the formatted documentation block for a symbol. 

1168 

1169 Args: 

1170 symbol (str): the name of the function/macro/... 

1171 declaration (str): the declaration of the function/macro. 

1172 

1173 Returns: 

1174 str: the formatted documentation 

1175 """ 

1176 

1177 dtype = DeclarationTypes[symbol] 

1178 logging.info('Output Symbol: "%s" "%s"', symbol, dtype) 

1179 if dtype == 'MACRO': 

1180 return OutputMacro(symbol, declaration) 

1181 elif dtype == 'TYPEDEF': 

1182 return OutputTypedef(symbol, declaration) 

1183 elif dtype == 'STRUCT': 

1184 return OutputStruct(symbol, declaration) 

1185 elif dtype == 'ENUM': 

1186 return OutputEnum(symbol, declaration) 

1187 elif dtype == 'UNION': 

1188 return OutputUnion(symbol, declaration) 

1189 elif dtype == 'VARIABLE': 

1190 return OutputVariable(symbol, declaration) 

1191 elif dtype == 'FUNCTION': 

1192 return OutputFunction(symbol, declaration, dtype) 

1193 elif dtype == 'USER_FUNCTION': 

1194 return OutputFunction(symbol, declaration, dtype) 

1195 else: 

1196 logging.warning("Unknown symbol type %s for symbol %s", dtype, symbol) 

1197 return ('', '') 

1198 

1199 

1200def OutputSymbolTraits(symbol): 

1201 """Returns the Since and StabilityLevel paragraphs for a symbol. 

1202 

1203 Args: 

1204 symbol (str): the name to describe 

1205 

1206 Returns: 

1207 str: paragraph or empty string 

1208 """ 

1209 

1210 desc = '' 

1211 

1212 if symbol in Since: 

1213 link_id = "api-index-" + Since[symbol] 

1214 desc += "<para role=\"since\">Since: <link linkend=\"%s\">%s</link></para>" % (link_id, Since[symbol]) 

1215 

1216 if symbol in StabilityLevel: 

1217 stability = StabilityLevel[symbol] 

1218 if stability in AnnotationDefinition: 

1219 AnnotationsUsed[stability] = True 

1220 stability = "<acronym>%s</acronym>" % stability 

1221 desc += "<para role=\"stability\">Stability Level: %s</para>" % stability 

1222 return desc 

1223 

1224 

1225def uri_escape(text): 

1226 if text is None: 

1227 return None 

1228 

1229 # Build a char to hex map 

1230 escapes = {chr(i): ("%%%02X" % i) for i in range(256)} 

1231 

1232 # Default unsafe characters. RFC 2732 ^(uric - reserved) 

1233 def do_escape(char): 

1234 return escapes[char] 

1235 return re.sub(r"([^A-Za-z0-9\-_.!~*'()]", do_escape, text) 

1236 

1237 

1238def extract_struct_body(symbol, decl, has_typedef, public): 

1239 """Returns a normalized struct body. 

1240 

1241 Normalizes whitespace and newlines and supresses non public members. 

1242 

1243 Returns: 

1244 str: the nomalized struct declaration 

1245 """ 

1246 decl_out = '' 

1247 

1248 m = re.search( 

1249 r'^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$', decl, flags=re.DOTALL) 

1250 if m: 

1251 new_boby = '' 

1252 for line in m.group(2).splitlines(): 

1253 logging.info("Struct line: %s", line) 

1254 m2 = re.search(r'/\*\s*<\s*public\s*>\s*\*/', line) 

1255 m3 = re.search(r'/\*\s*<\s*(private|protected)\s*>\s*\*/', line) 

1256 if m2: 

1257 public = True 

1258 elif m3: 

1259 public = False 

1260 elif public: 

1261 new_boby += line + "\n" 

1262 

1263 if new_boby: 

1264 # Strip any blank lines off the ends. 

1265 new_boby = re.sub(r'^\s*\n', '', new_boby) 

1266 new_boby = re.sub(r'\n\s*$', r'\n', new_boby) 

1267 

1268 if has_typedef: 

1269 decl_out = "typedef struct {\n%s} %s;\n" % (new_boby, symbol) 

1270 else: 

1271 decl_out = "struct %s {\n%s};\n" % (symbol, new_boby) 

1272 else: 

1273 common.LogWarning(*GetSymbolSourceLocation(symbol), 

1274 "Couldn't parse struct:\n%s" % decl) 

1275 

1276 if decl_out == '': 

1277 # If we couldn't parse the struct or it was all private, output an 

1278 # empty struct declaration. 

1279 if has_typedef: 

1280 decl_out = "typedef struct _%s %s;" % (symbol, symbol) 

1281 else: 

1282 decl_out = "struct %s;" % symbol 

1283 

1284 return decl_out 

1285 

1286 

1287def OutputSymbolExtraLinks(symbol): 

1288 """Returns extralinks for the symbol (if enabled). 

1289 

1290 Args: 

1291 symbol (str): the name to describe 

1292 

1293 Returns: 

1294 str: paragraph or empty string 

1295 """ 

1296 desc = '' 

1297 

1298 if False: # NEW FEATURE: needs configurability 

1299 sstr = uri_escape(symbol) 

1300 mstr = uri_escape(MODULE) 

1301 desc += '''<ulink role="extralinks" url="http://www.google.com/codesearch?q=%s">code search</ulink> 

1302<ulink role="extralinks" url="http://library.gnome.org/edit?module=%s&amp;symbol=%s">edit documentation</ulink> 

1303''' % (sstr, mstr, sstr) 

1304 

1305 return desc 

1306 

1307 

1308def OutputSectionExtraLinks(symbol, docsymbol): 

1309 desc = '' 

1310 

1311 if False: # NEW FEATURE: needs configurability 

1312 sstr = uri_escape(symbol) 

1313 mstr = uri_escape(MODULE) 

1314 dsstr = uri_escape(docsymbol) 

1315 desc += '''<ulink role="extralinks" url="http://www.google.com/codesearch?q=%s">code search</ulink> 

1316<ulink role="extralinks" url="http://library.gnome.org/edit?module=%s&amp;symbol=%s">edit documentation</ulink> 

1317''' % (sstr, mstr, dsstr) 

1318 return desc 

1319 

1320 

1321def OutputMacro(symbol, declaration): 

1322 """Returns the synopsis and detailed description of a macro. 

1323 

1324 Args: 

1325 symbol (str): the macro name. 

1326 declaration (str): the declaration of the macro. 

1327 

1328 Returns: 

1329 str: the formatted docs 

1330 """ 

1331 sid = common.CreateValidSGMLID(symbol) 

1332 condition = MakeConditionDescription(symbol) 

1333 synop = "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link>" % ( 

1334 sid, symbol) 

1335 

1336 fields = common.ParseMacroDeclaration(declaration, CreateValidSGML) 

1337 title = symbol 

1338 if len(fields) > 0: 

1339 title += '()' 

1340 

1341 desc = '<refsect2 id="%s" role="macro"%s>\n<title>%s</title>\n' % (sid, condition, title) 

1342 desc += MakeIndexterms(symbol, sid) 

1343 desc += "\n" 

1344 desc += OutputSymbolExtraLinks(symbol) 

1345 

1346 if len(fields) > 0: 

1347 synop += "<phrase role=\"c_punctuation\">()</phrase>" 

1348 

1349 synop += "</entry></row>\n" 

1350 

1351 # Don't output the macro definition if is is a conditional macro or it 

1352 # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is 

1353 # longer than 2 lines, otherwise we get lots of complicated macros like 

1354 # g_assert. 

1355 if symbol not in DeclarationConditional and not symbol.startswith('g_') \ 

1356 and not re.search(r'^_?gnome_', symbol) and declaration.count('\n') < 2: 

1357 decl_out = CreateValidSGML(declaration) 

1358 desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out 

1359 else: 

1360 desc += "<programlisting language=\"C\">" + "#define".ljust(RETURN_TYPE_FIELD_WIDTH) + symbol 

1361 m = re.search(r'^\s*#\s*define\s+\w+(\([^\)]*\))', declaration) 

1362 if m: 

1363 args = m.group(1) 

1364 pad = ' ' * (RETURN_TYPE_FIELD_WIDTH - len("#define ")) 

1365 # Align each line so that if should all line up OK. 

1366 args = args.replace('\n', '\n' + pad) 

1367 desc += CreateValidSGML(args) 

1368 

1369 desc += "</programlisting>\n" 

1370 

1371 desc += MakeDeprecationNote(symbol) 

1372 

1373 parameters = OutputParamDescriptions("MACRO", symbol, fields) 

1374 

1375 if symbol in SymbolDocs: 

1376 symbol_docs = ConvertMarkDown(symbol, SymbolDocs[symbol]) 

1377 desc += symbol_docs 

1378 

1379 desc += parameters 

1380 desc += OutputSymbolTraits(symbol) 

1381 desc += "</refsect2>\n" 

1382 return (synop, desc) 

1383 

1384 

1385def OutputTypedef(symbol, declaration): 

1386 """Returns the synopsis and detailed description of a typedef. 

1387 

1388 Args: 

1389 symbol (str): the typedef. 

1390 declaration (str): the declaration of the typedef, 

1391 e.g. 'typedef unsigned int guint;' 

1392 

1393 Returns: 

1394 str: the formatted docs 

1395 """ 

1396 sid = common.CreateValidSGMLID(symbol) 

1397 condition = MakeConditionDescription(symbol) 

1398 desc = "<refsect2 id=\"%s\" role=\"typedef\"%s>\n<title>%s</title>\n" % (sid, condition, symbol) 

1399 synop = "<row><entry role=\"typedef_keyword\">typedef</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % ( 

1400 sid, symbol) 

1401 

1402 desc += MakeIndexterms(symbol, sid) 

1403 desc += "\n" 

1404 desc += OutputSymbolExtraLinks(symbol) 

1405 

1406 if symbol not in DeclarationConditional: 

1407 decl_out = CreateValidSGML(declaration) 

1408 desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out 

1409 

1410 desc += MakeDeprecationNote(symbol) 

1411 

1412 if symbol in SymbolDocs: 

1413 desc += ConvertMarkDown(symbol, SymbolDocs[symbol]) 

1414 

1415 desc += OutputSymbolTraits(symbol) 

1416 desc += "</refsect2>\n" 

1417 return (synop, desc) 

1418 

1419 

1420def OutputStruct(symbol, declaration): 

1421 """Returns the synopsis and detailed description of a struct. 

1422 

1423 We check if it is a object struct, and if so we only output parts of it that 

1424 are noted as public fields. We also use a different IDs for object structs, 

1425 since the original ID is used for the entire RefEntry. 

1426 

1427 Args: 

1428 symbol (str): the struct. 

1429 declaration (str): the declaration of the struct. 

1430 

1431 Returns: 

1432 str: the formatted docs 

1433 """ 

1434 is_gtype = False 

1435 default_to_public = True 

1436 if CheckIsObject(symbol): 

1437 logging.info("Found struct gtype: %s", symbol) 

1438 is_gtype = True 

1439 default_to_public = ObjectRoots[symbol] == 'GBoxed' 

1440 

1441 sid = None 

1442 condition = None 

1443 if is_gtype: 

1444 sid = common.CreateValidSGMLID(symbol + "_struct") 

1445 condition = MakeConditionDescription(symbol + "_struct") 

1446 else: 

1447 sid = common.CreateValidSGMLID(symbol) 

1448 condition = MakeConditionDescription(symbol) 

1449 

1450 # Determine if it is a simple struct or it also has a typedef. 

1451 has_typedef = False 

1452 if symbol in StructHasTypedef or re.search(r'^\s*typedef\s+', declaration): 

1453 has_typedef = True 

1454 

1455 type_output = None 

1456 desc = None 

1457 if has_typedef: 

1458 # For structs with typedefs we just output the struct name. 

1459 type_output = '' 

1460 desc = "<refsect2 id=\"%s\" role=\"struct\"%s>\n<title>%s</title>\n" % (sid, condition, symbol) 

1461 else: 

1462 type_output = "struct" 

1463 desc = "<refsect2 id=\"%s\" role=\"struct\"%s>\n<title>struct %s</title>\n" % (sid, condition, symbol) 

1464 

1465 synop = "<row><entry role=\"datatype_keyword\">%s</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % ( 

1466 type_output, sid, symbol) 

1467 

1468 desc += MakeIndexterms(symbol, sid) 

1469 desc += "\n" 

1470 desc += OutputSymbolExtraLinks(symbol) 

1471 

1472 # Form a pretty-printed, private-data-removed form of the declaration 

1473 

1474 decl_out = '' 

1475 if re.search(r'^\s*$', declaration): 

1476 logging.info("Found opaque struct: %s", symbol) 

1477 decl_out = "typedef struct _%s %s;" % (symbol, symbol) 

1478 elif re.search(r'^\s*struct\s+\w+\s*;\s*$', declaration): 

1479 logging.info("Found opaque struct: %s", symbol) 

1480 decl_out = "struct %s;" % symbol 

1481 else: 

1482 decl_out = extract_struct_body(symbol, declaration, has_typedef, default_to_public) 

1483 

1484 decl_out = CreateValidSGML(decl_out) 

1485 desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out 

1486 

1487 desc += MakeDeprecationNote(symbol) 

1488 

1489 if symbol in SymbolDocs: 

1490 desc += ConvertMarkDown(symbol, SymbolDocs[symbol]) 

1491 

1492 # Create a table of fields and descriptions 

1493 

1494 # FIXME: Inserting &#160's into the produced type declarations here would 

1495 # improve the output in most situations ... except for function 

1496 # members of structs! 

1497 def pfunc(*args): 

1498 return '<structfield id="%s">%s</structfield>' % (common.CreateValidSGMLID(sid + '.' + args[0]), args[0]) 

1499 fields = common.ParseStructDeclaration(declaration, not default_to_public, 0, MakeXRef, pfunc) 

1500 field_descrs, found = GetSymbolParams(symbol) 

1501 

1502 if found: 

1503 missing_parameters = '' 

1504 unused_parameters = '' 

1505 sid = common.CreateValidSGMLID(symbol + ".members") 

1506 

1507 desc += '''<refsect3 id="%s" role="struct_members">\n<title>Members</title> 

1508<informaltable role="struct_members_table" pgwide="1" frame="none"> 

1509<tgroup cols="3"> 

1510<colspec colname="struct_members_name" colwidth="300px"/> 

1511<colspec colname="struct_members_description"/> 

1512<colspec colname="struct_members_annotations" colwidth="200px"/> 

1513<tbody> 

1514''' % sid 

1515 

1516 for field_name, text in fields.items(): 

1517 param_annotations = '' 

1518 

1519 desc += "<row role=\"member\"><entry role=\"struct_member_name\"><para>%s</para></entry>\n" % text 

1520 if field_name in field_descrs: 

1521 field_descr, param_annotations = ExpandAnnotation(symbol, field_descrs[field_name]) 

1522 field_descr = ConvertMarkDown(symbol, field_descr) 

1523 # trim 

1524 field_descr = re.sub(r'^(\s|\n)+', '', field_descr, flags=re.M | re.S) 

1525 field_descr = re.sub(r'(\s|\n)+$', '', field_descr, flags=re.M | re.S) 

1526 desc += "<entry role=\"struct_member_description\">%s</entry>\n<entry role=\"struct_member_annotations\">%s</entry>\n" % ( 

1527 field_descr, param_annotations) 

1528 del field_descrs[field_name] 

1529 else: 

1530 common.LogWarning(*GetSymbolSourceLocation(symbol), 

1531 "Field description for %s::%s is missing in source code comment block." % (symbol, field_name)) 

1532 if missing_parameters != '': 

1533 missing_parameters += ", " + field_name 

1534 else: 

1535 missing_parameters = field_name 

1536 

1537 desc += "<entry /><entry />\n" 

1538 

1539 desc += "</row>\n" 

1540 

1541 desc += "</tbody></tgroup></informaltable>\n</refsect3>\n" 

1542 for field_name in field_descrs: 

1543 # Documenting those standard fields is not required anymore, but 

1544 # we don't want to warn if they are documented anyway. 

1545 m = re.search(r'(g_iface|parent_instance|parent_class)', field_name) 

1546 if m: 

1547 continue 

1548 

1549 common.LogWarning(*GetSymbolSourceLocation(symbol), 

1550 "Field description for %s::%s is not used from source code comment block." % (symbol, field_name)) 

1551 if unused_parameters != '': 

1552 unused_parameters += ", " + field_name 

1553 else: 

1554 unused_parameters = field_name 

1555 

1556 # remember missing/unused parameters (needed in tmpl-free build) 

1557 if missing_parameters != '' and (symbol not in AllIncompleteSymbols): 

1558 AllIncompleteSymbols[symbol] = missing_parameters 

1559 

1560 if unused_parameters != '' and (symbol not in AllUnusedSymbols): 

1561 AllUnusedSymbols[symbol] = unused_parameters 

1562 else: 

1563 if fields: 

1564 if symbol not in AllIncompleteSymbols: 

1565 AllIncompleteSymbols[symbol] = "<items>" 

1566 common.LogWarning(*GetSymbolSourceLocation(symbol), 

1567 "Field descriptions for struct %s are missing in source code comment block." % symbol) 

1568 logging.info("Remaining structs fields: " + ':'.join(fields) + "\n") 

1569 

1570 desc += OutputSymbolTraits(symbol) 

1571 desc += "</refsect2>\n" 

1572 return (synop, desc) 

1573 

1574 

1575def OutputUnion(symbol, declaration): 

1576 """Returns the synopsis and detailed description of a union. 

1577 

1578 Args: 

1579 symbol (str): the union. 

1580 declaration (str): the declaration of the union. 

1581 

1582 Returns: 

1583 str: the formatted docs 

1584 """ 

1585 is_gtype = False 

1586 if CheckIsObject(symbol): 

1587 logging.info("Found union gtype: %s", symbol) 

1588 is_gtype = True 

1589 

1590 sid = None 

1591 condition = None 

1592 if is_gtype: 

1593 sid = common.CreateValidSGMLID(symbol + "_union") 

1594 condition = MakeConditionDescription(symbol + "_union") 

1595 else: 

1596 sid = common.CreateValidSGMLID(symbol) 

1597 condition = MakeConditionDescription(symbol) 

1598 

1599 # Determine if it is a simple struct or it also has a typedef. 

1600 has_typedef = False 

1601 if symbol in StructHasTypedef or re.search(r'^\s*typedef\s+', declaration): 

1602 has_typedef = True 

1603 

1604 type_output = None 

1605 desc = None 

1606 if has_typedef: 

1607 # For unions with typedefs we just output the union name. 

1608 type_output = '' 

1609 desc = "<refsect2 id=\"%s\" role=\"union\"%s>\n<title>%s</title>\n" % (sid, condition, symbol) 

1610 else: 

1611 type_output = "union" 

1612 desc = "<refsect2 id=\"%s\" role=\"union\"%s>\n<title>union %s</title>\n" % (sid, condition, symbol) 

1613 

1614 synop = "<row><entry role=\"datatype_keyword\">%s</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % ( 

1615 type_output, sid, symbol) 

1616 

1617 desc += MakeIndexterms(symbol, sid) 

1618 desc += "\n" 

1619 desc += OutputSymbolExtraLinks(symbol) 

1620 desc += MakeDeprecationNote(symbol) 

1621 

1622 if symbol in SymbolDocs: 

1623 desc += ConvertMarkDown(symbol, SymbolDocs[symbol]) 

1624 

1625 # Create a table of fields and descriptions 

1626 

1627 # FIXME: Inserting &#160's into the produced type declarations here would 

1628 # improve the output in most situations ... except for function 

1629 # members of structs! 

1630 def pfunc(*args): 

1631 return '<structfield id="%s">%s</structfield>' % (common.CreateValidSGMLID(sid + '.' + args[0]), args[0]) 

1632 fields = common.ParseStructDeclaration(declaration, 0, 0, MakeXRef, pfunc) 

1633 field_descrs, found = GetSymbolParams(symbol) 

1634 

1635 logging.debug('Union %s has %d entries, found=%d, has_typedef=%d', symbol, len(fields), found, has_typedef) 

1636 

1637 if found: 

1638 missing_parameters = '' 

1639 unused_parameters = '' 

1640 sid = common.CreateValidSGMLID('%s.members' % symbol) 

1641 

1642 desc += '''<refsect3 id="%s" role="union_members">\n<title>Members</title> 

1643<informaltable role="union_members_table" pgwide="1" frame="none"> 

1644<tgroup cols="3"> 

1645<colspec colname="union_members_name" colwidth="300px"/> 

1646<colspec colname="union_members_description"/> 

1647<colspec colname="union_members_annotations" colwidth="200px"/> 

1648<tbody> 

1649''' % sid 

1650 

1651 for field_name, text in fields.items(): 

1652 param_annotations = '' 

1653 

1654 desc += "<row><entry role=\"union_member_name\"><para>%s</para></entry>\n" % text 

1655 if field_name in field_descrs: 

1656 field_descr, param_annotations = ExpandAnnotation(symbol, field_descrs[field_name]) 

1657 field_descr = ConvertMarkDown(symbol, field_descr) 

1658 

1659 # trim 

1660 field_descr = re.sub(r'^(\s|\n)+', '', field_descr, flags=re.M | re.S) 

1661 field_descr = re.sub(r'(\s|\n)+$', '', field_descr, flags=re.M | re.S) 

1662 desc += "<entry role=\"union_member_description\">%s</entry>\n<entry role=\"union_member_annotations\">%s</entry>\n" % ( 

1663 field_descr, param_annotations) 

1664 del field_descrs[field_name] 

1665 else: 

1666 common.LogWarning(*GetSymbolSourceLocation(symbol), 

1667 "Field description for %s::%s is missing in source code comment block." % (symbol, field_name)) 

1668 if missing_parameters != '': 

1669 missing_parameters += ", " + field_name 

1670 else: 

1671 missing_parameters = field_name 

1672 

1673 desc += "<entry /><entry />\n" 

1674 

1675 desc += "</row>\n" 

1676 

1677 desc += "</tbody></tgroup></informaltable>\n</refsect3>" 

1678 for field_name in field_descrs: 

1679 common.LogWarning(*GetSymbolSourceLocation(symbol), 

1680 "Field description for %s::%s is not used from source code comment block." % (symbol, field_name)) 

1681 if unused_parameters != '': 

1682 unused_parameters += ", " + field_name 

1683 else: 

1684 unused_parameters = field_name 

1685 

1686 # remember missing/unused parameters (needed in tmpl-free build) 

1687 if missing_parameters != '' and (symbol not in AllIncompleteSymbols): 

1688 AllIncompleteSymbols[symbol] = missing_parameters 

1689 

1690 if unused_parameters != '' and (symbol not in AllUnusedSymbols): 

1691 AllUnusedSymbols[symbol] = unused_parameters 

1692 else: 

1693 if len(fields) > 0: 

1694 if symbol not in AllIncompleteSymbols: 

1695 AllIncompleteSymbols[symbol] = "<items>" 

1696 common.LogWarning(*GetSymbolSourceLocation(symbol), 

1697 "Field descriptions for union %s are missing in source code comment block." % symbol) 

1698 logging.info("Remaining union fields: " + ':'.join(fields) + "\n") 

1699 

1700 desc += OutputSymbolTraits(symbol) 

1701 desc += "</refsect2>\n" 

1702 return (synop, desc) 

1703 

1704 

1705def OutputEnum(symbol, declaration): 

1706 """Returns the synopsis and detailed description of a enum. 

1707 

1708 Args: 

1709 symbol (str): the enum. 

1710 declaration (str): the declaration of the enum. 

1711 

1712 Returns: 

1713 str: the formatted docs 

1714 """ 

1715 is_gtype = False 

1716 if CheckIsObject(symbol): 

1717 logging.info("Found enum gtype: %s", symbol) 

1718 is_gtype = True 

1719 

1720 sid = None 

1721 condition = None 

1722 if is_gtype: 

1723 sid = common.CreateValidSGMLID(symbol + "_enum") 

1724 condition = MakeConditionDescription(symbol + "_enum") 

1725 else: 

1726 sid = common.CreateValidSGMLID(symbol) 

1727 condition = MakeConditionDescription(symbol) 

1728 

1729 synop = "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % ( 

1730 sid, symbol) 

1731 desc = "<refsect2 id=\"%s\" role=\"enum\"%s>\n<title>enum %s</title>\n" % (sid, condition, symbol) 

1732 

1733 desc += MakeIndexterms(symbol, sid) 

1734 desc += "\n" 

1735 desc += OutputSymbolExtraLinks(symbol) 

1736 desc += MakeDeprecationNote(symbol) 

1737 

1738 if symbol in SymbolDocs: 

1739 desc += ConvertMarkDown(symbol, SymbolDocs[symbol]) 

1740 

1741 # Create a table of fields and descriptions 

1742 

1743 fields = common.ParseEnumDeclaration(declaration) 

1744 field_descrs, found = GetSymbolParams(symbol) 

1745 

1746 missing_parameters = '' 

1747 unused_parameters = '' 

1748 

1749 sid = common.CreateValidSGMLID("%s.members" % symbol) 

1750 desc += '''<refsect3 id="%s" role="enum_members">\n<title>Members</title> 

1751<informaltable role="enum_members_table" pgwide="1" frame="none"> 

1752<tgroup cols="3"> 

1753<colspec colname="enum_members_name" colwidth="300px"/> 

1754<colspec colname="enum_members_description"/> 

1755<colspec colname="enum_members_annotations" colwidth="200px"/> 

1756<tbody> 

1757''' % sid 

1758 

1759 for field_name in fields: 

1760 field_descr = field_descrs.get(field_name) 

1761 param_annotations = '' 

1762 

1763 sid = common.CreateValidSGMLID(field_name) 

1764 condition = MakeConditionDescription(field_name) 

1765 desc += "<row role=\"constant\"><entry role=\"enum_member_name\"><para id=\"%s\">%s</para></entry>\n" % ( 

1766 sid, field_name) 

1767 if field_descr: 

1768 field_descr, param_annotations = ExpandAnnotation(symbol, field_descr) 

1769 field_descr = ConvertMarkDown(symbol, field_descr) 

1770 desc += "<entry role=\"enum_member_description\">%s</entry>\n<entry role=\"enum_member_annotations\">%s</entry>\n" % ( 

1771 field_descr, param_annotations) 

1772 del field_descrs[field_name] 

1773 else: 

1774 if found: 

1775 common.LogWarning(*GetSymbolSourceLocation(symbol), 

1776 "Value description for %s::%s is missing in source code comment block." % (symbol, field_name)) 

1777 if missing_parameters != '': 

1778 missing_parameters += ", " + field_name 

1779 else: 

1780 missing_parameters = field_name 

1781 desc += "<entry /><entry />\n" 

1782 desc += "</row>\n" 

1783 

1784 desc += "</tbody></tgroup></informaltable>\n</refsect3>" 

1785 for field_name in field_descrs: 

1786 common.LogWarning(*GetSymbolSourceLocation(symbol), 

1787 "Value description for %s::%s is not used from source code comment block." % (symbol, field_name)) 

1788 if unused_parameters != '': 

1789 unused_parameters += ", " + field_name 

1790 else: 

1791 unused_parameters = field_name 

1792 

1793 # remember missing/unused parameters (needed in tmpl-free build) 

1794 if missing_parameters != '' and (symbol not in AllIncompleteSymbols): 

1795 AllIncompleteSymbols[symbol] = missing_parameters 

1796 

1797 if unused_parameters != '' and (symbol not in AllUnusedSymbols): 

1798 AllUnusedSymbols[symbol] = unused_parameters 

1799 

1800 if not found: 

1801 if len(fields) > 0: 

1802 if symbol not in AllIncompleteSymbols: 

1803 AllIncompleteSymbols[symbol] = "<items>" 

1804 common.LogWarning(*GetSymbolSourceLocation(symbol), 

1805 "Value descriptions for %s are missing in source code comment block." % symbol) 

1806 

1807 desc += OutputSymbolTraits(symbol) 

1808 desc += "</refsect2>\n" 

1809 return (synop, desc) 

1810 

1811 

1812def OutputVariable(symbol, declaration): 

1813 """Returns the synopsis and detailed description of a variable. 

1814 

1815 Args: 

1816 symbol (str): the extern'ed variable. 

1817 declaration (str): the declaration of the variable. 

1818 

1819 Returns: 

1820 str: the formatted docs 

1821 """ 

1822 sid = common.CreateValidSGMLID(symbol) 

1823 condition = MakeConditionDescription(symbol) 

1824 

1825 logging.info("ouputing variable: '%s' '%s'", symbol, declaration) 

1826 

1827 type_output = None 

1828 m1 = re.search( 

1829 r'^\s*extern\s+((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)(\s*)(const\s+)*([A-Za-z]\w*)\s*;', declaration) 

1830 m2 = re.search( 

1831 r'\s*((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)(\s*)(const\s+)*([A-Za-z]\w*)\s*=', declaration) 

1832 if m1: 

1833 mod1 = m1.group(1) or '' 

1834 ptr = m1.group(3) or '' 

1835 space = m1.group(4) or '' 

1836 mod2 = m1.group(5) or '' 

1837 type_output = "extern %s%s%s%s" % (mod1, ptr, space, mod2) 

1838 elif m2: 

1839 mod1 = m2.group(1) or '' 

1840 ptr = m2.group(3) or '' 

1841 space = m2.group(4) or '' 

1842 mod2 = m2.group(5) or '' 

1843 type_output = '%s%s%s%s' % (mod1, ptr, space, mod2) 

1844 else: 

1845 type_output = "extern" 

1846 

1847 synop = "<row><entry role=\"variable_type\">%s</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % ( 

1848 type_output, sid, symbol) 

1849 

1850 desc = "<refsect2 id=\"%s\" role=\"variable\"%s>\n<title>%s</title>\n" % (sid, condition, symbol) 

1851 

1852 desc += MakeIndexterms(symbol, sid) 

1853 desc += "\n" 

1854 desc += OutputSymbolExtraLinks(symbol) 

1855 

1856 decl_out = CreateValidSGML(declaration) 

1857 desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out 

1858 

1859 desc += MakeDeprecationNote(symbol) 

1860 

1861 if symbol in SymbolDocs: 

1862 desc += ConvertMarkDown(symbol, SymbolDocs[symbol]) 

1863 

1864 if symbol in SymbolAnnotations: 

1865 param_desc = SymbolAnnotations[symbol] 

1866 param_desc, param_annotations = ExpandAnnotation(symbol, param_desc) 

1867 if param_annotations != '': 

1868 desc += "\n<para>%s</para>" % param_annotations 

1869 

1870 desc += OutputSymbolTraits(symbol) 

1871 desc += "</refsect2>\n" 

1872 return (synop, desc) 

1873 

1874 

1875def OutputFunction(symbol, declaration, symbol_type): 

1876 """Returns the synopsis and detailed description of a function. 

1877 

1878 Args: 

1879 symbol (str): the function. 

1880 declaration (str): the declaration of the function. 

1881 

1882 Returns: 

1883 str: the formatted docs 

1884 """ 

1885 sid = common.CreateValidSGMLID(symbol) 

1886 condition = MakeConditionDescription(symbol) 

1887 

1888 # Take out the return type 

1889 # $1 $2 $3 

1890 regex = r'<RETURNS>\s*((?:const\s+|G_CONST_RETURN\s+|signed\s+|unsigned\s+|long\s+|short\s+|struct\s+|enum\s+)*)(\w+)(\s*\**\s*(?:const|G_CONST_RETURN)?\s*\**\s*(?:restrict)?\s*)<\/RETURNS>\n' 

1891 m = re.search(regex, declaration) 

1892 declaration = re.sub(regex, '', declaration) 

1893 type_modifier = m.group(1) or '' 

1894 type = m.group(2) 

1895 pointer = m.group(3) 

1896 pointer = pointer.rstrip() 

1897 xref = MakeXRef(type, tagify(type, "returnvalue")) 

1898 start = '' 

1899 # if (symbol_type == 'USER_FUNCTION') 

1900 # start = "typedef " 

1901 # 

1902 

1903 # We output const rather than G_CONST_RETURN. 

1904 type_modifier = re.sub(r'G_CONST_RETURN', 'const', type_modifier) 

1905 pointer = re.sub(r'G_CONST_RETURN', 'const', pointer) 

1906 pointer = re.sub(r'^\s+', '&#160;', pointer) 

1907 

1908 ret_type_output = "%s%s%s%s\n" % (start, type_modifier, xref, pointer) 

1909 

1910 indent_len = len(symbol) + 2 

1911 char1 = char2 = char3 = '' 

1912 if symbol_type == 'USER_FUNCTION': 

1913 indent_len += 3 

1914 char1 = "<phrase role=\"c_punctuation\">(</phrase>" 

1915 char2 = "*" 

1916 char3 = "<phrase role=\"c_punctuation\">)</phrase>" 

1917 

1918 symbol_output = "%s<link linkend=\"%s\">%s%s</link>%s" % (char1, sid, char2, symbol, char3) 

1919 if indent_len < MAX_SYMBOL_FIELD_WIDTH: 

1920 symbol_desc_output = "%s%s%s%s " % (char1, char2, symbol, char3) 

1921 else: 

1922 indent_len = MAX_SYMBOL_FIELD_WIDTH - 8 

1923 symbol_desc_output = ('%s%s%s%s\n' % (char1, char2, symbol, char3)) + (' ' * (indent_len - 1)) 

1924 

1925 synop = "<row><entry role=\"function_type\">%s</entry><entry role=\"function_name\">%s&#160;<phrase role=\"c_punctuation\">()</phrase></entry></row>\n" % ( 

1926 ret_type_output, symbol_output) 

1927 

1928 desc = "<refsect2 id=\"%s\" role=\"function\"%s>\n<title>%s&#160;()</title>\n" % (sid, condition, symbol) 

1929 

1930 desc += MakeIndexterms(symbol, sid) 

1931 desc += "\n" 

1932 desc += OutputSymbolExtraLinks(symbol) 

1933 

1934 desc += "<programlisting language=\"C\">%s%s(" % (ret_type_output, symbol_desc_output) 

1935 

1936 def tagfun(*args): 

1937 return tagify(args[0], "parameter") 

1938 

1939 fields = common.ParseFunctionDeclaration(declaration, MakeXRef, tagfun) 

1940 

1941 first = True 

1942 for field_name in fields.values(): 

1943 if first: 

1944 desc += field_name 

1945 first = False 

1946 else: 

1947 desc += ",\n" + (' ' * indent_len) + field_name 

1948 

1949 desc += ");</programlisting>\n" 

1950 

1951 desc += MakeDeprecationNote(symbol) 

1952 

1953 if symbol in SymbolDocs: 

1954 desc += ConvertMarkDown(symbol, SymbolDocs[symbol]) 

1955 

1956 if symbol in SymbolAnnotations: 

1957 param_desc = SymbolAnnotations[symbol] 

1958 param_desc, param_annotations = ExpandAnnotation(symbol, param_desc) 

1959 if param_annotations != '': 

1960 desc += "\n<para>%s</para>" % param_annotations 

1961 

1962 desc += OutputParamDescriptions("FUNCTION", symbol, fields.keys()) 

1963 desc += OutputSymbolTraits(symbol) 

1964 desc += "</refsect2>\n" 

1965 return (synop, desc) 

1966 

1967 

1968def OutputParamDescriptions(symbol_type, symbol, fields): 

1969 """Returns the DocBook output describing the parameters of a symbol. 

1970 

1971 This can be used for functions, macros or signal handlers. 

1972 

1973 Args: 

1974 symbol_type (str): 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal 

1975 handlers have an implicit user_data parameter last. 

1976 symbol (str): the name of the symbol being described. 

1977 fields (list): parsed fields from the declaration, used to determine 

1978 undocumented/unused entries 

1979 

1980 Returns: 

1981 str: the formatted parameter docs 

1982 """ 

1983 output = '' 

1984 num_params = 0 

1985 field_descrs = None 

1986 

1987 if fields: 

1988 field_descrs = [f for f in fields if f not in ['void', 'Returns']] 

1989 else: 

1990 field_descrs = [] 

1991 

1992 params = SymbolParams.get(symbol) 

1993 logging.info("param_desc(%s, %s) = %s", symbol_type, symbol, str(params)) 

1994 # This might be an empty dict, but for SIGNALS we append the user_data docs. 

1995 # TODO(ensonic): maybe create that docstring in GetSignals() 

1996 if params is not None: 

1997 returns = '' 

1998 params_desc = '' 

1999 missing_parameters = '' 

2000 unused_parameters = '' 

2001 

2002 for param_name, param_desc in params.items(): 

2003 param_desc, param_annotations = ExpandAnnotation(symbol, param_desc) 

2004 param_desc = ConvertMarkDown(symbol, param_desc) 

2005 # trim 

2006 param_desc = re.sub(r'^(\s|\n)+', '', param_desc, flags=re.M | re.S) 

2007 param_desc = re.sub(r'(\s|\n)+$', '', param_desc, flags=re.M | re.S) 

2008 if param_name == "Returns": 

2009 returns = param_desc 

2010 if param_annotations != '': 

2011 returns += "\n<para>%s</para>" % param_annotations 

2012 

2013 elif param_name == "void": 

2014 # FIXME: &common.LogWarning()? 

2015 logging.info("!!!! void in params for %s?\n", symbol) 

2016 else: 

2017 if fields: 

2018 if param_name not in field_descrs: 

2019 common.LogWarning(*GetSymbolSourceLocation(symbol), 

2020 "Parameter description for %s::%s is not used from source code comment block." % (symbol, param_name)) 

2021 if unused_parameters != '': 

2022 unused_parameters += ", " + param_name 

2023 else: 

2024 unused_parameters = param_name 

2025 else: 

2026 field_descrs.remove(param_name) 

2027 

2028 if param_desc != '': 

2029 params_desc += "<row><entry role=\"parameter_name\"><para>%s</para></entry>\n<entry role=\"parameter_description\">%s</entry>\n<entry role=\"parameter_annotations\">%s</entry></row>\n" % ( 

2030 param_name, param_desc, param_annotations) 

2031 num_params += 1 

2032 

2033 for param_name in field_descrs: 

2034 common.LogWarning(*GetSymbolSourceLocation(symbol), 

2035 "Parameter description for %s::%s is missing in source code comment block." % (symbol, param_name)) 

2036 if missing_parameters != '': 

2037 missing_parameters += ", " + param_name 

2038 else: 

2039 missing_parameters = param_name 

2040 

2041 # Signals have an implicit user_data parameter which we describe. 

2042 if symbol_type == "SIGNAL": 

2043 params_desc += "<row><entry role=\"parameter_name\"><simpara>user_data</simpara></entry>\n<entry role=\"parameter_description\"><simpara>user data set when the signal handler was connected.</simpara></entry>\n<entry role=\"parameter_annotations\"></entry></row>\n" 

2044 

2045 # Start a table if we need one. 

2046 if params_desc != '': 

2047 sid = common.CreateValidSGMLID("%s.parameters" % symbol) 

2048 

2049 output += '''<refsect3 id="%s" role="parameters">\n<title>Parameters</title> 

2050<informaltable role="parameters_table" pgwide="1" frame="none"> 

2051<tgroup cols="3"> 

2052<colspec colname="parameters_name" colwidth="150px"/> 

2053<colspec colname="parameters_description"/> 

2054<colspec colname="parameters_annotations" colwidth="200px"/> 

2055<tbody> 

2056''' % sid 

2057 output += params_desc 

2058 output += "</tbody></tgroup></informaltable>\n</refsect3>" 

2059 

2060 # Output the returns info last 

2061 if returns != '': 

2062 sid = common.CreateValidSGMLID("%s.returns" % symbol) 

2063 

2064 output += '''<refsect3 id="%s" role=\"returns\">\n<title>Returns</title> 

2065''' % sid 

2066 output += returns 

2067 output += "\n</refsect3>" 

2068 

2069 # remember missing/unused parameters (needed in tmpl-free build) 

2070 if missing_parameters != '' and (symbol not in AllIncompleteSymbols): 

2071 AllIncompleteSymbols[symbol] = missing_parameters 

2072 

2073 if unused_parameters != '' and (symbol not in AllUnusedSymbols): 

2074 AllUnusedSymbols[symbol] = unused_parameters 

2075 

2076 if num_params == 0 and fields and field_descrs: 

2077 if symbol not in AllIncompleteSymbols: 

2078 AllIncompleteSymbols[symbol] = "<parameters>" 

2079 return output 

2080 

2081 

2082def ParseStabilityLevel(stability, file, line, message): 

2083 """Parses a stability level and outputs a warning if it isn't valid. 

2084 Args: 

2085 stability (str): the stability text. 

2086 file, line: context for error message 

2087 message: description of where the level is from, to use in any error message. 

2088 Returns: 

2089 str: the parsed stability level string. 

2090 """ 

2091 stability = stability.strip() 

2092 sl = stability.strip().lower() 

2093 if sl == 'stable': 

2094 stability = "Stable" 

2095 elif sl == 'unstable': 

2096 stability = "Unstable" 

2097 elif sl == 'private': 

2098 stability = "Private" 

2099 else: 

2100 common.LogWarning(file, line, 

2101 "%s is %s. It should be one of these: Stable, " 

2102 "Unstable, or Private." % ( 

2103 message, stability)) 

2104 return str(stability) 

2105 

2106 

2107def OutputDBFile(file, title, section_id, includes, functions_synop, other_synop, functions_details, other_desc, signals_synop, signals_desc, args_synop, args_desc, actions_synop, actions_desc, hierarchy, interfaces, implementations, prerequisites, derived, file_objects, default_stability): 

2108 """Outputs the final DocBook file for one section. 

2109 

2110 Args: 

2111 file (str): the name of the file. 

2112 title (str): the title from the $MODULE-sections.txt file 

2113 section_id (str): the id to use for the toplevel tag. 

2114 includes (str): comma-separates list of include files added at top of 

2115 synopsis, with '<' '>' around them (if not already enclosed in ''). 

2116 functions_synop (str): the DocBook for the Functions Synopsis part. 

2117 other_synop (str): the DocBook for the Types and Values Synopsis part. 

2118 functions_details (str): the DocBook for the Functions Details part. 

2119 other_desc (str): the DocBook for the Types and Values Details part. 

2120 signal_synop (str): the DocBook for the Signal Synopsis part 

2121 signal_desc (str): the DocBook for the Signal Description part 

2122 args_synop (str): the DocBook for the Arg Synopsis part 

2123 args_desc (str): the DocBook for the Arg Description part 

2124 actions_synop (str): the DocBook for the Action Synopsis part 

2125 actions_desc (str): the DocBook for the Action Description part 

2126 hierarchy (str): the DocBook for the Object Hierarchy part 

2127 interfaces (str): the DocBook for the Interfaces part 

2128 implementations (str): the DocBook for the Known Implementations part 

2129 prerequisites (str): the DocBook for the Prerequisites part 

2130 derived (str): the DocBook for the Derived Interfaces part 

2131 file_objects (list): objects in this file 

2132 default_stability (str): fallback if no api-stability is set (optional) 

2133 

2134 Returns: 

2135 bool: True if the docs where updated 

2136 """ 

2137 

2138 logging.info("Output docbook for file %s with title '%s'", file, title) 

2139 

2140 # The edited title overrides the one from the sections file. 

2141 new_title = SymbolDocs.get(file + ":title") 

2142 if new_title and not new_title.strip() == '': 

2143 title = new_title 

2144 logging.info("Found title: %s", title) 

2145 

2146 short_desc = SymbolDocs.get(file + ":short_description") 

2147 if not short_desc or short_desc.strip() == '': 

2148 short_desc = '' 

2149 else: 

2150 # Don't use ConvertMarkDown here for now since we don't want blocks 

2151 short_desc = ExpandAbbreviations(title + ":short_description", short_desc) 

2152 logging.info("Found short_desc: %s", short_desc) 

2153 

2154 long_desc = SymbolDocs.get(file + ":long_description") 

2155 if not long_desc or long_desc.strip() == '': 

2156 long_desc = '' 

2157 else: 

2158 long_desc = ConvertMarkDown(title + ":long_description", long_desc) 

2159 logging.info("Found long_desc: %s", long_desc) 

2160 

2161 see_also = SymbolDocs.get(file + ":see_also") 

2162 if not see_also or re.search(r'^\s*(<para>)?\s*(</para>)?\s*$', see_also): 

2163 see_also = '' 

2164 else: 

2165 see_also = ConvertMarkDown(title + ":see_also", see_also) 

2166 logging.info("Found see_also: %s", see_also) 

2167 

2168 if see_also: 

2169 see_also = "<refsect1 id=\"%s.see-also\">\n<title>See Also</title>\n%s\n</refsect1>\n" % (section_id, see_also) 

2170 

2171 stability = SymbolDocs.get(file + ":stability") 

2172 if not stability or re.search(r'^\s*$', stability): 

2173 stability = '' 

2174 else: 

2175 line_number = GetSymbolSourceLocation(file + ":stability")[1] 

2176 stability = ParseStabilityLevel(stability, file, line_number, "Section stability level") 

2177 logging.info("Found stability: %s", stability) 

2178 

2179 if not stability: 

2180 stability = default_stability or '' 

2181 

2182 if stability: 

2183 if stability in AnnotationDefinition: 

2184 AnnotationsUsed[stability] = True 

2185 stability = "<acronym>%s</acronym>" % stability 

2186 stability = "<refsect1 id=\"%s.stability-level\">\n<title>Stability Level</title>\n%s, unless otherwise indicated\n</refsect1>\n" % ( 

2187 section_id, stability) 

2188 

2189 image = SymbolDocs.get(file + ":image") 

2190 if not image or re.search(r'^\s*$', image): 

2191 image = '' 

2192 else: 

2193 image = image.strip() 

2194 

2195 format = None 

2196 

2197 il = image.lower() 

2198 if re.search(r'jpe?g$', il): 

2199 format = "format='JPEG'" 

2200 elif il.endswith('png'): 

2201 format = "format='PNG'" 

2202 elif il.endswith('svg'): 

2203 format = "format='SVG'" 

2204 else: 

2205 format = '' 

2206 

2207 image = " <inlinegraphic fileref='%s' %s/>\n" % (image, format) 

2208 

2209 include_output = '' 

2210 if includes: 

2211 include_output += "<refsect1 id=\"%s.includes\"><title>Includes</title><synopsis>" % section_id 

2212 for include in includes.split(','): 

2213 if re.search(r'^\".+\"$', include): 

2214 include_output += "#include %s\n" % include 

2215 else: 

2216 include = re.sub(r'^\s+|\s+$', '', include, flags=re.S) 

2217 include_output += "#include &lt;%s&gt;\n" % include 

2218 

2219 include_output += "</synopsis></refsect1>\n" 

2220 

2221 extralinks = OutputSectionExtraLinks(title, "Section:%s" % file) 

2222 

2223 old_db_file = os.path.join(DB_OUTPUT_DIR, file + '.xml') 

2224 new_db_file = os.path.join(DB_OUTPUT_DIR, file + '.xml.new') 

2225 

2226 OUTPUT = open(new_db_file, 'w', encoding='utf-8') 

2227 

2228 object_anchors = '' 

2229 for fobject in file_objects: 

2230 if fobject == section_id: 

2231 continue 

2232 sid = common.CreateValidSGMLID(fobject) 

2233 logging.info("Adding anchor for %s\n", fobject) 

2234 object_anchors += "<anchor id=\"%s\"/>" % sid 

2235 

2236 # Make sure we produce valid docbook 

2237 if not functions_details: 

2238 functions_details = "<para />" 

2239 

2240 # We used to output this, but is messes up our common.UpdateFileIfChanged code 

2241 # since it changes every day (and it is only used in the man pages): 

2242 # "<refentry id="$section_id" revision="$mday $month $year">" 

2243 

2244 OUTPUT.write(DB_REFENTRY.substitute({ 

2245 'actions_desc': actions_desc, 

2246 'actions_synop': actions_synop, 

2247 'args_desc': args_desc, 

2248 'args_synop': args_synop, 

2249 'derived': derived, 

2250 'extralinks': extralinks, 

2251 'functions_details': functions_details, 

2252 'functions_synop': functions_synop, 

2253 'header': MakeDocHeader('refentry'), 

2254 'hierarchy': hierarchy, 

2255 'image': image, 

2256 'include_output': include_output, 

2257 'interfaces': interfaces, 

2258 'implementations': implementations, 

2259 'long_desc': long_desc, 

2260 'object_anchors': object_anchors, 

2261 'other_desc': other_desc, 

2262 'other_synop': other_synop, 

2263 'prerequisites': prerequisites, 

2264 'section_id': section_id, 

2265 'see_also': see_also, 

2266 'signals_desc': signals_desc, 

2267 'signals_synop': signals_synop, 

2268 'short_desc': short_desc, 

2269 'stability': stability, 

2270 'title': title, 

2271 'MODULE': MODULE.upper(), 

2272 })) 

2273 OUTPUT.close() 

2274 

2275 return common.UpdateFileIfChanged(old_db_file, new_db_file, 0) 

2276 

2277 

2278def OutputProgramDBFile(program, section_id): 

2279 """Outputs the final DocBook file for one program. 

2280 

2281 Args: 

2282 file (str): the name of the file. 

2283 section_id (str): the id to use for the toplevel tag. 

2284 

2285 Returns: 

2286 bool: True if the docs where updated 

2287 """ 

2288 logging.info("Output program docbook for %s", program) 

2289 

2290 short_desc = SourceSymbolDocs.get(program + ":short_description") 

2291 if not short_desc or short_desc.strip() == '': 

2292 short_desc = '' 

2293 else: 

2294 # Don't use ConvertMarkDown here for now since we don't want blocks 

2295 short_desc = ExpandAbbreviations(program, short_desc) 

2296 logging.info("Found short_desc: %s", short_desc) 

2297 

2298 synopsis = SourceSymbolDocs.get(program + ":synopsis") 

2299 if synopsis and synopsis.strip() != '': 

2300 items = synopsis.split(' ') 

2301 for i in range(0, len(items)): 

2302 parameter = items[i] 

2303 choice = "plain" 

2304 rep = '' 

2305 

2306 # first parameter is the command name 

2307 if i == 0: 

2308 synopsis = "<command>%s</command>\n" % parameter 

2309 continue 

2310 

2311 # square brackets indicate optional parameters, curly brackets 

2312 # indicate required parameters ("plain" parameters are also 

2313 # mandatory, but do not get extra decoration) 

2314 m1 = re.search(r'^\[(.+?)\]$', parameter) 

2315 m2 = re.search(r'^\{(.+?)\}$', parameter) 

2316 if m1: 

2317 choice = "opt" 

2318 parameter = m1.group(1) 

2319 elif m2: 

2320 choice = "req" 

2321 parameter = m2.group(1) 

2322 

2323 # parameters ending in "..." are repeatable 

2324 if parameter.endswith('...'): 

2325 rep = ' rep=\"repeat\"' 

2326 parameter = parameter[:-3] 

2327 

2328 # italic parameters are replaceable parameters 

2329 parameter = re.sub(r'\*(.+?)\*', r'<replaceable>\1</replaceable>', parameter) 

2330 

2331 synopsis += "<arg choice=\"%s\"%s>" % (choice, rep) 

2332 synopsis += parameter 

2333 synopsis += "</arg>\n" 

2334 

2335 logging.info("Found synopsis: %s", synopsis) 

2336 else: 

2337 synopsis = "<command>%s</command>" % program 

2338 

2339 long_desc = SourceSymbolDocs.get(program + ":long_description") 

2340 if not long_desc or long_desc.strip() == '': 

2341 long_desc = '' 

2342 else: 

2343 long_desc = ConvertMarkDown("%s:long_description" % program, long_desc) 

2344 logging.info("Found long_desc: %s", long_desc) 

2345 

2346 options = '' 

2347 o = program + ":options" 

2348 if o in SourceSymbolDocs: 

2349 opts = SourceSymbolDocs[o].split('\t') 

2350 

2351 logging.info('options: %d, %s', len(opts), str(opts)) 

2352 

2353 options = "<refsect1>\n<title>Options</title>\n<variablelist>\n" 

2354 for k in range(0, len(opts), 2): 

2355 opt_desc = opts[k + 1] 

2356 

2357 opt_desc = re.sub(r'\*(.+?)\*', r'<replaceable>\1</replaceable>', opt_desc) 

2358 

2359 options += "<varlistentry>\n<term>" 

2360 opt_names = opts[k].split(',') 

2361 for i in range(len(opt_names)): 

2362 prefix = ', ' if i > 0 else '' 

2363 # italic parameters are replaceable parameters 

2364 opt_name = re.sub(r'\*(.+?)\*', r'<replaceable>\1</replaceable>', opt_names[i]) 

2365 

2366 options += "%s<option>%s</option>\n" % (prefix, opt_name) 

2367 

2368 options += "</term>\n" 

2369 options += "<listitem><para>%s</para></listitem>\n" % opt_desc 

2370 options += "</varlistentry>\n" 

2371 

2372 options += "</variablelist></refsect1>\n" 

2373 

2374 exit_status = SourceSymbolDocs.get(program + ":returns") 

2375 if exit_status and exit_status != '': 

2376 exit_status = ConvertMarkDown("%s:returns" % program, exit_status) 

2377 exit_status = "<refsect1 id=\"%s.exit-status\">\n<title>Exit Status</title>\n%s\n</refsect1>\n" % ( 

2378 section_id, exit_status) 

2379 else: 

2380 exit_status = '' 

2381 

2382 see_also = SourceSymbolDocs.get(program + ":see_also") 

2383 if not see_also or re.search(r'^\s*(<para>)?\s*(</para>)?\s*$', see_also): 

2384 see_also = '' 

2385 else: 

2386 see_also = ConvertMarkDown("%s:see_also" % program, see_also) 

2387 logging.info("Found see_also: %s", see_also) 

2388 

2389 if see_also: 

2390 see_also = "<refsect1 id=\"%s.see-also\">\n<title>See Also</title>\n%s\n</refsect1>\n" % (section_id, see_also) 

2391 

2392 old_db_file = os.path.join(DB_OUTPUT_DIR, program + ".xml") 

2393 new_db_file = os.path.join(DB_OUTPUT_DIR, program + ".xml.new") 

2394 

2395 OUTPUT = open(new_db_file, 'w', encoding='utf-8') 

2396 

2397 OUTPUT.write('''%s 

2398<refentry id="%s"> 

2399<refmeta> 

2400<refentrytitle role="top_of_page" id="%s.top_of_page">%s</refentrytitle> 

2401<manvolnum>1</manvolnum> 

2402<refmiscinfo>User Commands</refmiscinfo> 

2403</refmeta> 

2404<refnamediv> 

2405<refname>%s</refname> 

2406<refpurpose>%s</refpurpose> 

2407</refnamediv> 

2408<refsynopsisdiv> 

2409<cmdsynopsis>%s</cmdsynopsis> 

2410</refsynopsisdiv> 

2411<refsect1 id="%s.description" role="desc"> 

2412<title role="desc.title">Description</title> 

2413%s 

2414</refsect1> 

2415%s%s%s 

2416</refentry> 

2417''' % (MakeDocHeader("refentry"), section_id, section_id, program, program, short_desc, synopsis, section_id, long_desc, options, exit_status, see_also)) 

2418 OUTPUT.close() 

2419 

2420 return common.UpdateFileIfChanged(old_db_file, new_db_file, 0) 

2421 

2422 

2423def OutputExtraFile(file): 

2424 """Copies an "extra" DocBook file into the output directory, expanding abbreviations. 

2425 

2426 Args: 

2427 file (str): the source file. 

2428 

2429 Returns: 

2430 bool: True if the docs where updated 

2431 """ 

2432 

2433 basename = os.path.basename(file) 

2434 

2435 old_db_file = os.path.join(DB_OUTPUT_DIR, basename) 

2436 new_db_file = os.path.join(DB_OUTPUT_DIR, basename + ".new") 

2437 

2438 contents = open(file, 'r', encoding='utf-8').read() 

2439 

2440 with open(new_db_file, 'w', encoding='utf-8') as out: 

2441 out.write(ExpandAbbreviations(basename + " file", contents)) 

2442 

2443 return common.UpdateFileIfChanged(old_db_file, new_db_file, 0) 

2444 

2445 

2446def GetDocbookHeader(main_file): 

2447 if os.path.exists(main_file): 

2448 INPUT = open(main_file, 'r', encoding='utf-8') 

2449 header = '' 

2450 for line in INPUT: 

2451 if re.search(r'^\s*<(book|chapter|article)', line): 

2452 # check that the top-level tagSYSTEM or the doctype decl contain the xinclude namespace decl 

2453 if not re.search(r'http:\/\/www.w3.org\/200[13]\/XInclude', line) and \ 

2454 not re.search(r'http:\/\/www.w3.org\/200[13]\/XInclude', header, flags=re.MULTILINE): 

2455 header = '' 

2456 break 

2457 

2458 # if there are SYSTEM ENTITIES here, we should prepend "../" to the path 

2459 # FIXME: not sure if we can do this now, as people already work-around the problem 

2460 # r'#<!ENTITY % ([a-zA-Z-]+) SYSTEM \"([^/][a-zA-Z./]+)\">', r'<!ENTITY % \1 SYSTEM \"../\2\">'; 

2461 line = re.sub( 

2462 r'<!ENTITY % gtkdocentities SYSTEM "([^"]*)">', r'<!ENTITY % gtkdocentities SYSTEM "../\1">', line) 

2463 header += line 

2464 INPUT.close() 

2465 header = header.strip() 

2466 else: 

2467 header = '''<?xml version="1.0"?> 

2468<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" 

2469 "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" 

2470[ 

2471 <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'"> 

2472 <!ENTITY % gtkdocentities SYSTEM "../xml/gtkdocentities.ent"> 

2473 %gtkdocentities; 

2474]>''' 

2475 return header 

2476 

2477 

2478def OutputBook(main_file, book_top, book_bottom, obj_tree): 

2479 """Outputs the entities that need to be included into the main docbook file for the module. 

2480 

2481 Args: 

2482 book_top (str): the declarations of the entities, which are added 

2483 at the top of the main docbook file. 

2484 book_bottom (str): the entities, which are added in the main docbook 

2485 file at the desired position. 

2486 obj_tree (list): object tree list 

2487 """ 

2488 

2489 old_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.top") 

2490 new_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.top.new") 

2491 

2492 with open(new_file, 'w', encoding='utf-8') as out: 

2493 out.write(book_top) 

2494 

2495 common.UpdateFileIfChanged(old_file, new_file, 0) 

2496 

2497 old_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.bottom") 

2498 new_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.bottom.new") 

2499 

2500 with open(new_file, 'w', encoding='utf-8') as out: 

2501 out.write(book_bottom) 

2502 

2503 common.UpdateFileIfChanged(old_file, new_file, 0) 

2504 

2505 # If the main docbook file hasn't been created yet, we create it here. 

2506 # The user can tweak it later. 

2507 if main_file and not os.path.exists(main_file): 

2508 OUTPUT = open(main_file, 'w', encoding='utf-8') 

2509 

2510 logging.info("no master doc, create default one at: " + main_file) 

2511 

2512 OUTPUT.write('''%s 

2513<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude"> 

2514 <bookinfo> 

2515 <title>&package_name; Reference Manual</title> 

2516 <releaseinfo> 

2517 for &package_string;. 

2518 The latest version of this documentation can be found on-line at 

2519 <ulink role="online-location" url="http://[SERVER]/&package_name;/index.html">http://[SERVER]/&package_name;/</ulink>. 

2520 </releaseinfo> 

2521 </bookinfo> 

2522 

2523 <chapter> 

2524 <title>[Insert title here]</title> 

2525%s </chapter> 

2526''' % (MakeDocHeader("book"), book_bottom)) 

2527 if obj_tree: 

2528 OUTPUT.write(''' <chapter id="object-tree"> 

2529 <title>Object Hierarchy</title> 

2530 <xi:include href="xml/tree_index.sgml"/> 

2531 </chapter> 

2532''') 

2533 else: 

2534 OUTPUT.write(''' <!-- enable this when you use gobject types 

2535 <chapter id="object-tree"> 

2536 <title>Object Hierarchy</title> 

2537 <xi:include href="xml/tree_index.sgml"/> 

2538 </chapter> 

2539 --> 

2540''') 

2541 

2542 OUTPUT.write(''' <index id="api-index-full"> 

2543 <title>API Index</title> 

2544 <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include> 

2545 </index> 

2546 <index id="deprecated-api-index" role="deprecated"> 

2547 <title>Index of deprecated API</title> 

2548 <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include> 

2549 </index> 

2550''') 

2551 for version in sorted(set(Since.values())): 

2552 dash_version = version.replace('.', '-') 

2553 OUTPUT.write(''' <index id="api-index-%s" role="%s"> 

2554 <title>Index of new API in %s</title> 

2555 <xi:include href="xml/api-index-%s.xml"><xi:fallback /></xi:include> 

2556 </index> 

2557''' % (dash_version, version, version, version)) 

2558 

2559 if AnnotationsUsed: 

2560 OUTPUT.write(''' <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include> 

2561''') 

2562 else: 

2563 OUTPUT.write(''' <!-- enable this when you use gobject introspection annotations 

2564 <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include> 

2565 --> 

2566''') 

2567 

2568 OUTPUT.write('''</book> 

2569''') 

2570 

2571 OUTPUT.close() 

2572 

2573 

2574def CreateValidSGML(text): 

2575 """Turn any chars which are used in XML into entities. 

2576 

2577 e.g. '<' into '&lt;' 

2578 

2579 Args: 

2580 text (str): the text to turn into proper XML. 

2581 

2582 Returns: 

2583 str: escaped input 

2584 """ 

2585 

2586 text = text.replace('&', '&amp;') # Do this first, or the others get messed up. 

2587 text = text.replace('<', '&lt;') 

2588 text = text.replace('>', '&gt;') 

2589 # browsers render single tabs inconsistently 

2590 text = re.sub(r'([^\s])\t([^\s])', r'\1&#160;\2', text) 

2591 return text 

2592 

2593 

2594def ConvertXMLChars(symbol, text): 

2595 """Escape XML chars. 

2596 

2597 This is used for text in source code comment blocks, to turn 

2598 chars which are used in XML into entities, e.g. '<' into 

2599 &lt;'. Depending on INLINE_MARKUP_MODE, this is done 

2600 unconditionally or only if the character doesn't seem to be 

2601 part of an XML construct (tag or entity reference). 

2602 Args: 

2603 text (str): the text to turn into proper XML. 

2604 

2605 Returns: 

2606 str: escaped input 

2607 """ 

2608 

2609 if INLINE_MARKUP_MODE: 

2610 # For the XML mode only convert to entities outside CDATA sections. 

2611 return ModifyXMLElements(text, symbol, 

2612 "<!\\[CDATA\\[|<programlisting[^>]*>", 

2613 ConvertXMLCharsEndTag, 

2614 ConvertXMLCharsCallback) 

2615 # For the simple non-sgml mode, convert to entities everywhere. 

2616 

2617 text = re.sub(r'&(?![a-zA-Z#]+;)', r'&amp;', text) # Do this first, or the others get messed up. 

2618 # Allow '<' in verbatim markdown 

2619 # TODO: we don't want to convert any of them between `` 

2620 text = re.sub(r'(?<!`)<', r'&lt;', text) 

2621 # Allow '>' at beginning of string for blockquote markdown 

2622 text = re.sub(r'''(?<=[^\w\n"'\/-])>''', r'&gt;', text) 

2623 

2624 return text 

2625 

2626 

2627def ConvertXMLCharsEndTag(start_tag): 

2628 if start_tag == '<![CDATA[': 

2629 return "]]>" 

2630 return "</programlisting>" 

2631 

2632 

2633def ConvertXMLCharsCallback(text, symbol, tag): 

2634 if re.search(r'^<programlisting', tag): 

2635 logging.debug('call modifyXML') 

2636 # We can handle <programlisting> specially here. 

2637 return ModifyXMLElements(text, symbol, 

2638 "<!\\[CDATA\\[", 

2639 ConvertXMLCharsEndTag, 

2640 ConvertXMLCharsCallback2) 

2641 elif tag == '': 

2642 logging.debug('replace entities') 

2643 # If we're not in CDATA convert to entities. 

2644 text = re.sub(r'&(?![a-zA-Z#]+;)', r'&amp;', text) # Do this first, or the others get messed up. 

2645 text = re.sub(r'<(?![a-zA-Z\/!])', r'&lt;', text) 

2646 # Allow '>' at beginning of string for blockquote markdown 

2647 text = re.sub(r'''(?<=[^\w\n"'\/-])>''', r'&gt;', text) 

2648 

2649 # Handle "#include <xxxxx>" 

2650 text = re.sub(r'#include(\s+)<([^>]+)>', r'#include\1&lt;\2&gt;', text) 

2651 

2652 return text 

2653 

2654 

2655def ConvertXMLCharsCallback2(text, symbol, tag): 

2656 # If we're not in CDATA convert to entities. 

2657 # We could handle <programlisting> differently, though I'm not sure it helps. 

2658 if tag == '': 

2659 # replace only if its not a tag 

2660 text = re.sub(r'&(?![a-zA-Z#]+;)', r'&amp;', text) # Do this first, or the others get messed up. 

2661 text = re.sub(r'<(?![a-zA-Z\/!])', r'&lt;', text) 

2662 text = re.sub(r'''(?<![a-zA-Z0-9"'\/-])>''', r'&gt;', text) 

2663 # Handle "#include <xxxxx>" 

2664 text = re.sub(r'/#include(\s+)<([^>]+)>', r'#include\1&lt;\2&gt;', text) 

2665 

2666 return text 

2667 

2668 

2669def ExpandAnnotation(symbol, param_desc): 

2670 """This turns annotations into acronym tags. 

2671 Args: 

2672 symbol (str): the symbol being documented, for error messages. 

2673 param_desc (str): the text to expand. 

2674 

2675 Returns: 

2676 str: the remaining param_desc 

2677 str: the formatted annotations 

2678 """ 

2679 param_annotations = '' 

2680 

2681 # look for annotations at the start of the comment part 

2682 # function level annotations don't end with a colon ':' 

2683 m = re.search(r'^\s*\((.*?)\)(:|$)', param_desc) 

2684 if m: 

2685 param_desc = param_desc[m.end():] 

2686 

2687 annotations = re.split(r'\)\s*\(', m.group(1)) 

2688 logging.info("annotations for %s: '%s'\n", symbol, m.group(1)) 

2689 for annotation in annotations: 

2690 # need to search for the longest key-match in %AnnotationDefinition 

2691 match_length = 0 

2692 match_annotation = '' 

2693 

2694 for annotationdef in AnnotationDefinition: 

2695 if annotation.startswith(annotationdef): 

2696 if len(annotationdef) > match_length: 

2697 match_length = len(annotationdef) 

2698 match_annotation = annotationdef 

2699 

2700 annotation_extra = '' 

2701 if match_annotation != '': 

2702 m = re.search(match_annotation + r'\s+(.*)', annotation) 

2703 if m: 

2704 annotation_extra = " " + m.group(1) 

2705 

2706 AnnotationsUsed[match_annotation] = 1 

2707 param_annotations += "[<acronym>%s</acronym>%s]" % (match_annotation, annotation_extra) 

2708 else: 

2709 common.LogWarning(*GetSymbolSourceLocation(symbol), 

2710 "unknown annotation \"%s\" in documentation for %s." % (annotation, symbol)) 

2711 param_annotations += "[%s]" % annotation 

2712 

2713 param_desc = param_desc.strip() 

2714 m = re.search(r'^(.*?)\.*\s*$', param_desc, flags=re.S) 

2715 param_desc = m.group(1) + '. ' 

2716 

2717 if param_annotations != '': 

2718 param_annotations = "<emphasis role=\"annotation\">%s</emphasis>" % param_annotations 

2719 

2720 return (param_desc, param_annotations) 

2721 

2722 

2723def ExpandAbbreviations(symbol, text): 

2724 """Expand the shortcut notation for symbol references. 

2725 

2726 This turns the abbreviations function(), macro(), @param, %constant, and #symbol 

2727 into appropriate DocBook markup. CDATA sections and <programlisting> parts 

2728 are skipped. 

2729 

2730 Args: 

2731 symbol (str): the symbol being documented, for error messages. 

2732 text (str): the text to expand. 

2733 

2734 Returns: 

2735 str: the expanded text 

2736 """ 

2737 # Note: This is a fallback and normally done in the markdown parser 

2738 

2739 logging.debug('expand abbreviations for "%s", text: [%s]', symbol, text) 

2740 m = re.search(r'\|\[[^\n]*\n(.*)\]\|', text, flags=re.M | re.S) 

2741 if m: 

2742 logging.debug('replaced entities in code block') 

2743 text = text[:m.start(1)] + md_to_db.ReplaceEntities(m.group(1)) + text[m.end(1):] 

2744 

2745 # Convert "|[" and "]|" into the start and end of program listing examples. 

2746 # Support \[<!-- language="C" --> modifiers 

2747 text = re.sub(r'\|\[<!-- language="([^"]+)" -->', 

2748 r'<informalexample><programlisting role="example" language="\1"><![CDATA[', text) 

2749 text = re.sub(r'\|\[', r'<informalexample><programlisting role="example"><![CDATA[', text) 

2750 text = re.sub(r'\]\|', r']]></programlisting></informalexample>', text) 

2751 

2752 # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags 

2753 # as such) 

2754 return ModifyXMLElements(text, symbol, 

2755 "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE", 

2756 ExpandAbbreviationsEndTag, 

2757 ExpandAbbreviationsCallback) 

2758 

2759 

2760def ExpandAbbreviationsEndTag(start_tag): 

2761 # Returns the end tag (as a regexp) corresponding to the given start tag. 

2762 if start_tag == r'<!\[CDATA\[': 

2763 return "]]>" 

2764 if start_tag == "<!DOCTYPE": 

2765 return '>' 

2766 m = re.search(r'<(\w+)', start_tag) 

2767 if m: 

2768 return "</%s>" % m.group(1) 

2769 

2770 logging.warning('no end tag for "%s"', start_tag) 

2771 return '' 

2772 

2773 

2774def ExpandAbbreviationsCallback(text, symbol, tag): 

2775 # Called inside or outside each CDATA or <programlisting> section. 

2776 if tag.startswith(r'^<programlisting'): 

2777 # Handle any embedded CDATA sections. 

2778 return ModifyXMLElements(text, symbol, 

2779 "<!\\[CDATA\\[", 

2780 ExpandAbbreviationsEndTag, 

2781 ExpandAbbreviationsCallback2) 

2782 elif tag == '': 

2783 # NOTE: this is a fallback. It is normally done by the Markdown parser. 

2784 # but is also used for OutputExtraFile 

2785 

2786 # We are outside any CDATA or <programlisting> sections, so we expand 

2787 # any gtk-doc abbreviations. 

2788 

2789 # Convert '@param()' 

2790 # FIXME: we could make those also links ($symbol.$2), but that would be less 

2791 # useful as the link target is a few lines up or down 

2792 text = re.sub(r'(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)', r'\1<parameter>\2()</parameter>', text) 

2793 

2794 # Convert 'function()' or 'macro()'. 

2795 # if there is abc_*_def() we don't want to make a link to _def() 

2796 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/ 

2797 def f1(m): 

2798 return m.group(1) + MakeXRef(m.group(2), tagify(m.group(2) + "()", "function")) 

2799 text = re.sub(r'([^\*.\w])(\w+)\s*\(\)', f1, text) 

2800 # handle #Object.func() 

2801 text = re.sub(r'(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)', f1, text) 

2802 

2803 # Convert '@param', but not '\@param'. 

2804 text = re.sub(r'(\A|[^\\])\@(\w+((\.|->)\w+)*)', r'\1<parameter>\2</parameter>', text) 

2805 text = re.sub(r'/\\\@', r'\@', text) 

2806 

2807 # Convert '%constant', but not '\%constant'. 

2808 # Also allow negative numbers, e.g. %-1. 

2809 def f2(m): 

2810 return m.group(1) + MakeXRef(m.group(2), tagify(m.group(2), "literal")) 

2811 text = re.sub(r'(\A|[^\\])\%(-?\w+)', f2, text) 

2812 text = re.sub(r'\\\%', r'\%', text) 

2813 

2814 # Convert '#symbol', but not '\#symbol'. 

2815 def f3(m): 

2816 return m.group(1) + MakeHashXRef(m.group(2), "type") 

2817 text = re.sub(r'(\A|[^\\])#([\w\-:\.]+[\w]+)', f3, text) 

2818 text = re.sub(r'\\#', '#', text) 

2819 

2820 return text 

2821 

2822 

2823def ExpandAbbreviationsCallback2(text, symbol, tag): 

2824 # This is called inside a <programlisting> 

2825 if tag == '': 

2826 # We are inside a <programlisting> but outside any CDATA sections, 

2827 # so we expand any gtk-doc abbreviations. 

2828 # FIXME: why is this different from &ExpandAbbreviationsCallback(), 

2829 # why not just call it 

2830 text = re.sub(r'#(\w+)', lambda m: '%s;' % MakeHashXRef(m.group(1), ''), text) 

2831 elif tag == "<![CDATA[": 

2832 # NOTE: this is a fallback. It is normally done by the Markdown parser. 

2833 text = md_to_db.ReplaceEntities(text, symbol) 

2834 

2835 return text 

2836 

2837 

2838def MakeHashXRef(symbol, tag): 

2839 text = symbol 

2840 

2841 # Check for things like '#include', '#define', and skip them. 

2842 if symbol in PreProcessorDirectives: 

2843 return "#%s" % symbol 

2844 

2845 # Get rid of special suffixes ('-struct','-enum'). 

2846 text = re.sub(r'-struct$', '', text) 

2847 text = re.sub(r'-enum$', '', text) 

2848 

2849 # If the symbol is in the form "Object::signal", then change the symbol to 

2850 # "Object-signal" and use "signal" as the text. 

2851 if '::' in symbol: 

2852 o, s = symbol.split('::', 1) 

2853 symbol = '%s-%s' % (o, s) 

2854 text = u'“' + s + u'”' 

2855 

2856 # If the symbol is in the form "Object:property", then change the symbol to 

2857 # "Object--property" and use "property" as the text. 

2858 if ':' in symbol: 

2859 o, p = symbol.split(':', 1) 

2860 symbol = '%s--%s' % (o, p) 

2861 text = u'“' + p + u'”' 

2862 

2863 if tag != '': 

2864 text = tagify(text, tag) 

2865 

2866 return MakeXRef(symbol, text) 

2867 

2868 

2869def ModifyXMLElements(text, symbol, start_tag_regexp, end_tag_func, callback): 

2870 """Rewrite XML blocks. 

2871 

2872 Looks for given XML element tags within the text, and calls 

2873 the callback on pieces of text inside & outside those elements. 

2874 Used for special handling of text inside things like CDATA 

2875 and <programlisting>. 

2876 

2877 Args: 

2878 text (str): the text. 

2879 symbol (str): the symbol currently being documented (only used for 

2880 error messages). 

2881 start_tag_regexp (str): the regular expression to match start tags. 

2882 e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to 

2883 match CDATA sections or programlisting elements. 

2884 end_tag_func (func): function which is passed the matched start tag 

2885 and should return the appropriate end tag string 

2886 regexp. 

2887 callback - callback called with each part of the text. It is 

2888 called with a piece of text, the symbol being 

2889 documented, and the matched start tag or '' if the text 

2890 is outside the XML elements being matched. 

2891 

2892 Returns: 

2893 str: modified text 

2894 """ 

2895 before_tag = start_tag = end_tag_regexp = end_tag = None 

2896 result = '' 

2897 

2898 logging.debug('modify xml for symbol: %s, regex: %s, text: [%s]', symbol, start_tag_regexp, text) 

2899 

2900 m = re.search(start_tag_regexp, text, flags=re.S) 

2901 while m: 

2902 before_tag = text[:m.start()] # Prematch for last successful match string 

2903 start_tag = m.group(0) # Last successful match 

2904 text = text[m.end():] # Postmatch for last successful match string 

2905 # get the matching end-tag for current tag 

2906 end_tag_regexp = end_tag_func(start_tag) 

2907 

2908 logging.debug('symbol: %s matched start: %s, end_tag: %s, text: [%s]', symbol, start_tag, end_tag_regexp, text) 

2909 

2910 logging.debug('converting before tag: [%s]', before_tag) 

2911 result += callback(before_tag, symbol, '') 

2912 result += start_tag 

2913 

2914 m2 = re.search(end_tag_regexp, text, flags=re.S) 

2915 if m2: 

2916 before_tag = text[:m2.start()] 

2917 end_tag = m2.group(0) 

2918 text = text[m2.end():] 

2919 

2920 logging.debug('symbol: %s matched end %s: text: [%s]', symbol, end_tag, text) 

2921 

2922 result += callback(before_tag, symbol, start_tag) 

2923 result += end_tag 

2924 else: 

2925 common.LogWarning(*GetSymbolSourceLocation(symbol), 

2926 "Can't find tag end: %s in docs for: %s." % (end_tag_regexp, symbol)) 

2927 # Just assume it is all inside the tag. 

2928 result += callback(text, symbol, start_tag) 

2929 text = '' 

2930 m = re.search(start_tag_regexp, text, flags=re.S) 

2931 

2932 # Handle any remaining text outside the tags. 

2933 logging.debug('converting after tag: [%s]', text) 

2934 result += callback(text, symbol, '') 

2935 logging.debug('results for symbol: %s, text: [%s]', symbol, result) 

2936 

2937 return result 

2938 

2939 

2940def tagify(text, elem): 

2941 # Adds a tag around some text. 

2942 # e.g tagify("Text", "literal") => "<literal>Text</literal>". 

2943 return '<' + elem + '>' + text + '</' + elem + '>' 

2944 

2945 

2946def MakeDocHeader(tag): 

2947 """Builds a docbook header for the given tag. 

2948 

2949 Args: 

2950 tag (str): doctype tag 

2951 

2952 Returns: 

2953 str: the docbook header 

2954 """ 

2955 header = re.sub(r'<!DOCTYPE \w+', r'<!DOCTYPE ' + tag, doctype_header) 

2956 # fix the path for book since this is one level up 

2957 if tag == 'book': 

2958 header = re.sub( 

2959 r'<!ENTITY % gtkdocentities SYSTEM "../([a-zA-Z./]+)">', r'<!ENTITY % gtkdocentities SYSTEM "\1">', header) 

2960 return header 

2961 

2962 

2963def MakeXRef(symbol, text=None): 

2964 """This returns a cross-reference link to the given symbol. 

2965 

2966 Though it doesn't try to do this for a few standard C types that it knows 

2967 won't be in the documentation. 

2968 

2969 Args: 

2970 symbol (str): the symbol to try to create a XRef to. 

2971 text (str): text to put inside the XRef, defaults to symbol 

2972 

2973 Returns: 

2974 str: a docbook link 

2975 """ 

2976 symbol = symbol.strip() 

2977 if not text: 

2978 text = symbol 

2979 

2980 # Get rid of special suffixes ('-struct','-enum'). 

2981 text = re.sub(r'-struct$', '', text) 

2982 text = re.sub(r'-enum$', '', text) 

2983 

2984 if ' ' in symbol: 

2985 return text 

2986 

2987 logging.info("Getting type link for %s -> %s", symbol, text) 

2988 

2989 symbol_id = common.CreateValidSGMLID(symbol) 

2990 return "<link linkend=\"%s\">%s</link>" % (symbol_id, text) 

2991 

2992 

2993def MakeIndexterms(symbol, sid): 

2994 """This returns a indexterm elements for the given symbol 

2995 

2996 Args: 

2997 symbol (str): the symbol to create indexterms for 

2998 

2999 Returns: 

3000 str: doxbook index terms 

3001 """ 

3002 terms = '' 

3003 sortas = '' 

3004 

3005 # make the index useful, by omitting the namespace when sorting 

3006 if NAME_SPACE != '': 

3007 m = re.search(r'^%s\_?(.*)' % NAME_SPACE, symbol, flags=re.I) 

3008 if m: 

3009 sortas = ' sortas="%s"' % m.group(1) 

3010 

3011 if symbol in Deprecated: 

3012 terms += "<indexterm zone=\"%s\" role=\"deprecated\"><primary%s>%s</primary></indexterm>" % ( 

3013 sid, sortas, symbol) 

3014 IndexEntriesDeprecated[symbol] = sid 

3015 IndexEntriesFull[symbol] = sid 

3016 if symbol in Since: 

3017 since = Since[symbol].strip() 

3018 if since != '': 

3019 terms += "<indexterm zone=\"%s\" role=\"%s\"><primary%s>%s</primary></indexterm>" % ( 

3020 sid, since, sortas, symbol) 

3021 IndexEntriesSince[symbol] = sid 

3022 IndexEntriesFull[symbol] = sid 

3023 if terms == '': 

3024 terms += "<indexterm zone=\"%s\"><primary%s>%s</primary></indexterm>" % (sid, sortas, symbol) 

3025 IndexEntriesFull[symbol] = sid 

3026 return terms 

3027 

3028 

3029def MakeDeprecationNote(symbol): 

3030 """This returns a deprecation warning for the given symbol. 

3031 

3032 Args: 

3033 symbol (str): the symbol to try to create a warning for. 

3034 

3035 Returns: 

3036 str: formatted warning or empty string if symbol is not deprecated 

3037 """ 

3038 desc = '' 

3039 if symbol in Deprecated: 

3040 desc += "<warning><para><literal>%s</literal> " % symbol 

3041 note = Deprecated[symbol] 

3042 

3043 m = re.search(r'^\s*([0-9\.]+)\s*:?', note) 

3044 if m: 

3045 desc += "has been deprecated since version %s and should not be used in newly-written code.</para>" % m.group( 

3046 1) 

3047 else: 

3048 desc += "is deprecated and should not be used in newly-written code.</para>" 

3049 

3050 note = re.sub(r'^\s*([0-9\.]+)\s*:?\s*', '', note) 

3051 note = note.strip() 

3052 

3053 if note != '': 

3054 note = ConvertMarkDown(symbol, note) 

3055 desc += " " + note 

3056 

3057 desc += "</warning>\n" 

3058 

3059 return desc 

3060 

3061 

3062def MakeConditionDescription(symbol): 

3063 """This returns a summary of conditions for the given symbol. 

3064 

3065 Args: 

3066 symbol (str): the symbol to create the summary for. 

3067 

3068 Returns: 

3069 str: formatted text or empty string if no special conditions apply. 

3070 """ 

3071 desc = '' 

3072 if symbol in Deprecated: 

3073 if desc != '': 

3074 desc += "|" 

3075 m = re.search(r'^\s*(.*?)\s*$', Deprecated[symbol]) 

3076 if m: 

3077 desc += "deprecated:%s" % m.group(1) 

3078 else: 

3079 desc += "deprecated" 

3080 

3081 if symbol in Since: 

3082 if desc != '': 

3083 desc += "|" 

3084 m = re.search(r'^\s*(.*?)\s*$', Since[symbol]) 

3085 if m: 

3086 desc += "since:%s" % m.group(1) 

3087 else: 

3088 desc += "since" 

3089 

3090 if symbol in StabilityLevel: 

3091 if desc != '': 

3092 desc += "|" 

3093 

3094 desc += "stability:" + StabilityLevel[symbol] 

3095 

3096 if desc != '': 

3097 cond = re.sub(r'"', r'&quot;', desc) 

3098 desc = ' condition=\"%s\"' % cond 

3099 logging.info("condition for '%s' = '%s'", symbol, desc) 

3100 

3101 return desc 

3102 

3103 

3104def GetHierarchy(gobject, hierarchy): 

3105 """Generate the object inheritance graph. 

3106 

3107 Returns the DocBook output describing the ancestors and 

3108 immediate children of a GObject subclass. It uses the 

3109 global Objects and ObjectLevels arrays to walk the tree. 

3110 

3111 Args: 

3112 object (str): the GtkObject subclass. 

3113 hierarchy (list) - previous hierarchy 

3114 

3115 Returns: 

3116 list: lines of docbook describing the hierarchy 

3117 """ 

3118 # Find object in the objects array. 

3119 found = False 

3120 children = [] 

3121 level = 0 

3122 j = 0 

3123 for i in range(len(Objects)): 

3124 if found: 

3125 if ObjectLevels[i] <= level: 

3126 break 

3127 

3128 elif ObjectLevels[i] == level + 1: 

3129 children.append(Objects[i]) 

3130 

3131 elif Objects[i] == gobject: 

3132 found = True 

3133 j = i 

3134 level = ObjectLevels[i] 

3135 

3136 if not found: 

3137 return hierarchy 

3138 

3139 logging.info("=== Hierarchy for: %s (%d existing entries) ===", gobject, len(hierarchy)) 

3140 

3141 # Walk up the hierarchy, pushing ancestors onto the ancestors array. 

3142 ancestors = [gobject] 

3143 logging.info("Level: %s", level) 

3144 while level > 1: 

3145 j -= 1 

3146 if ObjectLevels[j] < level: 

3147 ancestors.append(Objects[j]) 

3148 level = ObjectLevels[j] 

3149 logging.info("Level: %s", level) 

3150 

3151 # Output the ancestors, indented and with links. 

3152 logging.info('%d ancestors', len(ancestors)) 

3153 last_index = 0 

3154 level = 1 

3155 for i in range(len(ancestors) - 1, -1, -1): 

3156 ancestor = ancestors[i] 

3157 ancestor_id = common.CreateValidSGMLID(ancestor) 

3158 indent = ' ' * (level * 4) 

3159 # Don't add a link to the current object, i.e. when i == 0. 

3160 if i > 0: 

3161 entry_text = indent + "<link linkend=\"%s\">%s</link>" % (ancestor_id, ancestor) 

3162 alt_text = indent + ancestor 

3163 else: 

3164 entry_text = indent + ancestor 

3165 alt_text = indent + "<link linkend=\"%s\">%s</link>" % (ancestor_id, ancestor) 

3166 

3167 logging.info("Checking for '%s' or '%s'", entry_text, alt_text) 

3168 # Check if we already have this object 

3169 index = -1 

3170 for j in range(len(hierarchy)): 

3171 if hierarchy[j] == entry_text or (hierarchy[j] == alt_text): 

3172 index = j 

3173 break 

3174 if index == -1: 

3175 # We have a new entry, find insert position in alphabetical order 

3176 found = False 

3177 for j in range(last_index, len(hierarchy)): 

3178 if not re.search(r'^' + indent, hierarchy[j]): 

3179 last_index = j 

3180 found = True 

3181 break 

3182 elif re.search(r'^%s[^ ]' % indent, hierarchy[j]): 

3183 stripped_text = hierarchy[j] 

3184 if r'<link linkend' not in entry_text: 

3185 stripped_text = re.sub(r'<link linkend="[A-Za-z]*">', '', stripped_text) 

3186 stripped_text = re.sub(r'</link>', '', stripped_text) 

3187 

3188 if entry_text < stripped_text: 

3189 last_index = j 

3190 found = True 

3191 break 

3192 

3193 # Append to bottom 

3194 if not found: 

3195 last_index = len(hierarchy) 

3196 

3197 logging.debug('insert at %d: %s', last_index, entry_text) 

3198 hierarchy.insert(last_index, entry_text) 

3199 last_index += 1 

3200 else: 

3201 # Already have this one, make sure we use the not linked version 

3202 if r'<link linkend' not in entry_text: 

3203 hierarchy[j] = entry_text 

3204 

3205 # Remember index as base insert point 

3206 last_index = index + 1 

3207 

3208 level += 1 

3209 

3210 # Output the children, indented and with links. 

3211 logging.info('%d children', len(children)) 

3212 for i in range(len(children)): 

3213 sid = common.CreateValidSGMLID(children[i]) 

3214 indented_text = ' ' * (level * 4) + "<link linkend=\"%s\">%s</link>" % (sid, children[i]) 

3215 logging.debug('insert at %d: %s', last_index, indented_text) 

3216 hierarchy.insert(last_index, indented_text) 

3217 last_index += 1 

3218 return hierarchy 

3219 

3220 

3221def GetInterfaces(gobject): 

3222 """Generate interface implementation graph. 

3223 

3224 Returns the DocBook output describing the interfaces 

3225 implemented by a class. It uses the global Interfaces hash. 

3226 

3227 Args: 

3228 object (str): the GObject subclass. 

3229 

3230 Returns: 

3231 str: implemented interfaces 

3232 """ 

3233 text = '' 

3234 # Find object in the objects array. 

3235 if gobject in Interfaces: 

3236 ifaces = Interfaces[gobject].split() 

3237 text = '''<para> 

3238%s implements 

3239''' % gobject 

3240 count = len(ifaces) 

3241 for i in range(count): 

3242 sid = common.CreateValidSGMLID(ifaces[i]) 

3243 text += " <link linkend=\"%s\">%s</link>" % (sid, ifaces[i]) 

3244 if i < count - 2: 

3245 text += ', ' 

3246 elif i < count - 1: 

3247 text += ' and ' 

3248 else: 

3249 text += '.' 

3250 text += '</para>\n' 

3251 return text 

3252 

3253 

3254def GetImplementations(gobject): 

3255 """Generate interface usage graph. 

3256 

3257 Returns the DocBook output describing the implementations 

3258 of an interface. It uses the global Interfaces hash. 

3259 

3260 Args: 

3261 object (str): the GObject subclass. 

3262 

3263 Returns: 

3264 str: interface implementations 

3265 """ 

3266 text = '' 

3267 impls = [] 

3268 for key in Interfaces: 

3269 if re.search(r'\b%s\b' % gobject, Interfaces[key]): 

3270 impls.append(key) 

3271 

3272 count = len(impls) 

3273 if count > 0: 

3274 impls.sort() 

3275 text = '''<para> 

3276%s is implemented by 

3277''' % gobject 

3278 for i in range(count): 

3279 sid = common.CreateValidSGMLID(impls[i]) 

3280 text += " <link linkend=\"%s\">%s</link>" % (sid, impls[i]) 

3281 if i < count - 2: 

3282 text += ', ' 

3283 elif i < count - 1: 

3284 text += ' and ' 

3285 else: 

3286 text += '.' 

3287 text += '</para>\n' 

3288 return text 

3289 

3290 

3291def GetPrerequisites(iface): 

3292 """Generates interface requirements. 

3293 

3294 Returns the DocBook output describing the prerequisites 

3295 of an interface. It uses the global Prerequisites hash. 

3296 Args: 

3297 iface (str): the interface. 

3298 

3299 Returns: 

3300 str: required interfaces 

3301 """ 

3302 

3303 text = '' 

3304 if iface in Prerequisites: 

3305 text = '''<para> 

3306%s requires 

3307''' % iface 

3308 prereqs = Prerequisites[iface].split() 

3309 count = len(prereqs) 

3310 for i in range(count): 

3311 sid = common.CreateValidSGMLID(prereqs[i]) 

3312 text += " <link linkend=\"%s\">%s</link>" % (sid, prereqs[i]) 

3313 if i < count - 2: 

3314 text += ', ' 

3315 elif i < count - 1: 

3316 text += ' and ' 

3317 else: 

3318 text += '.' 

3319 text += '</para>\n' 

3320 return text 

3321 

3322 

3323def GetDerived(iface): 

3324 """ 

3325 Returns the DocBook output describing the derived interfaces 

3326 of an interface. It uses the global %Prerequisites hash. 

3327 

3328 Args: 

3329 iface (str): the interface. 

3330 

3331 Returns: 

3332 str: derived interfaces 

3333 """ 

3334 text = '' 

3335 derived = [] 

3336 for key in Prerequisites: 

3337 if re.search(r'\b%s\b' % iface, Prerequisites[key]): 

3338 derived.append(key) 

3339 

3340 count = len(derived) 

3341 if count > 0: 

3342 derived.sort() 

3343 text = '''<para> 

3344%s is required by 

3345''' % iface 

3346 for i in range(count): 

3347 sid = common.CreateValidSGMLID(derived[i]) 

3348 text += " <link linkend=\"%s\">%s</link>" % (sid, derived[i]) 

3349 if i < count - 2: 

3350 text += ', ' 

3351 elif i < count - 1: 

3352 text += ' and ' 

3353 else: 

3354 text += '.' 

3355 text += '</para>\n' 

3356 return text 

3357 

3358 

3359def GetSignals(gobject): 

3360 """Generate signal docs. 

3361 

3362 Returns the synopsis and detailed description DocBook output 

3363 for the signal handlers of a given GObject subclass. 

3364 

3365 Args: 

3366 object (str): the GObject subclass, e.g. 'GtkButton'. 

3367 

3368 Returns: 

3369 str: signal docs 

3370 """ 

3371 synop = '' 

3372 desc = '' 

3373 

3374 for i in range(len(SignalObjects)): 

3375 if SignalObjects[i] == gobject: 

3376 logging.info("Found signal: %s", SignalNames[i]) 

3377 name = SignalNames[i] 

3378 symbol = '%s::%s' % (gobject, name) 

3379 sid = common.CreateValidSGMLID('%s-%s' % (gobject, name)) 

3380 

3381 desc += u"<refsect2 id=\"%s\" role=\"signal\"><title>The <literal>“%s”</literal> signal</title>\n" % ( 

3382 sid, name) 

3383 desc += MakeIndexterms(symbol, sid) 

3384 desc += "\n" 

3385 desc += OutputSymbolExtraLinks(symbol) 

3386 

3387 desc += "<programlisting language=\"C\">" 

3388 

3389 m = re.search(r'\s*(const\s+)?(\w+)\s*(\**)', SignalReturns[i]) 

3390 type_modifier = m.group(1) or '' 

3391 gtype = m.group(2) 

3392 pointer = m.group(3) 

3393 xref = MakeXRef(gtype, tagify(gtype, "returnvalue")) 

3394 

3395 ret_type_output = '%s%s%s' % (type_modifier, xref, pointer) 

3396 callback_name = "user_function" 

3397 desc += '%s\n%s (' % (ret_type_output, callback_name) 

3398 

3399 indentation = ' ' * (len(callback_name) + 2) 

3400 

3401 sourceparams = SourceSymbolParams.get(symbol) 

3402 sourceparam_names = None 

3403 if sourceparams: 

3404 sourceparam_names = list(sourceparams) # keys as list 

3405 params = SignalPrototypes[i].splitlines() 

3406 type_len = len("gpointer") 

3407 name_len = len("user_data") 

3408 # do two passes, the first one is to calculate padding 

3409 for l in range(2): 

3410 for j in range(len(params)): 

3411 param_name = None 

3412 # allow alphanumerics, '_', '[' & ']' in param names 

3413 m = re.search(r'^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$', params[j]) 

3414 if m: 

3415 gtype = m.group(1) 

3416 pointer = m.group(2) 

3417 if sourceparam_names: 

3418 if j < len(sourceparam_names): 

3419 param_name = sourceparam_names[j] 

3420 logging.info('from sourceparams: "%s" (%d: %s)', param_name, j, params[j]) 

3421 # we're missing the docs for this param, don't warn here though 

3422 else: 

3423 param_name = m.group(3) 

3424 logging.info('from params: "%s" (%d: %s)', param_name, j, params[j]) 

3425 

3426 if not param_name: 

3427 param_name = "arg%d" % j 

3428 

3429 if l == 0: 

3430 if len(gtype) + len(pointer) > type_len: 

3431 type_len = len(gtype) + len(pointer) 

3432 if len(param_name) > name_len: 

3433 name_len = len(param_name) 

3434 else: 

3435 logging.info("signal arg[%d]: '%s'", j, param_name) 

3436 xref = MakeXRef(gtype, tagify(gtype, "type")) 

3437 pad = ' ' * (type_len - len(gtype) - len(pointer)) 

3438 desc += '%s%s %s%s,\n' % (xref, pad, pointer, param_name) 

3439 desc += indentation 

3440 

3441 else: 

3442 common.LogWarning(*GetSymbolSourceLocation(symbol), 

3443 "Can't parse arg: %s\nArgs:%s" % (params[j], SignalPrototypes[i])) 

3444 

3445 xref = MakeXRef("gpointer", tagify("gpointer", "type")) 

3446 pad = ' ' * (type_len - len("gpointer")) 

3447 desc += '%s%s user_data)' % (xref, pad) 

3448 desc += "</programlisting>\n" 

3449 

3450 flags = SignalFlags[i] 

3451 flags_string = '' 

3452 if flags: 

3453 if 'f' in flags: 

3454 flags_string = "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>" 

3455 

3456 elif 'l' in flags: 

3457 flags_string = "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>" 

3458 

3459 elif 'c' in flags: 

3460 flags_string = "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>" 

3461 flags_string = "Cleanup" 

3462 

3463 if 'r' in flags: 

3464 if flags_string: 

3465 flags_string += " / " 

3466 flags_string = "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>" 

3467 

3468 if 'd' in flags: 

3469 if flags_string: 

3470 flags_string += " / " 

3471 flags_string = "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>" 

3472 

3473 if 'a' in flags: 

3474 if flags_string: 

3475 flags_string += " / " 

3476 flags_string = "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>" 

3477 

3478 if 'h' in flags: 

3479 if flags_string: 

3480 flags_string += " / " 

3481 flags_string = "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>" 

3482 

3483 synop += "<row><entry role=\"signal_type\">%s</entry><entry role=\"signal_name\"><link linkend=\"%s\">%s</link></entry><entry role=\"signal_flags\">%s</entry></row>\n" % ( 

3484 ret_type_output, sid, name, flags_string) 

3485 

3486 parameters = OutputParamDescriptions("SIGNAL", symbol, None) 

3487 logging.info("formatted signal params: '%s' -> '%s'", symbol, parameters) 

3488 

3489 AllSymbols[symbol] = 1 

3490 if symbol in SymbolDocs: 

3491 symbol_docs = ConvertMarkDown(symbol, SymbolDocs[symbol]) 

3492 

3493 desc += symbol_docs 

3494 

3495 if not IsEmptyDoc(SymbolDocs[symbol]): 

3496 AllDocumentedSymbols[symbol] = 1 

3497 

3498 if symbol in SymbolAnnotations: 

3499 param_desc = SymbolAnnotations[symbol] 

3500 param_desc, param_annotations = ExpandAnnotation(symbol, param_desc) 

3501 if param_annotations != '': 

3502 desc += "\n<para>%s</para>" % param_annotations 

3503 

3504 desc += MakeDeprecationNote(symbol) 

3505 

3506 desc += parameters 

3507 if flags_string: 

3508 desc += "<para>Flags: %s</para>\n" % flags_string 

3509 

3510 desc += OutputSymbolTraits(symbol) 

3511 desc += "</refsect2>" 

3512 

3513 return (synop, desc) 

3514 

3515 

3516def GetArgs(gobject): 

3517 """Generate property docs. 

3518 

3519 Returns the synopsis and detailed description DocBook output 

3520 for the Args of a given GtkObject subclass. 

3521 

3522 Args: 

3523 object (str): the GObject subclass, e.g. 'GtkButton'. 

3524 

3525 Returns: 

3526 str: property docs 

3527 """ 

3528 synop = '' 

3529 desc = '' 

3530 child_synop = '' 

3531 child_desc = '' 

3532 style_synop = '' 

3533 style_desc = '' 

3534 

3535 for i in range(len(ArgObjects)): 

3536 if ArgObjects[i] == gobject: 

3537 logging.info("Found arg: %s", ArgNames[i]) 

3538 name = ArgNames[i] 

3539 flags = ArgFlags[i] 

3540 flags_string = '' 

3541 kind = '' 

3542 id_sep = '' 

3543 

3544 if 'c' in flags: 

3545 kind = "child property" 

3546 id_sep = "c-" 

3547 elif 's' in flags: 

3548 kind = "style property" 

3549 id_sep = "s-" 

3550 else: 

3551 kind = "property" 

3552 

3553 # Remember only one colon so we don't clash with signals. 

3554 symbol = '%s:%s' % (gobject, name) 

3555 # use two dashes and ev. an extra separator here for the same reason. 

3556 sid = common.CreateValidSGMLID('%s--%s%s' % (gobject, id_sep, name)) 

3557 

3558 atype = ArgTypes[i] 

3559 type_output = None 

3560 arange = ArgRanges[i] 

3561 range_output = CreateValidSGML(arange) 

3562 default = ArgDefaults[i] 

3563 default_output = CreateValidSGML(default) 

3564 

3565 if atype == "GtkString": 

3566 atype = "char&#160;*" 

3567 

3568 if atype == "GtkSignal": 

3569 atype = "GtkSignalFunc, gpointer" 

3570 type_output = MakeXRef("GtkSignalFunc") + ", " + MakeXRef("gpointer") 

3571 elif re.search(r'^(\w+)\*$', atype): 

3572 m = re.search(r'^(\w+)\*$', atype) 

3573 type_output = MakeXRef(m.group(1), tagify(m.group(1), "type")) + "&#160;*" 

3574 else: 

3575 type_output = MakeXRef(atype, tagify(atype, "type")) 

3576 

3577 if 'r' in flags: 

3578 flags_string = "Read" 

3579 

3580 if 'w' in flags: 

3581 if flags_string: 

3582 flags_string += " / " 

3583 flags_string += "Write" 

3584 

3585 if 'x' in flags: 

3586 if flags_string: 

3587 flags_string += " / " 

3588 flags_string += "Construct" 

3589 

3590 if 'X' in flags: 

3591 if flags_string: 

3592 flags_string += " / " 

3593 flags_string += "Construct&#160;Only" 

3594 

3595 AllSymbols[symbol] = 1 

3596 blurb = '' 

3597 if symbol in SymbolDocs and not IsEmptyDoc(SymbolDocs[symbol]): 

3598 blurb = ConvertMarkDown(symbol, SymbolDocs[symbol]) 

3599 logging.info(".. [%s][%s]", SymbolDocs[symbol], blurb) 

3600 AllDocumentedSymbols[symbol] = 1 

3601 

3602 else: 

3603 if ArgBlurbs[i] != '': 

3604 blurb = "<para>" + CreateValidSGML(ArgBlurbs[i]) + "</para>" 

3605 AllDocumentedSymbols[symbol] = 1 

3606 else: 

3607 # FIXME: print a warning? 

3608 logging.info(".. no description") 

3609 

3610 pad1 = '' 

3611 if len(name) < 24: 

3612 pad1 = " " * (24 - len(name)) 

3613 

3614 arg_synop = "<row><entry role=\"property_type\">%s</entry><entry role=\"property_name\"><link linkend=\"%s\">%s</link></entry><entry role=\"property_flags\">%s</entry></row>\n" % ( 

3615 type_output, sid, name, flags_string) 

3616 arg_desc = u"<refsect2 id=\"%s\" role=\"property\"><title>The <literal>“%s”</literal> %s</title>\n" % ( 

3617 sid, name, kind) 

3618 arg_desc += MakeIndexterms(symbol, sid) 

3619 arg_desc += "\n" 

3620 arg_desc += OutputSymbolExtraLinks(symbol) 

3621 

3622 arg_desc += u"<programlisting> “%s”%s %s</programlisting>\n" % (name, pad1, type_output) 

3623 arg_desc += blurb 

3624 if symbol in SymbolAnnotations: 

3625 param_desc = SymbolAnnotations[symbol] 

3626 param_desc, param_annotations = ExpandAnnotation(symbol, param_desc) 

3627 if param_annotations != '': 

3628 arg_desc += "\n<para>%s</para>" % param_annotations 

3629 

3630 arg_desc += MakeDeprecationNote(symbol) 

3631 

3632 arg_desc += "<para>Owner: %s</para>\n" % gobject 

3633 

3634 if flags_string: 

3635 arg_desc += "<para>Flags: %s</para>\n" % flags_string 

3636 

3637 if arange != '': 

3638 arg_desc += "<para>Allowed values: %s</para>\n" % range_output 

3639 

3640 if default != '': 

3641 arg_desc += "<para>Default value: %s</para>\n" % default_output 

3642 

3643 arg_desc += OutputSymbolTraits(symbol) 

3644 arg_desc += "</refsect2>\n" 

3645 

3646 if 'c' in flags: 

3647 child_synop += arg_synop 

3648 child_desc += arg_desc 

3649 

3650 elif 's' in flags: 

3651 style_synop += arg_synop 

3652 style_desc += arg_desc 

3653 

3654 else: 

3655 synop += arg_synop 

3656 desc += arg_desc 

3657 

3658 return (synop, child_synop, style_synop, desc, child_desc, style_desc) 

3659 

3660 

3661def GetActions(gobject): 

3662 """Generate action docs. 

3663 

3664 Returns the synopsis and detailed description DocBook output 

3665 for the actions of a given GtkWidget subclass. 

3666 

3667 Args: 

3668 object (str): the GObject subclass, e.g. 'GtkButton'. 

3669 

3670 Returns: 

3671 str: action docs 

3672 """ 

3673 synop = '' 

3674 desc = '' 

3675 

3676 for i in range(len(ActionObjects)): 

3677 if ActionObjects[i] == gobject: 

3678 logging.info("Found action: %s", ActionNames[i]) 

3679 name = ActionNames[i] 

3680 params = ActionParams[i] 

3681 prop = ActionProperties[i] 

3682 

3683 # Remember: pipe, so we don't clash with signals. 

3684 symbol = '%s|%s' % (gobject, name) 

3685 sid = common.CreateValidSGMLID(symbol) 

3686 

3687 AllSymbols[symbol] = 1 

3688 blurb = '' 

3689 if symbol in SymbolDocs and not IsEmptyDoc(SymbolDocs[symbol]): 

3690 blurb = ConvertMarkDown(symbol, SymbolDocs[symbol]) 

3691 logging.info(".. [%s][%s]", SymbolDocs[symbol], blurb) 

3692 AllDocumentedSymbols[symbol] = 1 

3693 

3694 else: 

3695 # FIXME: print a warning? 

3696 logging.info(".. no description") 

3697 

3698 pad1 = '' 

3699 if len(name) < 24: 

3700 pad1 = " " * (24 - len(name)) 

3701 

3702 action_synop = "<row><entry></entry><entry role=\"action_name\"><link linkend=\"%s\">%s</link></entry><entry role=\"parameter_type\">%s</entry></row>\n" % ( 

3703 sid, name, params) 

3704 action_desc = u"<refsect2 id=\"%s\" role=\"action\"><title>The <literal>“%s”</literal> action</title>\n" % ( 

3705 sid, name) 

3706 action_desc += MakeIndexterms(symbol, sid) 

3707 action_desc += "\n" 

3708 action_desc += OutputSymbolExtraLinks(symbol) 

3709 if blurb != '': 

3710 action_desc += blurb 

3711 elif prop != '': 

3712 action_desc += "<para>The %s action sets the %s property.</para>\n" % (name, MakeHashXRef (gobject + ':' + prop, "type")) 

3713 action_desc += MakeDeprecationNote(symbol) 

3714 

3715 if params != '': 

3716 action_desc += "<para>Parameter type: %s</para>\n" % params 

3717 

3718 action_desc += OutputParamDescriptions("ACTION", symbol, None) 

3719 action_desc += OutputSymbolTraits(symbol) 

3720 action_desc += "</refsect2>\n" 

3721 

3722 synop += action_synop 

3723 desc += action_desc 

3724 

3725 return (synop, desc) 

3726 

3727def IgnorePath(path, source_dirs, ignore_files): 

3728 for sdir in source_dirs: 

3729 # Cut off base directory 

3730 m1 = re.search(r'^%s/(.*)$' % re.escape(sdir), path) 

3731 if m1: 

3732 # Check if the filename is in the ignore list. 

3733 m2 = re.search(r'(\s|^)%s(\s|$)' % re.escape(m1.group(1)), ignore_files) 

3734 if m2: 

3735 logging.info("Skipping path: %s", path) 

3736 return True 

3737 else: 

3738 logging.info("No match for: %s", m1.group(1)) 

3739 else: 

3740 logging.info("No match for: %s", path) 

3741 return False 

3742 

3743 

3744def ReadSourceDocumentation(source_dir, suffix_list, source_dirs, ignore_files): 

3745 """Read the documentation embedded in comment blocks in the source code. 

3746 

3747 It recursively descends the source directory looking for source files and 

3748 scans them looking for specially-formatted comment blocks. 

3749 

3750 Args: 

3751 source_dir (str): the directory to scan. 

3752 suffix_list (list): extensions to check 

3753 """ 

3754 if IgnorePath(source_dir, source_dirs, ignore_files): 

3755 return 

3756 

3757 logging.info("Scanning source directory: %s", source_dir) 

3758 

3759 # This array holds any subdirectories found. 

3760 subdirs = [] 

3761 

3762 for ifile in sorted(os.listdir(source_dir)): 

3763 logging.debug("... : %s", ifile) 

3764 if ifile.startswith('.'): 

3765 continue 

3766 fname = os.path.join(source_dir, ifile) 

3767 if os.path.isdir(fname): 

3768 subdirs.append(fname) 

3769 else: 

3770 for suffix in suffix_list: 

3771 if ifile.endswith(suffix): 

3772 if not IgnorePath(fname, source_dirs, ignore_files): 

3773 ScanSourceFile(fname, ignore_files) 

3774 break 

3775 

3776 # Now recursively scan the subdirectories. 

3777 for sdir in subdirs: 

3778 ReadSourceDocumentation(sdir, suffix_list, source_dirs, ignore_files) 

3779 

3780 

3781def ScanSourceFile(ifile, ignore_files): 

3782 """Scans one source file looking for specially-formatted comment blocks. 

3783 

3784 Later MergeSourceDocumentation() is copying over the doc blobs that are not 

3785 suppressed/ignored. 

3786 

3787 Args: 

3788 ifile (str): the file to scan. 

3789 """ 

3790 m = re.search(r'^.*[\/\\]([^\/\\]*)$', ifile) 

3791 if m: 

3792 basename = m.group(1) 

3793 else: 

3794 common.LogWarning(ifile, 1, "Can't find basename for this filename.") 

3795 basename = ifile 

3796 

3797 # Check if the basename is in the list of files to ignore. 

3798 if re.search(r'(\s|^)%s(\s|$)' % re.escape(basename), ignore_files): 

3799 logging.info("Skipping source file: %s", ifile) 

3800 return 

3801 

3802 logging.info("Scanning %s", ifile) 

3803 

3804 with open(ifile, 'r', encoding='utf-8') as src: 

3805 input_lines = src.readlines() 

3806 

3807 for c in ScanSourceContent(input_lines, ifile): 

3808 ParseCommentBlock(c[0], c[1], ifile) 

3809 

3810 logging.info("Scanning %s done", ifile) 

3811 

3812 

3813def ScanSourceContent(input_lines, ifile=''): 

3814 """Scans the source code lines for specially-formatted comment blocks. 

3815 

3816 Updates global state in SourceSymbolDocs, SymbolSourceLocation, 

3817 Since, StabilityLevel, Deprecated, SymbolAnnotations. 

3818 

3819 Might read from global state in DeclarationTypes 

3820 

3821 Args: 

3822 input_lines (list): list of source code lines 

3823 ifile (str): file name of the source file (for reporting) 

3824 

3825 Returns: 

3826 list: tuples with comment block and its starting line 

3827 """ 

3828 comments = [] 

3829 in_comment_block = False 

3830 line_number = 0 

3831 comment = [] 

3832 starting_line = 0 

3833 for line in input_lines: 

3834 line_number += 1 

3835 

3836 if not in_comment_block: 

3837 # Look for the start of a comment block. 

3838 if re.search(r'^\s*/\*.*\*/', line): 

3839 # one-line comment - not gtkdoc 

3840 pass 

3841 elif re.search(r'^\s*/\*\*\s', line): 

3842 logging.info("Found comment block start") 

3843 

3844 in_comment_block = True 

3845 comment = [] 

3846 starting_line = line_number + 1 

3847 else: 

3848 # Look for end of comment 

3849 if re.search(r'^\s*\*+/', line): 

3850 comments.append((comment, starting_line)) 

3851 in_comment_block = False 

3852 continue 

3853 

3854 # Get rid of ' * ' at start of every line in the comment block. 

3855 line = re.sub(r'^\s*\*\s?', '', line) 

3856 # But make sure we don't get rid of the newline at the end. 

3857 if not line.endswith('\n'): 

3858 line += "\n" 

3859 

3860 logging.info("scanning :%s", line.strip()) 

3861 comment.append(line) 

3862 

3863 return comments 

3864 

3865 

3866def SegmentCommentBlock(lines, line_number=0, ifile=''): 

3867 """Cut a single comment block into segments. 

3868 

3869 Args: 

3870 lines (list): the comment block 

3871 line_number (int): the first line of the block (for reporting) 

3872 ifile (str): file name of the source file (for reporting) 

3873 

3874 Returns: 

3875 (str, dict): symbol name and dict of comment segments 

3876 """ 

3877 symbol = None 

3878 in_part = '' 

3879 segments = {'body': ''} 

3880 params = OrderedDict() 

3881 param_name = None 

3882 param_indent = None 

3883 line_number -= 1 

3884 for line in lines: 

3885 line_number += 1 

3886 logging.info("scanning[%s] :%s", in_part, line.strip()) 

3887 

3888 # If we haven't found the symbol name yet, look for it. 

3889 # We need to allow for the following cases: 

3890 # function: 

3891 # Class::signal: 

3892 # Class:property: 

3893 # Class|action: 

3894 # Signal and property names can contain dashes, action names 

3895 # can contain period. 

3896 # In all cases, there might be annotations in parentheses 

3897 # following the symbol name. 

3898 if not symbol: 

3899 m1 = re.search(r'^\s*((SECTION|PROGRAM):\s*\S+)', line) 

3900 m2 = re.search(r'^\s*([\w:.|-]*\w)\s*:?\s*(\(.+?\)\s*)*$', line) 

3901 if m1: 

3902 symbol = m1.group(1) 

3903 logging.info("docs found in source for : '%s'", symbol) 

3904 elif m2: 

3905 symbol = m2.group(1) 

3906 logging.info("docs found in source for : '%s'", symbol) 

3907 if m2.group(2): 

3908 annotation = m2.group(2).strip() 

3909 if annotation != '': 

3910 SymbolAnnotations[symbol] = annotation 

3911 logging.info("remaining text for %s: '%s'", symbol, annotation) 

3912 

3913 continue 

3914 

3915 if in_part == "body": 

3916 # Get rid of 'Description:' 

3917 line = re.sub(r'^\s*Description:', '', line) 

3918 

3919 m1 = re.search(r'^\s*(returns|return\s+value):', line, flags=re.I) 

3920 m2 = re.search(r'^\s*since:', line, flags=re.I) 

3921 m3 = re.search(r'^\s*deprecated:', line, flags=re.I) 

3922 m4 = re.search(r'^\s*stability:', line, flags=re.I) 

3923 

3924 if m1: 

3925 if in_part != '': 

3926 in_part = "return" 

3927 segments[in_part] = line[m1.end():] 

3928 continue 

3929 if m2: 

3930 if in_part != "param": 

3931 in_part = "since" 

3932 segments[in_part] = line[m2.end():] 

3933 continue 

3934 elif m3: 

3935 if in_part != "param": 

3936 in_part = "deprecated" 

3937 segments[in_part] = line[m3.end():] 

3938 continue 

3939 elif m4: 

3940 in_part = "stability" 

3941 segments[in_part] = line[m4.end():] 

3942 continue 

3943 

3944 if in_part in ["body", "return", "since", "stability", "deprecated"]: 

3945 segments[in_part] += line 

3946 continue 

3947 

3948 # We must be in the parameters. Check for the empty line below them. 

3949 if re.search(r'^\s*$', line): 

3950 # TODO: only switch if next line starts without indent? 

3951 in_part = "body" 

3952 continue 

3953 

3954 # Look for a parameter name. 

3955 m = re.search(r'^\s*@(.+?)\s*:\s*', line) 

3956 if m: 

3957 param_name = m.group(1) 

3958 param_desc = line[m.end():] 

3959 param_indent = None 

3960 

3961 # Allow varargs variations 

3962 if re.search(r'^\.\.\.$', param_name): 

3963 param_name = "..." 

3964 

3965 logging.info("Found param for symbol %s : '%s'= '%s'", symbol, param_name, line) 

3966 params[param_name] = param_desc 

3967 in_part = "param" 

3968 continue 

3969 elif in_part == '': 

3970 logging.info("continuation for %s annotation '%s'", symbol, line) 

3971 annotation = re.sub(r'^\s+|\s+$', '', line) 

3972 if symbol in SymbolAnnotations: 

3973 SymbolAnnotations[symbol] += annotation 

3974 else: 

3975 SymbolAnnotations[symbol] = annotation 

3976 continue 

3977 

3978 # We must be in the middle of a parameter description, so add it on 

3979 # to the last element in @params. 

3980 if not param_name: 

3981 common.LogWarning(ifile, line_number, 

3982 "Parsing comment block file : parameter expected, but got '%s'" % line) 

3983 else: 

3984 if not param_indent: 

3985 # determine indentation of first continuation line 

3986 spc = len(line) - len(line.lstrip(' ')) 

3987 if spc > 0: 

3988 param_indent = spc 

3989 logging.debug("Found param-indentation of %d", param_indent) 

3990 if param_indent: 

3991 # cut common indentation (after double checking that it is all spaces) 

3992 if line[:param_indent].strip() == '': 

3993 line = line[param_indent:] 

3994 else: 

3995 logging.warning("Not cutting param-indentation for %s: '%s'", 

3996 param_name, line[:param_indent]) 

3997 

3998 params[param_name] += line 

3999 

4000 return (symbol, segments, params) 

4001 

4002 

4003def ParseCommentBlockSegments(symbol, segments, params, line_number=0, ifile=''): 

4004 """Parse the comemnts block segments. 

4005 

4006 Args: 

4007 symbol (str): the symbol name 

4008 segments(dict): the comment block segments (except params) 

4009 parans (dict): the comment block params 

4010 line_number (int): the first line of the block (for reporting) 

4011 ifile (str): file name of the source file (for reporting) 

4012 

4013 """ 

4014 # Add the return value description to the end of the params. 

4015 if "return" in segments: 

4016 # TODO(ensonic): check for duplicated Return docs 

4017 # common.LogWarning(file, line_number, "Multiple Returns for %s." % symbol) 

4018 params['Returns'] = segments["return"] 

4019 

4020 # Convert special characters 

4021 segments["body"] = ConvertXMLChars(symbol, segments["body"]) 

4022 for (param_name, param_desc) in params.items(): 

4023 params[param_name] = ConvertXMLChars(symbol, param_desc) 

4024 

4025 # Handle Section docs 

4026 m = re.search(r'SECTION:\s*(.*)', symbol) 

4027 m2 = re.search(r'PROGRAM:\s*(.*)', symbol) 

4028 if m: 

4029 real_symbol = m.group(1) 

4030 

4031 logging.info("SECTION DOCS found in source for : '%s'", real_symbol) 

4032 for param_name, param_desc in params.items(): 

4033 logging.info(" '" + param_name + "'") 

4034 param_name = param_name.lower() 

4035 if param_name in ['image', 'include', 'section_id', 'see_also', 'short_description', 'stability', 'title']: 

4036 key = real_symbol + ':' + param_name 

4037 SourceSymbolDocs[key] = param_desc 

4038 SymbolSourceLocation[key] = (ifile, line_number) 

4039 

4040 key = real_symbol + ":long_description" 

4041 if key not in KnownSymbols or KnownSymbols[key] != 1: 

4042 common.LogWarning( 

4043 ifile, line_number, "Section %s is not defined in the %s-sections.txt file." % (real_symbol, MODULE)) 

4044 SourceSymbolDocs[key] = segments["body"] 

4045 SymbolSourceLocation[key] = (ifile, line_number) 

4046 elif m2: 

4047 real_symbol = m2.group(1) 

4048 section_id = None 

4049 

4050 logging.info("PROGRAM DOCS found in source for '%s'", real_symbol) 

4051 for param_name, param_desc in params.items(): 

4052 logging.info("PROGRAM key %s: '%s'", real_symbol, param_name) 

4053 param_name = param_name.lower() 

4054 if param_name in ['returns', 'section_id', 'see_also', 'short_description', 'synopsis']: 

4055 key = real_symbol + ':' + param_name 

4056 logging.info("PROGRAM value %s: '%s'", real_symbol, param_desc.rstrip()) 

4057 SourceSymbolDocs[key] = param_desc.rstrip() 

4058 SymbolSourceLocation[key] = (ifile, line_number) 

4059 elif re.search(r'^(-.*)', param_name): 

4060 logging.info("PROGRAM opts: '%s': '%s'", param_name, param_desc) 

4061 key = real_symbol + ":options" 

4062 opts = [] 

4063 opts_str = SourceSymbolDocs.get(key) 

4064 if opts_str: 

4065 opts = opts_str.split('\t') 

4066 opts.append(param_name) 

4067 opts.append(param_desc) 

4068 

4069 logging.info("Setting options for symbol: %s: '%s'", real_symbol, '\t'.join(opts)) 

4070 SourceSymbolDocs[key] = '\t'.join(opts) 

4071 

4072 key = real_symbol + ":long_description" 

4073 SourceSymbolDocs[key] = segments["body"] 

4074 SymbolSourceLocation[key] = (ifile, line_number) 

4075 

4076 # TODO(ensonic): we need to track these somehow and output the files 

4077 # later, see comment in Run() 

4078 section_id = SourceSymbolDocs.get(real_symbol + ":section_id") 

4079 if section_id and section_id.strip() != '': 

4080 # Remove trailing blanks and use as is 

4081 section_id = section_id.rstrip() 

4082 else: 

4083 section_id = common.CreateValidSGMLID('%s-%s' % (MODULE, real_symbol)) 

4084 OutputProgramDBFile(real_symbol, section_id) 

4085 

4086 else: 

4087 logging.info("SYMBOL DOCS found in source for : '%s'", symbol) 

4088 SourceSymbolDocs[symbol] = segments["body"] 

4089 SourceSymbolParams[symbol] = params 

4090 SymbolSourceLocation[symbol] = (ifile, line_number) 

4091 

4092 if "since" in segments: 

4093 arr = segments["since"].splitlines() 

4094 desc = arr[0].strip() 

4095 extra_lines = arr[1:] 

4096 logging.info("Since(%s) : [%s]", symbol, desc) 

4097 Since[symbol] = ConvertXMLChars(symbol, desc) 

4098 if len(extra_lines) > 1: 

4099 common.LogWarning(ifile, line_number, "multi-line since docs found") 

4100 

4101 if "stability" in segments: 

4102 desc = ParseStabilityLevel( 

4103 segments["stability"], ifile, line_number, "Stability level for %s" % symbol) 

4104 StabilityLevel[symbol] = ConvertXMLChars(symbol, desc) 

4105 

4106 if "deprecated" in segments: 

4107 if symbol not in Deprecated: 

4108 # don't warn for signals and properties 

4109 # if ($symbol !~ m/::?(.*)/) 

4110 if symbol in DeclarationTypes: 

4111 common.LogWarning(ifile, line_number, 

4112 "%s is deprecated in the inline comments, but no deprecation guards were found around the declaration. (See the --deprecated-guards option for gtkdoc-scan.)" % symbol) 

4113 

4114 Deprecated[symbol] = ConvertXMLChars(symbol, segments["deprecated"]) 

4115 

4116 

4117def ParseCommentBlock(lines, line_number=0, ifile=''): 

4118 """Parse a single comment block. 

4119 

4120 Args: 

4121 lines (list): the comment block 

4122 line_number (int): the first line of the block (for reporting) 

4123 ifile (str): file name of the source file (for reporting) 

4124 """ 

4125 (symbol, segments, params) = SegmentCommentBlock(lines, line_number, ifile) 

4126 if not symbol: 

4127 # maybe its not even meant to be a gtk-doc comment? 

4128 common.LogWarning(ifile, line_number, "Symbol name not found at the start of the comment block.") 

4129 return 

4130 

4131 ParseCommentBlockSegments(symbol, segments, params, line_number, ifile) 

4132 

4133 

4134def OutputMissingDocumentation(): 

4135 """Outputs report of documentation coverage to a file. 

4136 

4137 Returns: 

4138 bool: True if the report was updated 

4139 """ 

4140 old_undocumented_file = os.path.join(ROOT_DIR, MODULE + "-undocumented.txt") 

4141 new_undocumented_file = os.path.join(ROOT_DIR, MODULE + "-undocumented.new") 

4142 

4143 n_documented = 0 

4144 n_incomplete = 0 

4145 total = 0 

4146 symbol = None 

4147 percent = None 

4148 buffer = '' 

4149 buffer_deprecated = '' 

4150 buffer_descriptions = '' 

4151 

4152 UNDOCUMENTED = open(new_undocumented_file, 'w', encoding='utf-8') 

4153 

4154 for symbol in sorted(AllSymbols.keys()): 

4155 # FIXME: should we print common.LogWarnings for undocumented stuff? 

4156 m = re.search( 

4157 r':(title|long_description|short_description|see_also|stability|include|section_id|image)', symbol) 

4158 m2 = re.search(r':(long_description|short_description)', symbol) 

4159 if not m: 

4160 total += 1 

4161 if symbol in AllDocumentedSymbols: 

4162 n_documented += 1 

4163 if symbol in AllIncompleteSymbols: 

4164 n_incomplete += 1 

4165 buffer += symbol + " (" + AllIncompleteSymbols[symbol] + ")\n" 

4166 

4167 elif symbol in Deprecated: 

4168 if symbol in AllIncompleteSymbols: 

4169 n_incomplete += 1 

4170 buffer_deprecated += symbol + " (" + AllIncompleteSymbols[symbol] + ")\n" 

4171 else: 

4172 buffer_deprecated += symbol + "\n" 

4173 

4174 else: 

4175 if symbol in AllIncompleteSymbols: 

4176 n_incomplete += 1 

4177 buffer += symbol + " (" + AllIncompleteSymbols[symbol] + ")\n" 

4178 else: 

4179 buffer += symbol + "\n" 

4180 

4181 elif m2: 

4182 total += 1 

4183 if (symbol in SymbolDocs and len(SymbolDocs[symbol]) > 0)\ 

4184 or (symbol in AllDocumentedSymbols and AllDocumentedSymbols[symbol] > 0): 

4185 n_documented += 1 

4186 else: 

4187 buffer_descriptions += symbol + "\n" 

4188 

4189 if total == 0: 

4190 percent = 100 

4191 else: 

4192 percent = (n_documented / total) * 100.0 

4193 

4194 UNDOCUMENTED.write("%.0f%% symbol docs coverage.\n" % percent) 

4195 UNDOCUMENTED.write("%s symbols documented.\n" % n_documented) 

4196 UNDOCUMENTED.write("%s symbols incomplete.\n" % n_incomplete) 

4197 UNDOCUMENTED.write("%d not documented.\n" % (total - n_documented)) 

4198 

4199 if buffer_deprecated != '': 

4200 buffer += "\n" + buffer_deprecated 

4201 

4202 if buffer_descriptions != '': 

4203 buffer += "\n" + buffer_descriptions 

4204 

4205 if buffer != '': 

4206 UNDOCUMENTED.write("\n\n" + buffer) 

4207 

4208 UNDOCUMENTED.close() 

4209 

4210 return common.UpdateFileIfChanged(old_undocumented_file, new_undocumented_file, 0) 

4211 

4212 

4213def OutputUndeclaredSymbols(): 

4214 """Reports undeclared symbols. 

4215 

4216 Outputs symbols that are listed in the section file, but have no declaration 

4217 in the sources. 

4218 

4219 Returns: 

4220 bool: True if the report was updated 

4221 """ 

4222 old_undeclared_file = os.path.join(ROOT_DIR, MODULE + "-undeclared.txt") 

4223 new_undeclared_file = os.path.join(ROOT_DIR, MODULE + "-undeclared.new") 

4224 

4225 with open(new_undeclared_file, 'w', encoding='utf-8') as out: 

4226 if UndeclaredSymbols: 

4227 out.write("\n".join(sorted(UndeclaredSymbols.keys()))) 

4228 out.write("\n") 

4229 print("See %s-undeclared.txt for the list of undeclared symbols." % MODULE) 

4230 

4231 return common.UpdateFileIfChanged(old_undeclared_file, new_undeclared_file, 0) 

4232 

4233 

4234def OutputUnusedSymbols(): 

4235 """Reports unused documentation. 

4236 

4237 Outputs symbols that are documented in comments, but not declared in the 

4238 sources. 

4239 

4240 Returns: 

4241 bool: True if the report was updated 

4242 """ 

4243 num_unused = 0 

4244 old_unused_file = os.path.join(ROOT_DIR, MODULE + "-unused.txt") 

4245 new_unused_file = os.path.join(ROOT_DIR, MODULE + "-unused.new") 

4246 

4247 with open(new_unused_file, 'w', encoding='utf-8') as out: 

4248 

4249 for symbol in sorted(Declarations.keys()): 

4250 if symbol not in DeclarationOutput: 

4251 out.write("%s\n" % symbol) 

4252 num_unused += 1 

4253 

4254 for symbol in sorted(AllUnusedSymbols.keys()): 

4255 out.write(symbol + "(" + AllUnusedSymbols[symbol] + ")\n") 

4256 num_unused += 1 

4257 

4258 if num_unused != 0: 

4259 common.LogWarning( 

4260 old_unused_file, 1, "%d unused declarations. They should be added to %s-sections.txt in the appropriate place." % (num_unused, MODULE)) 

4261 

4262 return common.UpdateFileIfChanged(old_unused_file, new_unused_file, 0) 

4263 

4264 

4265def OutputAllSymbols(): 

4266 """Outputs list of all symbols to a file.""" 

4267 new_symbols_file = os.path.join(ROOT_DIR, MODULE + "-symbols.txt") 

4268 with open(new_symbols_file, 'w', encoding='utf-8') as out: 

4269 for symbol in sorted(AllSymbols.keys()): 

4270 out.write(symbol + "\n") 

4271 

4272 

4273def OutputSymbolsWithoutSince(): 

4274 """Outputs list of all symbols without a since tag to a file.""" 

4275 new_nosince_file = os.path.join(ROOT_DIR, MODULE + "-nosince.txt") 

4276 with open(new_nosince_file, 'w', encoding='utf-8') as out: 

4277 for symbol in sorted(SourceSymbolDocs.keys()): 

4278 if symbol in Since: 

4279 out.write(symbol + "\n") 

4280 

4281 

4282def CheckParamsDocumented(symbol, params): 

4283 stype = DeclarationTypes.get(symbol) 

4284 

4285 item = "Parameter" 

4286 if stype: 

4287 if stype == 'STRUCT': 

4288 item = "Field" 

4289 elif stype == 'ENUM': 

4290 item = "Value" 

4291 elif stype == 'UNION': 

4292 item = "Field" 

4293 else: 

4294 stype = "SIGNAL" 

4295 logging.info("Check param docs for %s, params: %s entries, type=%s", symbol, len(params), stype) 

4296 

4297 if len(params) > 0: 

4298 logging.info("params: %s", str(params)) 

4299 for (param_name, param_desc) in params.items(): 

4300 # Output a warning if the parameter is empty and remember for stats. 

4301 if param_name != "void" and not re.search(r'\S', param_desc): 

4302 if symbol in AllIncompleteSymbols: 

4303 AllIncompleteSymbols[symbol] += ", " + param_name 

4304 else: 

4305 AllIncompleteSymbols[symbol] = param_name 

4306 

4307 common.LogWarning(*GetSymbolSourceLocation(symbol), 

4308 "%s description for %s::%s is missing in source code comment block." % (item, symbol, param_name)) 

4309 

4310 elif len(params) == 0: 

4311 AllIncompleteSymbols[symbol] = "<items>" 

4312 common.LogWarning(*GetSymbolSourceLocation(symbol), 

4313 "%s descriptions for %s are missing in source code comment block." % (item, symbol)) 

4314 

4315 

4316def MergeSourceDocumentation(): 

4317 """Merges documentation read from a source file. 

4318 

4319 Parameter descriptions override any in the template files. 

4320 Function descriptions are placed before any description from 

4321 the template files. 

4322 """ 

4323 

4324 # add what's found in the source 

4325 symbols = set(SourceSymbolDocs.keys()) 

4326 

4327 # and add known symbols from -sections.txt 

4328 for symbol in KnownSymbols.keys(): 

4329 if KnownSymbols[symbol] == 1: 

4330 symbols.add(symbol) 

4331 

4332 logging.info("num source entries: %d", len(symbols)) 

4333 

4334 for symbol in symbols: 

4335 AllSymbols[symbol] = 1 

4336 

4337 if symbol in SourceSymbolDocs: 

4338 logging.info("merging [%s] from source", symbol) 

4339 

4340 # remove leading and training whitespaces 

4341 src_docs = SourceSymbolDocs[symbol].strip() 

4342 if src_docs != '': 

4343 AllDocumentedSymbols[symbol] = 1 

4344 

4345 SymbolDocs[symbol] = src_docs 

4346 

4347 # merge parameters 

4348 if symbol in SourceSymbolParams: 

4349 param_docs = SourceSymbolParams[symbol] 

4350 SymbolParams[symbol] = param_docs 

4351 # if this symbol is documented, check if docs are complete 

4352 # remove all xml-tags and whitespaces 

4353 check_docs = re.sub(r'\s', '', re.sub(r'<.*?>', '', src_docs)) 

4354 if check_docs != '' and param_docs: 

4355 CheckParamsDocumented(symbol, param_docs) 

4356 else: 

4357 logging.info("[%s] undocumented", symbol) 

4358 

4359 logging.info("num doc entries: %d / %d", len(SymbolDocs), len(SourceSymbolDocs)) 

4360 

4361 

4362def IsEmptyDoc(doc): 

4363 """Check if a doc-string is empty. 

4364 

4365 It is also regarded as empty if it only consist of whitespace or e.g. FIXME. 

4366 

4367 Args: 

4368 doc (str): the doc-string 

4369 

4370 Returns: 

4371 bool: True if empty 

4372 """ 

4373 if re.search(r'^\s*$', doc): 

4374 return True 

4375 if re.search(r'^\s*<para>\s*(FIXME)?\s*<\/para>\s*$', doc): 

4376 return True 

4377 return False 

4378 

4379 

4380def ConvertMarkDown(symbol, text): 

4381 md_to_db.Init() 

4382 return md_to_db.MarkDownParse(text, symbol) 

4383 

4384 

4385def ReadDeclarationsFile(ifile, override): 

4386 """Reads in a file containing the function/macro/enum etc. declarations. 

4387 

4388 Note that in some cases there are several declarations with 

4389 the same name, e.g. for conditional macros. In this case we 

4390 set a flag in the DeclarationConditional hash so the 

4391 declaration is not shown in the docs. 

4392 

4393 If a macro and a function have the same name, e.g. for 

4394 g_object_ref, the function declaration takes precedence. 

4395 

4396 Some opaque structs are just declared with 'typedef struct 

4397 _name name;' in which case the declaration may be empty. 

4398 The structure may have been found later in the header, so 

4399 that overrides the empty declaration. 

4400 

4401 Args: 

4402 file (str): the declarations file to read 

4403 override (bool): if declarations in this file should override 

4404 any current declaration. 

4405 """ 

4406 if override == 0: 

4407 Declarations.clear() 

4408 DeclarationTypes.clear() 

4409 DeclarationConditional.clear() 

4410 DeclarationOutput.clear() 

4411 

4412 INPUT = open(ifile, 'r', encoding='utf-8') 

4413 declaration_type = '' 

4414 declaration_name = None 

4415 declaration = None 

4416 is_deprecated = 0 

4417 line_number = 0 

4418 for line in INPUT: 

4419 line_number += 1 

4420 # logging.debug("%s:%d: %s", ifile, line_number, line) 

4421 if not declaration_type: 

4422 m1 = re.search(r'^<([^>]+)>', line) 

4423 if m1: 

4424 declaration_type = m1.group(1) 

4425 declaration_name = '' 

4426 logging.info("Found declaration: %s", declaration_type) 

4427 declaration = '' 

4428 else: 

4429 m2 = re.search(r'^<NAME>(.*)</NAME>', line) 

4430 m3 = re.search(r'^<DEPRECATED/>', line) 

4431 m4 = re.search(r'^</%s>' % declaration_type, line) 

4432 if m2: 

4433 declaration_name = m2.group(1) 

4434 elif m3: 

4435 is_deprecated = True 

4436 elif m4: 

4437 logging.info("Found end of declaration: %s, %s", declaration_type, declaration_name) 

4438 # Check that the declaration has a name 

4439 if declaration_name == '': 

4440 common.LogWarning(ifile, line_number, declaration_type + " has no name.\n") 

4441 

4442 # If the declaration is an empty typedef struct _XXX XXX 

4443 # set the flag to indicate the struct has a typedef. 

4444 if (declaration_type == 'STRUCT' or declaration_type == 'UNION') \ 

4445 and re.search(r'^\s*$', declaration): 

4446 logging.info("Struct has typedef: %s", declaration_name) 

4447 StructHasTypedef[declaration_name] = 1 

4448 

4449 # Check if the symbol is already defined. 

4450 if declaration_name in Declarations and override == 0: 

4451 # Function declarations take precedence. 

4452 if DeclarationTypes[declaration_name] == 'FUNCTION': 

4453 # Ignore it. 

4454 pass 

4455 elif declaration_type == 'FUNCTION': 

4456 if is_deprecated: 

4457 Deprecated[declaration_name] = '' 

4458 

4459 Declarations[declaration_name] = declaration 

4460 DeclarationTypes[declaration_name] = declaration_type 

4461 elif DeclarationTypes[declaration_name] == declaration_type: 

4462 # If the existing declaration is empty, or is just a 

4463 # forward declaration of a struct, override it. 

4464 if declaration_type == 'STRUCT' or declaration_type == 'UNION': 

4465 if re.search(r'^\s*((struct|union)\s+\w+\s*;)?\s*$', Declarations[declaration_name]): 

4466 if is_deprecated: 

4467 Deprecated[declaration_name] = '' 

4468 Declarations[declaration_name] = declaration 

4469 elif re.search(r'^\s*((struct|union)\s+\w+\s*;)?\s*$', declaration): 

4470 # Ignore an empty or forward declaration. 

4471 pass 

4472 else: 

4473 common.LogWarning( 

4474 ifile, line_number, "Structure %s has multiple definitions." % declaration_name) 

4475 

4476 else: 

4477 # set flag in %DeclarationConditional hash for 

4478 # multiply defined macros/typedefs. 

4479 DeclarationConditional[declaration_name] = 1 

4480 

4481 else: 

4482 common.LogWarning(ifile, line_number, declaration_name + " has multiple definitions.") 

4483 

4484 else: 

4485 if is_deprecated: 

4486 Deprecated[declaration_name] = '' 

4487 

4488 Declarations[declaration_name] = declaration 

4489 DeclarationTypes[declaration_name] = declaration_type 

4490 logging.debug("added declaration: %s, %s, [%s]", declaration_type, declaration_name, declaration) 

4491 

4492 declaration_type = '' 

4493 is_deprecated = False 

4494 else: 

4495 declaration += line 

4496 INPUT.close() 

4497 

4498 

4499def ReadSignalsFile(ifile): 

4500 """Reads information about object signals. 

4501 

4502 It creates the arrays @SignalNames and @SignalPrototypes containing details 

4503 about the signals. The first line of the SignalPrototype is the return type 

4504 of the signal handler. The remaining lines are the parameters passed to it. 

4505 The last parameter, "gpointer user_data" is always the same so is not included. 

4506 

4507 Args: 

4508 ifile (str): the file containing the signal handler prototype information. 

4509 """ 

4510 in_signal = 0 

4511 signal_object = None 

4512 signal_name = None 

4513 signal_returns = None 

4514 signal_flags = None 

4515 signal_prototype = None 

4516 

4517 # Reset the signal info. 

4518 SignalObjects[:] = [] 

4519 SignalNames[:] = [] 

4520 SignalReturns[:] = [] 

4521 SignalFlags[:] = [] 

4522 SignalPrototypes[:] = [] 

4523 

4524 if not os.path.isfile(ifile): 

4525 return 

4526 

4527 INPUT = open(ifile, 'r', encoding='utf-8') 

4528 line_number = 0 

4529 for line in INPUT: 

4530 line_number += 1 

4531 if not in_signal: 

4532 if re.search(r'^<SIGNAL>', line): 

4533 in_signal = 1 

4534 signal_object = '' 

4535 signal_name = '' 

4536 signal_returns = '' 

4537 signal_prototype = '' 

4538 

4539 else: 

4540 m = re.search(r'^<NAME>(.*)<\/NAME>', line) 

4541 m2 = re.search(r'^<RETURNS>(.*)<\/RETURNS>', line) 

4542 m3 = re.search(r'^<FLAGS>(.*)<\/FLAGS>', line) 

4543 if m: 

4544 signal_name = m.group(1) 

4545 m1_2 = re.search(r'^(.*)::(.*)$', signal_name) 

4546 if m1_2: 

4547 signal_object = m1_2.group(1) 

4548 signal_name = m1_2.group(2).replace('_', '-') 

4549 logging.info("Found signal: %s", signal_name) 

4550 else: 

4551 common.LogWarning(ifile, line_number, "Invalid signal name: %s." % signal_name) 

4552 

4553 elif m2: 

4554 signal_returns = m2.group(1) 

4555 elif m3: 

4556 signal_flags = m3.group(1) 

4557 elif re.search(r'^</SIGNAL>', line): 

4558 logging.info("Found end of signal: %s::%s\nReturns: %s\n%s", 

4559 signal_object, signal_name, signal_returns, signal_prototype) 

4560 SignalObjects.append(signal_object) 

4561 SignalNames.append(signal_name) 

4562 SignalReturns.append(signal_returns) 

4563 SignalFlags.append(signal_flags) 

4564 SignalPrototypes.append(signal_prototype) 

4565 in_signal = False 

4566 else: 

4567 signal_prototype += line 

4568 INPUT.close() 

4569 

4570 

4571def ReadObjectHierarchy(ifile): 

4572 """Reads the $MODULE-hierarchy file. 

4573 

4574 This contains all the GObject subclasses described in this module (and their 

4575 ancestors). 

4576 It places them in the Objects array, and places their level 

4577 in the object hierarchy in the ObjectLevels array, at the 

4578 same index. GObject, the root object, has a level of 1. 

4579 

4580 Args: 

4581 ifile (str): the input filename. 

4582 """ 

4583 

4584 if not os.path.isfile(ifile): 

4585 logging.debug('no %s file found', ifile) 

4586 return 

4587 

4588 Objects[:] = [] 

4589 ObjectLevels[:] = [] 

4590 

4591 INPUT = open(ifile, 'r', encoding='utf-8') 

4592 

4593 # Only emit objects if they are supposed to be documented, or if 

4594 # they have documented children. To implement this, we maintain a 

4595 # stack of pending objects which will be emitted if a documented 

4596 # child turns up. 

4597 pending_objects = [] 

4598 pending_levels = [] 

4599 root = None 

4600 tree = [] 

4601 for line in INPUT: 

4602 m1 = re.search(r'\S+', line) 

4603 if not m1: 

4604 continue 

4605 

4606 gobject = m1.group(0) 

4607 level = len(line[:m1.start()]) // 2 + 1 

4608 

4609 if level == 1: 

4610 root = gobject 

4611 

4612 while pending_levels and pending_levels[-1] >= level: 

4613 pending_objects.pop() 

4614 pending_levels.pop() 

4615 

4616 pending_objects.append(gobject) 

4617 pending_levels.append(level) 

4618 

4619 if gobject in KnownSymbols: 

4620 while len(pending_levels) > 0: 

4621 gobject = pending_objects.pop(0) 

4622 level = pending_levels.pop(0) 

4623 xref = MakeXRef(gobject) 

4624 

4625 tree.append(' ' * (level * 4) + xref) 

4626 Objects.append(gobject) 

4627 ObjectLevels.append(level) 

4628 ObjectRoots[gobject] = root 

4629 # else 

4630 # common.LogWarning(ifile, line_number, "unknown type %s" % object) 

4631 # 

4632 

4633 INPUT.close() 

4634 logging.debug('got %d entries for hierarchy', len(tree)) 

4635 return tree 

4636 

4637 

4638def ReadInterfaces(ifile): 

4639 """Reads the $MODULE.interfaces file. 

4640 

4641 Args: 

4642 ifile (str): the input filename. 

4643 """ 

4644 

4645 Interfaces.clear() 

4646 

4647 if not os.path.isfile(ifile): 

4648 return 

4649 

4650 INPUT = open(ifile, 'r', encoding='utf-8') 

4651 

4652 for line in INPUT: 

4653 line = line.strip() 

4654 ifaces = line.split() 

4655 gobject = ifaces.pop(0) 

4656 if gobject in KnownSymbols and KnownSymbols[gobject] == 1: 

4657 knownIfaces = [] 

4658 

4659 # filter out private interfaces, but leave foreign interfaces 

4660 for iface in ifaces: 

4661 if iface not in KnownSymbols or KnownSymbols[iface] == 1: 

4662 knownIfaces.append(iface) 

4663 

4664 Interfaces[gobject] = ' '.join(knownIfaces) 

4665 logging.info("Interfaces for %s: %s", gobject, Interfaces[gobject]) 

4666 else: 

4667 logging.info("skipping interfaces for unknown symbol: %s", gobject) 

4668 

4669 INPUT.close() 

4670 

4671 

4672def ReadPrerequisites(ifile): 

4673 """This reads in the $MODULE.prerequisites file. 

4674 

4675 Args: 

4676 ifile (str): the input filename. 

4677 """ 

4678 Prerequisites.clear() 

4679 

4680 if not os.path.isfile(ifile): 

4681 return 

4682 

4683 INPUT = open(ifile, 'r', encoding='utf-8') 

4684 

4685 for line in INPUT: 

4686 line = line.strip() 

4687 prereqs = line.split() 

4688 iface = prereqs.pop(0) 

4689 if iface in KnownSymbols and KnownSymbols[iface] == 1: 

4690 knownPrereqs = [] 

4691 

4692 # filter out private prerequisites, but leave foreign prerequisites 

4693 for prereq in prereqs: 

4694 if prereq not in KnownSymbols or KnownSymbols[prereq] == 1: 

4695 knownPrereqs.append(prereq) 

4696 

4697 Prerequisites[iface] = ' '.join(knownPrereqs) 

4698 

4699 INPUT.close() 

4700 

4701 

4702def ReadArgsFile(ifile): 

4703 """Reads information about object properties 

4704 

4705 It creates the arrays ArgObjects, ArgNames, ArgTypes, ArgFlags, ArgNicks and 

4706 ArgBlurbs containing info on the args. 

4707 

4708 Args: 

4709 ifile (str): the input filename. 

4710 """ 

4711 in_arg = False 

4712 arg_object = None 

4713 arg_name = None 

4714 arg_type = None 

4715 arg_flags = None 

4716 arg_nick = None 

4717 arg_blurb = None 

4718 arg_default = None 

4719 arg_range = None 

4720 

4721 # Reset the args info. 

4722 ArgObjects[:] = [] 

4723 ArgNames[:] = [] 

4724 ArgTypes[:] = [] 

4725 ArgFlags[:] = [] 

4726 ArgNicks[:] = [] 

4727 ArgBlurbs[:] = [] 

4728 ArgDefaults[:] = [] 

4729 ArgRanges[:] = [] 

4730 

4731 if not os.path.isfile(ifile): 

4732 return 

4733 

4734 INPUT = open(ifile, 'r', encoding='utf-8') 

4735 line_number = 0 

4736 for line in INPUT: 

4737 line_number += 1 

4738 if not in_arg: 

4739 if line.startswith('<ARG>'): 

4740 in_arg = True 

4741 arg_object = '' 

4742 arg_name = '' 

4743 arg_type = '' 

4744 arg_flags = '' 

4745 arg_nick = '' 

4746 arg_blurb = '' 

4747 arg_default = '' 

4748 arg_range = '' 

4749 

4750 else: 

4751 m1 = re.search(r'^<NAME>(.*)</NAME>', line) 

4752 m2 = re.search(r'^<TYPE>(.*)</TYPE>', line) 

4753 m3 = re.search(r'^<RANGE>(.*)</RANGE>', line) 

4754 m4 = re.search(r'^<FLAGS>(.*)</FLAGS>', line) 

4755 m5 = re.search(r'^<NICK>(.*)</NICK>', line) 

4756 m6 = re.search(r'^<BLURB>(.*)</BLURB>', line) 

4757 m7 = re.search(r'^<DEFAULT>(.*)</DEFAULT>', line) 

4758 if m1: 

4759 arg_name = m1.group(1) 

4760 m1_1 = re.search(r'^(.*)::(.*)$', arg_name) 

4761 if m1_1: 

4762 arg_object = m1_1.group(1) 

4763 arg_name = m1_1.group(2).replace('_', '-') 

4764 logging.info("Found arg: %s", arg_name) 

4765 else: 

4766 common.LogWarning(ifile, line_number, "Invalid argument name: " + arg_name) 

4767 

4768 elif m2: 

4769 arg_type = m2.group(1) 

4770 elif m3: 

4771 arg_range = m3.group(1) 

4772 elif m4: 

4773 arg_flags = m4.group(1) 

4774 elif m5: 

4775 arg_nick = m5.group(1) 

4776 elif m6: 

4777 arg_blurb = m6.group(1) 

4778 if arg_blurb == "(null)": 

4779 arg_blurb = '' 

4780 common.LogWarning( 

4781 ifile, line_number, "Property %s:%s has no documentation." % (arg_object, arg_name)) 

4782 

4783 elif m7: 

4784 arg_default = m7.group(1) 

4785 elif re.search(r'^</ARG>', line): 

4786 logging.info("Found end of arg: %s::%s\n%s : %s", arg_object, arg_name, arg_type, arg_flags) 

4787 ArgObjects.append(arg_object) 

4788 ArgNames.append(arg_name) 

4789 ArgTypes.append(arg_type) 

4790 ArgRanges.append(arg_range) 

4791 ArgFlags.append(arg_flags) 

4792 ArgNicks.append(arg_nick) 

4793 ArgBlurbs.append(arg_blurb) 

4794 ArgDefaults.append(arg_default) 

4795 in_arg = False 

4796 

4797 INPUT.close() 

4798 

4799def ReadActionsFile(ifile): 

4800 """Reads information about object actions 

4801 

4802 It creates the arrays ActionObjects, ActionNames, ActionParams 

4803 and ActionProperties containing info on the actions. 

4804 

4805 Args: 

4806 ifile (str): the input filename. 

4807 """ 

4808 in_action = False 

4809 action_object = None 

4810 action_name = None 

4811 action_param = None 

4812 action_prop = None 

4813 

4814 # Reset the args info. 

4815 ActionObjects[:] = [] 

4816 ActionNames[:] = [] 

4817 ActionParams[:] = [] 

4818 ActionProperties[:] = [] 

4819 

4820 if not os.path.isfile(ifile): 

4821 return 

4822 

4823 INPUT = open(ifile, 'r', encoding='utf-8') 

4824 line_number = 0 

4825 for line in INPUT: 

4826 line_number += 1 

4827 if not in_action: 

4828 if line.startswith('<ACTION>'): 

4829 in_action = True 

4830 action_object = '' 

4831 action_name = '' 

4832 action_param = '' 

4833 action_prop = '' 

4834 

4835 else: 

4836 m1 = re.search(r'^<NAME>(.*)</NAME>', line) 

4837 m2 = re.search(r'^<PARAMETER>(.*)</PARAMETER>', line) 

4838 m3 = re.search(r'^<PROPERTY>(.*)</PROPERTY>', line) 

4839 if m1: 

4840 action_name = m1.group(1) 

4841 m1_1 = re.search(r'^(.*):::(.*)$', action_name) 

4842 if m1_1: 

4843 action_object = m1_1.group(1) 

4844 action_name = m1_1.group(2).replace('_', '-') 

4845 logging.info("Found action: %s", action_name) 

4846 else: 

4847 common.LogWarning(ifile, line_number, "Invalid action name: " + action_name) 

4848 

4849 elif m2: 

4850 action_param = m2.group(1) 

4851 elif m3: 

4852 action_prop = m3.group(1) 

4853 elif re.search(r'^</ACTION>', line): 

4854 logging.info("Found end of action: %s::%s", action_object, action_name) 

4855 ActionObjects.append(action_object) 

4856 ActionNames.append(action_name) 

4857 ActionParams.append(action_param) 

4858 ActionProperties.append(action_prop) 

4859 in_action = False 

4860 

4861 INPUT.close() 

4862 

4863 

4864def AddTreeLineArt(tree): 

4865 """Generate a line art tree. 

4866 

4867 Add unicode lineart to a pre-indented string array and returns 

4868 it as as multiline string. 

4869 

4870 Args: 

4871 tree (list): of indented strings. 

4872 

4873 Returns: 

4874 str: multiline string with tree line art 

4875 """ 

4876 # iterate bottom up over the tree 

4877 for i in range(len(tree) - 1, -1, -1): 

4878 # count leading spaces 

4879 m = re.search(r'^([^<A-Za-z]*)', tree[i]) 

4880 indent = len(m.group(1)) 

4881 # replace with ╰───, if place of ╰ is not space insert ├ 

4882 if indent > 4: 

4883 if tree[i][indent - 4] == " ": 

4884 tree[i] = tree[i][:indent - 4] + "--- " + tree[i][indent:] 

4885 else: 

4886 tree[i] = tree[i][:indent - 4] + "+-- " + tree[i][indent:] 

4887 

4888 # go lines up while space and insert | 

4889 j = i - 1 

4890 while j >= 0 and tree[j][indent - 4] == ' ': 

4891 tree[j] = tree[j][:indent - 4] + '|' + tree[j][indent - 3:] 

4892 j -= 1 

4893 

4894 res = "\n".join(tree) 

4895 # unicode chars for: ╰── 

4896 res = re.sub(r'---', '<phrase role=\"lineart\">&#9584;&#9472;&#9472;</phrase>', res) 

4897 # unicde chars for: ├── 

4898 res = re.sub(r'\+--', '<phrase role=\"lineart\">&#9500;&#9472;&#9472;</phrase>', res) 

4899 # unicode char for: │ 

4900 res = re.sub(r'\|', '<phrase role=\"lineart\">&#9474;</phrase>', res) 

4901 

4902 return res 

4903 

4904 

4905def CheckIsObject(name): 

4906 """Check if symbols is an object. 

4907 

4908 It uses the global Objects array. Note that the Objects array only contains 

4909 classes in the current module and their ancestors - not all GObject classes. 

4910 

4911 Args: 

4912 name (str): the object name to check. 

4913 

4914 Returns: 

4915 bool: True if the given name is a GObject or a subclass. 

4916 """ 

4917 root = ObjectRoots.get(name) 

4918 # Let GBoxed pass as an object here to get -struct appended to the id 

4919 # and prevent conflicts with sections. 

4920 return root and root != 'GEnum' and root != 'GFlags' 

4921 

4922 

4923def GetSymbolParams(symbol): 

4924 """Get the symbol params and check that they are not empty. 

4925 

4926 If no parameters are filled in, we don't generate the description table, 

4927 for backwards compatibility. 

4928 

4929 Args: 

4930 symbol: the symbol to check the parameters for 

4931 

4932 Returns: 

4933 dict: The parameters 

4934 bool: True if empty 

4935 """ 

4936 params = SymbolParams.get(symbol, {}) 

4937 # TODO: strip at parsing stage? 

4938 found = next((True for p in params.values() if p.strip() != ''), False) 

4939 return (params, found) 

4940 

4941 

4942def GetSymbolSourceLocation(symbol): 

4943 """Get the filename and line where the symbol docs where taken from.""" 

4944 return SymbolSourceLocation.get(symbol, ('', 0)) 

4945 

4946 

4947def new_args_parser(): 

4948 parser = argparse.ArgumentParser() 

4949 parser.add_argument("--version", action="version", version=config.version) 

4950 parser.add_argument( 

4951 "--module", required=True, help="Name of the doc module being parsed" 

4952 ) 

4953 parser.add_argument("--source-dir", action="append", dest="source_dir", default=[]) 

4954 parser.add_argument("--source-suffixes", dest="source_suffixes", default="") 

4955 parser.add_argument("--ignore-files", dest="ignore_files", default="") 

4956 parser.add_argument("--output-dir", dest="output_dir", default="") 

4957 parser.add_argument("--tmpl-dir", dest="tmpl_dir", help="DEPRECATED") 

4958 parser.add_argument("--main-sgml-file", dest="main_sgml_file", default="") 

4959 parser.add_argument( 

4960 "--expand-content-files", dest="expand_content_files", default="" 

4961 ) 

4962 group = parser.add_mutually_exclusive_group() 

4963 group.add_argument( 

4964 "--sgml-mode", action="store_true", default=False, dest="sgml_mode" 

4965 ) 

4966 group.add_argument( 

4967 "--xml-mode", action="store_true", default=False, dest="xml_mode" 

4968 ) 

4969 parser.add_argument( 

4970 "--default-stability", 

4971 dest="default_stability", 

4972 choices=["", "Stable", "Private", "Unstable"], 

4973 default="", 

4974 ) 

4975 parser.add_argument("--default-includes", dest="default_includes", default="") 

4976 parser.add_argument("--output-format", default="xml") # MUST be 'xml', deprecate? 

4977 parser.add_argument("--name-space", dest="name_space", default="") 

4978 parser.add_argument("--outputallsymbols", default=False, action="store_true") 

4979 parser.add_argument( 

4980 "--outputsymbolswithoutsince", default=False, action="store_true" 

4981 ) 

4982 return parser