4 # gtk-doc - GTK DocBook documentation generator.
5 # Copyright (C) 1998 Damon Chaplin
6 # 2007-2016 Stefan Sauer
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #############################################################################
24 # Script : gtkdoc-fixxref
25 # Description : This fixes cross-references in the HTML documentation.
26 #############################################################################
32 push @INC, '@PACKAGE_DATA_DIR@';
33 require "gtkdoc-common.pl";
37 # name of documentation module
46 # This contains all the entities and their relative URLs.
49 # failing link targets we don't warn about even once
67 # Cache of dirs we already scanned for index files
70 Run() unless caller; # Run program unless loaded as a module
74 my %optctl = ('module' => \$MODULE,
75 'module-dir' => \$MODULE_DIR,
76 'html-dir' => \$HTML_DIR,
77 'extra-dir' => \@EXTRA_DIRS,
78 'version' => \$PRINT_VERSION,
79 'help' => \$PRINT_HELP,
80 'src-lang' => \$SRC_LANG);
82 GetOptions(\%optctl, "module=s", "module-dir=s", "html-dir:s", "extra-dir=s@",
83 "src-lang=s", "version", "help");
92 gtkdoc-fixxref version @VERSION@ - fix cross references in html files
94 --module=MODULE_NAME Name of the doc module being parsed
95 --module-dir=MODULE_DIR The directory which contains the generated HTML
96 --html-dir=HTML_DIR The directory where gtk-doc generated documentation is
98 --extra-dir=EXTRA_DIR Directories to recursively scan for indices (index.sgml)
99 in addition to HTML_DIR
100 May be used more than once for multiple directories
101 --src-lang=SRC_LANG Programing language used for syntax highlighting. The
102 available languages depend on the source source
104 --version Print the version of this program
105 --help Print this help
115 if ($HTML_DIR =~ m%(.*?)/share/gtk-doc/html%) {
117 @TRACE@("Path prefix: $path_prefix");
120 if (!defined $MODULE_DIR) {
121 $MODULE_DIR="$HTML_DIR/$MODULE";
126 # We scan the directory containing GLib and any directories in GNOME2_PATH
127 # first, but these will be overriden by any later scans.
128 $dir = `@PKG_CONFIG@ --variable=prefix glib-2.0`;
130 $dir = $dir . "/share/gtk-doc/html";
132 # Some predefined link targets to get links into type hierarchies as these
133 # have no targets. These are always absolute for now.
134 $Links{'GBoxed'} = "$dir/gobject/gobject-Boxed-Types.html";
135 $Links{'GEnum'} = "$dir/gobject/gobject-Enumeration-and-Flag-Types.html";
136 $Links{'GFlags'} = "$dir/gobject/gobject-Enumeration-and-Flag-Types.html";
137 $Links{'GInterface'} = "$dir/gobject/GTypeModule.html";
139 if ($dir ne $HTML_DIR) {
140 @TRACE@("Scanning GLib directory: $dir");
141 if ($dir !~ m%^\Q$path_prefix\E/%) {
142 &ScanIndices ($dir, 1);
144 &ScanIndices ($dir, 0);
149 if (defined ($ENV{"GNOME2_PATH"})) {
150 foreach $dir (split (/:/, $ENV{"GNOME2_PATH"})) {
151 $dir = $dir . "/share/gtk-doc/html";
152 if (-d $dir && $dir ne $HTML_DIR) {
153 @TRACE@("Scanning GNOME2_PATH directory: $dir");
154 if ($dir !~ m%^\Q$path_prefix\E/%) {
155 &ScanIndices ($dir, 1);
157 &ScanIndices ($dir, 0);
160 # ubuntu started to compress this as index.sgml.gz :/
161 # https://bugs.launchpad.net/ubuntu/+source/gtk-doc/+bug/77138
165 @TRACE@("Scanning HTML_DIR directory: $HTML_DIR");
166 &ScanIndices ($HTML_DIR, 0);
167 @TRACE@("Scanning HTML_DIR directory: $MODULE_DIR");
168 &ScanIndices ($MODULE_DIR, 0);
170 # check all extra dirs, but skip already scanned dirs or subdirs of those
171 foreach my $dir (@EXTRA_DIRS) {
175 @TRACE@("Scanning EXTRA_DIR directory: $dir");
177 # If the --extra-dir option is not relative and is not sharing the same
178 # prefix as the target directory of the docs, we need to use absolute
179 # directories for the links
180 if ($dir !~m/^\.\./ && $dir !~ m%\Q$path_prefix\E/%) {
181 &ScanIndices ($dir, 1);
183 &ScanIndices ($dir, 0);
189 &FixCrossReferences ($MODULE_DIR);
194 my ($scan_dir, $use_absolute_links) = @_;
196 if (exists $DirCache{$scan_dir}) {
199 $DirCache{$scan_dir} = 1;
201 @TRACE@("Scanning source directory: $scan_dir absolute: $use_absolute_links");
203 # This array holds any subdirectories found.
206 opendir (HTMLDIR, $scan_dir) || return;
208 foreach $file (readdir (HTMLDIR)) {
209 if ($file eq '.' || $file eq '..') {
211 } elsif (-d "$scan_dir/$file") {
212 push (@subdirs, $file);
215 if ($file =~ m/\.devhelp2$/) {
216 # if devhelp-file is good don't read index.sgml
217 &ReadDevhelp ("$scan_dir/$file", $use_absolute_links);
219 elsif (($file eq "index.sgml.gz") && ! (-e "$scan_dir/index.sgml")) {
220 # debian/ubuntu started to compress this as index.sgml.gz :/
222 Please fix https://bugs.launchpad.net/ubuntu/+source/gtk-doc/+bug/77138 . For now run:
223 gunzip $scan_dir/$file
226 elsif (($file =~ m/(.*?)\.devhelp2.gz$/) && ! (-e "$scan_dir/$1.devhelp2")) {
227 # debian/ubuntu started to compress this as *devhelp2.gz :/
229 Please fix https://bugs.launchpad.net/ubuntu/+source/gtk-doc/+bug/1466210 . For now run:
230 gunzip $scan_dir/$file
233 # we could consider supporting: use IO::Zlib;
237 # Now recursively scan the subdirectories.
239 foreach $dir (sort(@subdirs)) {
240 &ScanIndices ("$scan_dir/$dir", $use_absolute_links);
246 my ($file, $use_absolute_links) = @_;
248 # Determine the absolute directory, to be added to links in $file
249 # if we need to use an absolute link.
250 # $file will be something like /prefix/gnome/share/gtk-doc/html/gtk/$file
251 # We want the part up to 'html/.*' since the links in $file include
254 if ($use_absolute_links) {
255 # For uninstalled index.sgml files we'd need to map the path to where it
256 # will be installed to
257 if ($file !~ /\.\//) {
258 $file =~ /(.*\/)(.*?)\/.*?\.devhelp2/;
262 if ($file =~ /(.*\/)(.*?)\/.*?\.devhelp2/) {
269 @TRACE@("Scanning index file=$file, absolute=$use_absolute_links, dir=$dir");
271 open (INDEXFILE, $file)
272 || die "Can't open $file: $!";
273 while (<INDEXFILE>) {
274 if (m/ link="([^#]*)#([^"]*)"/) {
275 @TRACE@("Found id: $2 href: $1#$2");
276 $Links{$2} = "$dir$1#$2";
284 if (!defined($MODULE)) {
288 open (INPUT, "$MODULE-sections.txt")
289 || die "Can't open $MODULE-sections.txt: $!";
295 } elsif (m/^<SECTION>/) {
297 } elsif (m/^<SUBSECTION\s*(.*)>/i) {
299 } elsif (m/^<SUBSECTION>/) {
301 } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
303 } elsif (m/^<FILE>(.*)<\/FILE>/) {
305 } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
307 } elsif (m/^<\/SECTION>/) {
309 } elsif (m/^(\S+)/) {
312 if ($subsection eq "Standard" || $subsection eq "Private") {
313 $NoLinks{CreateValidSGMLID($symbol)} = 1;
321 sub FixCrossReferences {
324 opendir (HTMLDIR, $scan_dir)
325 || die "Can't open HTML directory $scan_dir: $!";
327 foreach $file (readdir (HTMLDIR)) {
328 if ($file eq '.' || $file eq '..') {
330 } elsif ($file =~ m/.html?$/) {
331 &FixHTMLFile ("$scan_dir/$file");
340 @TRACE@("Fixing file: $file");
342 open (HTMLFILE, $file)
343 || die "Can't open $file: $!";
345 my $entire_file = <HTMLFILE>;
348 if ("@HIGHLIGHT@" ne "") {
349 # FIXME: ideally we'd pass a clue about the example language to the highligher
350 # unfortunately the "language" attribute is not appearing in the html output
351 # we could patch the customization to have <code class="xxx"> inside of <pre>
352 if ("@HIGHLIGHT@" =~ m%/vim$%) {
353 $entire_file =~ s%<div class=\"(example-contents|informalexample)\"><pre class=\"programlisting\">(.*?)</pre></div>%&HighlightSourceVim($1,$2);%gse;
356 $entire_file =~ s%<div class=\"(example-contents|informalexample)\"><pre class=\"programlisting\">(.*?)</pre></div>%&HighlightSource($1,$2);%gse;
358 # this just broke existing GTKDOCLINK tags
359 # <GTKDOCLINK HREF="GST-PAD-SINK:CAPS">GST_PAD_SINK</GTKDOCLINK>
360 $entire_file =~ s%\<GTKDOCLINK\s+HREF=\"(.*?)\"\>(.*?)\</GTKDOCLINK\>%\<GTKDOCLINK\ HREF=\"$1\"\>$2\</GTKDOCLINK\>%gs;
362 # from the highlighter we get all the functions marked up
363 # now we could turn them into GTKDOCLINK items
364 $entire_file =~ s%(<span class=\"function\">)(.*?)(</span>)%&MakeGtkDocLink($1,$2,$3);%gse;
365 # we could also try the first item in stuff marked up as 'normal'
366 $entire_file =~ s%(<span class=\"normal\">\s*)(.+?)((\s+.+?)?\s*</span>)%&MakeGtkDocLink($1,$2,$3);%gse;
369 my @lines = split(/\n/, $entire_file);
370 for (my $i=0; $i<$#lines; $i++) {
371 $lines[$i] =~ s%<GTKDOCLINK\s+HREF="([^"]*)"\s*>(.*?)</GTKDOCLINK\s*>% &MakeXRef($file,$i+1,$1,$2); %ge;
372 if ($lines[$i] =~ m/GTKDOCLINK/) {
373 @TRACE@("make xref failed for line: ".$lines[$i]);
376 $entire_file = join("\n",@lines);
378 open (NEWFILE, ">$file.new")
379 || die "Can't open $file: $!";
380 print NEWFILE $entire_file;
384 || die "Can't delete $file: $!";
385 rename ("$file.new", $file)
386 || die "Can't rename $file.new: $!";
390 my ($file, $line, $id, $text) = @_;
392 my $href = $Links{$id};
394 # this is a workaround for some inconsistency we have with CreateValidSGMLID
395 if (!$href && $id =~ m/:/) {
398 $href = $Links{$tid};
400 # poor mans plural support
401 if (!$href && $id =~ m/s$/) {
404 $href = $Links{$tid};
405 if (!$href && defined $Links{"$tid-struct"}) {
406 $href = $Links{"$tid-struct"};
409 if (!$href && defined $Links{"$id-struct"}) {
410 $href = $Links{"$id-struct"};
414 # if it is a link to same module, remove path to make it work
416 if (defined($MODULE) && $href =~ m%^\.\./$MODULE/(.*)$%) {
418 @TRACE@(" Fixing link to uninstalled doc: $id, $href, $text");
420 @TRACE@(" Fixing link: $id, $href, $text");
422 return "<a href=\"$href\">$text</a>";
425 @TRACE@(" no link for: $id, $text");
427 # don't warn multiple times and also skip blacklisted (ctypes)
428 $warn = 0 if exists $NoLinks{$id};
429 # if it's a function, don't warn if it does not contain a "_"
430 # (transformed to "-")
431 # - gnome coding style would use '_'
432 # - will avoid wrong warnings for ansi c functions
433 $warn = 0 if ($text =~ m/ class=\"function\"/ && $id !~ m/-/);
434 # if it's a 'return value', don't warn (implicitly created link)
435 $warn = 0 if ($text =~ m/ class=\"returnvalue\"/);
436 # if it's a 'type', don't warn if it starts with lowercase
437 # - gnome coding style would use CamelCase
438 $warn = 0 if ($text =~ m/ class=\"type\"/ && ($id =~ m/^[a-z]/));
439 # don't warn for self links
440 $warn = 0 if ($text eq $id);
443 &LogWarning ($file, $line, "no link for: '$id' -> ($text).");
452 my ($pre,$symbol,$post) = @_;
454 my $id=CreateValidSGMLID($symbol);
456 # these are implicitely created links in highlighed sources
457 # we don't want warnings for those if the links cannot be resolved.
460 #return "<span class=\"$type\"><GTKDOCLINK HREF=\"$id\">$symbol</GTKDOCLINK></span>";
461 return "$pre<GTKDOCLINK HREF=\"$id\">$symbol</GTKDOCLINK>$post";
465 sub HighlightSource {
466 my ($type, $source) = @_;
468 # chop of leading and trailing empty lines
469 $source =~ s/^\s*\n+//gs;
470 $source =~ s/[\s\n]+$//gs;
472 $source =~ m/^(\s*)/;
473 $source =~ s/^$1//gms;
474 # avoid double entity replacement
475 $source =~ s/</</g;
476 $source =~ s/>/>/g;
477 $source =~ s/&/&/g;
479 # write source to a temp file
480 # FIXME: use .c for now to hint the language to the highlighter
481 my $temp_source_file="$MODULE_DIR/_temp_src.$$.c";
482 open (NEWFILE, ">$temp_source_file") || die "Can't open $temp_source_file: $!";
483 print NEWFILE $source;
486 @TRACE@(" running @HIGHLIGHT@ @HIGHLIGHT_OPTIONS@$temp_source_file ");
489 my $highlighted_source=`@HIGHLIGHT@ @HIGHLIGHT_OPTIONS@$temp_source_file`;
490 if ("@HIGHLIGHT@" =~ m%/source-highlight$%) {
491 $highlighted_source =~ s%^<\!-- .*? -->%%gs;
492 $highlighted_source =~ s%<pre><tt>(.*?)</tt></pre>%$1%gs;
494 elsif ("@HIGHLIGHT@" =~ m%/highlight$%) {
495 # need to rewrite the stylesheet classes
496 $highlighted_source =~ s%<span class="gtkdoc com">%<span class="comment">%gs;
497 $highlighted_source =~ s%<span class="gtkdoc dir">%<span class="preproc">%gs;
498 $highlighted_source =~ s%<span class="gtkdoc kwd">%<span class="function">%gs;
499 $highlighted_source =~ s%<span class="gtkdoc kwa">%<span class="keyword">%gs;
500 $highlighted_source =~ s%<span class="gtkdoc line">%<span class="linenum">%gs;
501 $highlighted_source =~ s%<span class="gtkdoc num">%<span class="number">%gs;
502 $highlighted_source =~ s%<span class="gtkdoc str">%<span class="string">%gs;
503 $highlighted_source =~ s%<span class="gtkdoc sym">%<span class="symbol">%gs;
505 # $highlighted_source =~ s%</span>(.+)<span%</span><span class="normal">$1</span><span%gs;
508 unlink ($temp_source_file)
509 || die "Can't delete $temp_source_file: $!";
511 return &HighlightSourcePostprocess($type, $highlighted_source);
515 sub HighlightSourceVim {
516 my ($type, $source) = @_;
518 # chop of leading and trailing empty lines
519 $source =~ s/^\s*\n+//gs;
520 $source =~ s/[\s\n]+$//gs;
522 $source =~ m/^(\s*)/;
523 $source =~ s/^$1//gms;
524 # avoid double entity replacement
525 $source =~ s/</</g;
526 $source =~ s/>/>/g;
527 $source =~ s/&/&/g;
529 # write source to a temp file
530 my $temp_source_file="$MODULE_DIR/_temp_src.$$.h";
531 open (NEWFILE, ">$temp_source_file") || die "Can't open $temp_source_file: $!";
532 print NEWFILE $source;
536 system "echo 'let html_number_lines=0|let html_use_css=1|let html_use_xhtml=1|e $temp_source_file|syn on|set syntax=$SRC_LANG|run! syntax/2html.vim|w! $temp_source_file.html|qa!' | @HIGHLIGHT@ -n -e -u NONE -T xterm >/dev/null";
538 my $highlighted_source;
541 open (NEWFILE, "<$temp_source_file.html");
542 $highlighted_source = <NEWFILE>;
545 $highlighted_source =~ s#.*<pre\b[^>]*>\n##s;
546 $highlighted_source =~ s#</pre>.*##s;
548 # need to rewrite the stylesheet classes
549 # FIXME: Vim has somewhat different syntax groups
550 $highlighted_source =~ s%<span class="Comment">%<span class="comment">%gs;
551 $highlighted_source =~ s%<span class="PreProc">%<span class="preproc">%gs;
552 $highlighted_source =~ s%<span class="Statement">%<span class="keyword">%gs;
553 $highlighted_source =~ s%<span class="Identifier">%<span class="function">%gs;
554 $highlighted_source =~ s%<span class="Constant">%<span class="number">%gs;
555 $highlighted_source =~ s%<span class="Special">%<span class="symbol">%gs;
556 $highlighted_source =~ s%<span class="Type">%<span class="type">%gs;
559 unlink ($temp_source_file)
560 || die "Can't delete $temp_source_file: $!";
561 unlink ("$temp_source_file.html")
562 || die "Can't delete $temp_source_file.html: $!";
564 return &HighlightSourcePostprocess($type, $highlighted_source);
568 sub HighlightSourcePostprocess {
569 my ($type, $highlighted_source) = @_;
571 # chop of leading and trailing empty lines
572 $highlighted_source =~ s/^[\s\n]+//gs;
573 $highlighted_source =~ s/[\s\n]+$//gs;
575 # turn common urls in comments into links
576 $highlighted_source =~ s%<span class="url">(.*?)</span>%<span class="url"><a href="$1">$1</a></span>%gs;
578 # we do own line-numbering
580 my $line_count = () = $highlighted_source =~ /\n/gs;
581 for (my $i=1; $i < ($line_count+2); $i++) {
582 $source_lines.="$i\n";
584 $source_lines =~ s/\n\Z//;
588 <table class="listing_frame" border="0" cellpadding="0" cellspacing="0">
591 <td class="listing_lines" align="right"><pre>$source_lines</pre></td>
592 <td class="listing_code"><pre class="programlisting">$highlighted_source</pre></td>