tools: append to the perl path so that it stays overrideable. Fixes #604892
[gtk-doc.git] / gtkdoc-fixxref.in
blobdb56804b194de77cd32efc2c024e95e59c771a2e
1 #!@PERL@ -w
2 # -*- cperl -*-
4 # gtk-doc - GTK DocBook documentation generator.
5 # Copyright (C) 1998  Damon Chaplin
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.
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.
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.
22 #############################################################################
23 # Script      : gtkdoc-fixxref
24 # Description : This fixes cross-references in the HTML documentation.
25 #############################################################################
27 use strict;
28 use bytes;
29 use Getopt::Long;
31 push @INC, '@PACKAGE_DATA_DIR@';
32 require "gtkdoc-common.pl";
34 # Options
36 # name of documentation module
37 my $MODULE;
38 my $MODULE_DIR;
39 my $HTML_DIR = "";
40 my @EXTRA_DIRS;
41 my $PRINT_VERSION;
42 my $PRINT_HELP;
44 my %optctl = ('module' => \$MODULE,
45               'module-dir' => \$MODULE_DIR,
46               'html-dir' => \$HTML_DIR,
47               'extra-dir' => \@EXTRA_DIRS,
48               'version' => \$PRINT_VERSION,
49               'help' => \$PRINT_HELP);
50 GetOptions(\%optctl, "module=s", "module-dir=s", "html-dir:s", "extra-dir=s@",
51         "version", "help");
53 if ($PRINT_VERSION) {
54     print "@VERSION@\n";
55     exit 0;
58 if ($PRINT_HELP) {
59         print <<EOF;
60 gtkdoc-fixxref version @VERSION@ - fix cross references in html files
62 --module=MODULE_NAME    Name of the doc module being parsed
63 --module-dir=MODULE_DIR The directory which contains the generated HTML
64 --html-dir=HTML_DIR     The directory where gtk-doc generated documentation is
65                         installed
66 --extra-dir=EXTRA_DIR   Directories to recursively scan for indices (index.sgml)
67                         in addition to HTML_DIR
68                         May be used more than once for multiple directories
69 --version               Print the version of this program
70 --help                  Print this help
71 EOF
72     exit 0;
75 # This contains all the entities and their relative URLs.
76 my %Links;
77 # This hold the path entries we already scanned
78 my @VisitedPaths;
80 # failing link targets we don't warn about even once
81 my %NoLinks = (
82     'char'  => 1,
83     'double'  => 1,
84     'float'  => 1,
85     'int'  => 1,
86     'long'  => 1,
87     'main'  => 1,
88     'signed'  => 1,
89     'unsigned'  => 1,
90     'va-list' => 1,
91     'void'  => 1,
92     'GInterface' => 1
93     );
95 my $path_prefix="";
96 if ($HTML_DIR =~ m%(.*?)/share/gtk-doc/html%) {
97     $path_prefix=$1;
98     #print "Path prefix: $path_prefix\n";
101 if (!defined $MODULE_DIR) {
102   $MODULE_DIR="$HTML_DIR/$MODULE";
105 my $dir;
107 # We scan the directory containing GLib and any directories in GNOME2_PATH
108 # first, but these will be overriden by any later scans.
109 $dir = `pkg-config --variable=prefix glib-2.0`;
110 $dir =~ s/\s+$//;
111 $dir = $dir . "/share/gtk-doc/html";
112 if (-d $dir && $dir ne $HTML_DIR) {
113     #print "Scanning GLib directory: $dir\n";
114     if ($dir !~ m%^\Q$path_prefix\E/%) {
115         &ScanIndices ($dir, 1);
116     } else {
117         &ScanIndices ($dir, 0);
118     }
119     push (@VisitedPaths, $dir);
122 if (defined ($ENV{"GNOME2_PATH"})) {
123     foreach $dir (split (/:/, $ENV{"GNOME2_PATH"})) {
124         $dir = $dir . "/share/gtk-doc/html";
125         if (-d $dir && $dir ne $HTML_DIR) {
126             #print "Scanning GNOME2_PATH directory: $dir\n";
127             if ($dir !~ m%^\Q$path_prefix\E/%) {
128                 &ScanIndices ($dir, 1);
129             } else {
130                 &ScanIndices ($dir, 0);
131             }
132             push (@VisitedPaths, $dir);
133         }
134         # ubuntu started to compress this as index.sgml.gz :/
135         # https://bugs.launchpad.net/ubuntu/+source/gtk-doc/+bug/77138
136     }
139 #print "Scanning HTML_DIR directory: $HTML_DIR\n";
140 &ScanIndices ($HTML_DIR, 0);
141 push (@VisitedPaths, $HTML_DIR);
142 #print "Scanning HTML_DIR directory: $MODULE_DIR\n";
143 &ScanIndices ($MODULE_DIR, 0);
144 push (@VisitedPaths, $MODULE_DIR);
146 # check all extra dirs, but skip already scanned dirs or subdirs of those
147 foreach my $dir (@EXTRA_DIRS) {
148     my $vdir;
149     my $skip = 0;
151     foreach $vdir (@VisitedPaths) {
152         if ($dir eq $vdir || $dir =~ m%^\Q$vdir\E/%) {
153             #print "Skipping EXTRA_DIR directory: $dir\n";
154             $skip=1;
155         }
156     }
157     next if $skip;
158     #print "Scanning EXTRA_DIR directory: $dir\n";
159     push (@VisitedPaths, $dir);
161     # If the --extra-dir option is not relative and is not sharing the same
162     # prefix as the target directory of the docs, we need to use absolute
163     # directories for the links
164     if ($dir !~m/^\.\./ &&  $dir !~ m%\Q$path_prefix\E/%) {
165         &ScanIndices ($dir, 1);
166     } else {
167         &ScanIndices ($dir, 0);
168     }
171 open (INPUT, "$MODULE-sections.txt")
172         || die "Can't open $MODULE-sections.txt: $!";
173 my $subsection = "";
174 while (<INPUT>) {
175     if (m/^#/) {
176         next;
178     } elsif (m/^<SECTION>/) {
179         $subsection = "";
180     } elsif (m/^<SUBSECTION\s*(.*)>/i) {
181         $subsection = $1;
182     } elsif (m/^<SUBSECTION>/) {
183         next;
184     } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
185         next;
186     } elsif (m/^<FILE>(.*)<\/FILE>/) {
187         next;
188     } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
189         next;
190     } elsif (m/^<\/SECTION>/) {
191         next;
192     } elsif (m/^(\S+)/) {
193         my $symbol=CreateValidSGMLID($1);
195         if ($subsection eq "Standard" || $subsection eq "Private") {
196             $NoLinks{$symbol} = 1;
197         }
198     }
200 close (INPUT);
202 &FixCrossReferences ($MODULE_DIR);
204 sub ScanIndices {
205     my ($scan_dir, $use_absolute_links) = @_;
207     #print "Scanning source directory: $scan_dir absolute: $use_absolute_links\n";
209     # This array holds any subdirectories found.
210     my (@subdirs) = ();
212     opendir (HTMLDIR, $scan_dir) || return;
213     my $file;
214     foreach $file (readdir (HTMLDIR)) {
215         if ($file eq '.' || $file eq '..') {
216             next;
217         } elsif (-d "$scan_dir/$file") {
218             push (@subdirs, $file);
219         } elsif ($file eq "index.sgml") {
220             &ScanIndex ("$scan_dir/$file", $use_absolute_links);
221         }
222         # ubuntu started to compress this as index.sgml.gz :/
223         # https://bugs.launchpad.net/ubuntu/+source/gtk-doc/+bug/77138
224     }
225     closedir (HTMLDIR);
227     # Now recursively scan the subdirectories.
228     my $dir;
229     foreach $dir (@subdirs) {
230         &ScanIndices ("$scan_dir/$dir", $use_absolute_links);
231     }
234 sub ScanIndex {
235     my ($file, $use_absolute_links) = @_;
236     #print "Scanning index file: $file absolute: $use_absolute_links\n";
238     # Determine the absolute directory, to be added to links in index.sgml
239     # if we need to use an absolute link.
240     # $file will be something like /opt/gnome/share/gtk-doc/html/gtk/index.sgml
241     # We want the part up to 'html' since the links in index.sgml include
242     # the rest.
243     my $dir = "../";
244     if ($use_absolute_links) {
245         $file =~ /(.*\/)(.*?)\/index\.sgml/;
246         $dir = $1;
247     }
249     open (INDEXFILE, $file)
250         || die "Can't open $file: $!";
251     while (<INDEXFILE>) {
252         if (m/^<ANCHOR\s+id\s*=\s*"([^"]*)"\s+href\s*=\s*"([^"]*)"\s*>/) {
253             #print "Found id: $1 href: $2\n";
254             $Links{$1} = "$dir$2";
255         }
256     }
257     close (INDEXFILE);
261 sub FixCrossReferences {
262     my ($scan_dir) = @_;
264     opendir (HTMLDIR, $scan_dir)
265         || die "Can't open HTML directory $scan_dir: $!";
266     my $file;
267     foreach $file (readdir (HTMLDIR)) {
268         if ($file eq '.' || $file eq '..') {
269             next;
270         } elsif ($file =~ m/.html?$/) {
271             &FixHTMLFile ("$scan_dir/$file");
272         }
273     }
274     closedir (HTMLDIR);
278 sub FixHTMLFile {
279     my ($file) = @_;
280     #print "Fixing file: $file\n";
282     open (HTMLFILE, $file)
283         || die "Can't open $file: $!";
284     undef $/;
285     my $entire_file = <HTMLFILE>;
286     close (HTMLFILE);
287     
288     if ("@HIGHLIGHT@" ne "") {
289         if ("@HIGHLIGHT@" =~ m%/vim$%) {
290             $entire_file =~ s%<div class=\"(example-contents|informalexample)\"><pre class=\"programlisting\">(.*?)</pre></div>%&HighlightSourceVim($1,$2);%gse;
291         }
292         else {
293             $entire_file =~ s%<div class=\"(example-contents|informalexample)\"><pre class=\"programlisting\">(.*?)</pre></div>%&HighlightSource($1,$2);%gse;
294         }
295         # this just broke existing GTKDOCLINK tags
296         # &lt;GTKDOCLINK HREF=&quot;GST-PAD-SINK:CAPS&quot;&gt;GST_PAD_SINK&lt;/GTKDOCLINK&gt;
297         $entire_file =~ s%\&lt;GTKDOCLINK\s+HREF=\&quot;(.*?)\&quot;\&gt;(.*?)\&lt;/GTKDOCLINK\&gt;%\<GTKDOCLINK\ HREF=\"$1\"\>$2\</GTKDOCLINK\>%gs;
298         
299         # from the highlighter we get all the functions marked up
300         # now we could turn them into GTKDOCLINK items
301         $entire_file =~ s%(<span class=\"function\">)(.*?)(</span>)%&MakeGtkDocLink($1,$2,$3);%gse;
302         # we could also try the first item in stuff marked up as 'normal'
303         $entire_file =~ s%(<span class=\"normal\">\s*)(.+?)((\s+.+?)?\s*</span>)%&MakeGtkDocLink($1,$2,$3);%gse;
304     }
306     my @lines = split(/\n/, $entire_file);
307     for (my $i=0; $i<$#lines; $i++) {
308         $lines[$i] =~ s%<GTKDOCLINK\s+HREF="([^"]*)"\s*>(.*?)</GTKDOCLINK\s*>% &MakeXRef($file,$i+1,$1,$2); %ge;
309         #if ($lines[$i] =~ m/GTKDOCLINK/) {
310         #    print "make xref failed for line: ",$lines[$i], "\n";
311         #}
312     }
313     $entire_file = join("\n",@lines);
315     open (NEWFILE, ">$file.new")
316         || die "Can't open $file: $!";
317     print NEWFILE $entire_file;
318     close (NEWFILE);
320     unlink ($file)
321         || die "Can't delete $file: $!";
322     rename ("$file.new", $file)
323         || die "Can't rename $file.new: $!";
326 sub MakeXRef {
327     my ($file, $line, $id, $text) = @_;
329     my $href = $Links{$id};
330     
331     # this is a workaround for some inconsitency we have with CreateValidSGMLID
332     if (!$href && $id =~ m/:/) {
333         my $tid = $id;
334         $tid =~ s/:/--/g;
335         $href = $Links{$tid};
336     }
337     # poor mans plural support
338     if (!$href && $id =~ m/s$/) {
339         my $tid = $id;
340         $tid =~ s/s$//g;
341         $href = $Links{$tid};
342     }
344     if ($href) {
345         # if it is a link to same module, remove path to make it work
346         # uninstalled
347         if (defined($MODULE) && $href =~ m%^\.\./$MODULE/(.*)$%) {
348             $href=$1;
349         }
350         #print "  Fixing link: $id, $href, $text\n";
351         return "<a href=\"$href\">$text</a>";
352     } else {
353         my $warn = 1;
354         #print "  no link for: $id, $text\n";
356         # don't warn multiple times and also skip blacklisted (ctypes)
357         $warn = 0 if exists $NoLinks{$id};
358         # if its a function, don't warn if it does not contain a "_"
359         # - gnome coding style would use '_'
360         # - will avoid wrong warnings for ansi c functions
361         $warn = 0 if ($text =~ m/ class=\"function\"/ && $id !~ m/_/);
362         # if its a 'return value', don't warn (implizitely created link)
363         $warn = 0 if ($text =~ m/ class=\"returnvalue\"/);
364         # if its a 'type', don't warn if it starts with lowercase
365         # - gnome coding style would use CamelCase
366         $warn = 0 if ($text =~ m/ class=\"type\"/ && ($id =~ m/^[a-z]/));
368         if ($warn == 1) {
369           &LogWarning ($file, $line, "no link for: '$id' -> ($text).");
370           $NoLinks{$id} = 1;
371         }
372         return $text;
373     }
377 sub MakeGtkDocLink {
378     my ($pre,$symbol,$post) = @_;
380     my $id=CreateValidSGMLID($symbol);
382     # these are implicitely created links in highlighed sources
383     # we don't want warnings for those if the links cannot be resolved.
384     $NoLinks{$id} = 1;
386     #return "<span class=\"$type\"><GTKDOCLINK HREF=\"$id\">$symbol</GTKDOCLINK></span>";
387     return "$pre<GTKDOCLINK HREF=\"$id\">$symbol</GTKDOCLINK>$post";
391 sub HighlightSource {
392     my ($type, $source) = @_;
394     # chop of leading and trailing empty lines
395     $source =~ s/^\s*\n+//gs;
396     $source =~ s/[\s\n]+$//gs;
397     # cut common indent
398     $source =~ m/^(\s*)/;
399     $source =~ s/^$1//gms;
400     # avoid double entity replacement
401     $source =~ s/&lt;/</g;
402     $source =~ s/&gt;/>/g;
403     $source =~ s/&amp;/&/g;
405     # write source to a temp file
406     # FIXME: use .c for now to hint the language to the highlighter
407     my $temp_source_file="$MODULE_DIR/_temp_src.$$.c";
408     open (NEWFILE, ">$temp_source_file") || die "Can't open $temp_source_file: $!";
409     print NEWFILE $source;
410     close (NEWFILE);
411     
412     #print" running @HIGHLIGHT@ @HIGHLIGHT_OPTIONS@$temp_source_file \n";
413     
414     # format source
415     my $highlighted_source=`@HIGHLIGHT@ @HIGHLIGHT_OPTIONS@$temp_source_file`;
416     if ("@HIGHLIGHT@" =~ m%/source-highlight$%) {
417         $highlighted_source =~ s%^<\!-- .*? -->%%gs;
418         $highlighted_source =~ s%<pre><tt>(.*?)</tt></pre>%$1%gs;
419     }
420     elsif ("@HIGHLIGHT@" =~ m%/highlight$%) {
421         # need to rewrite the stylesheet classes
422         $highlighted_source =~ s%<span class="gtkdoc com">%<span class="comment">%gs;
423         $highlighted_source =~ s%<span class="gtkdoc dir">%<span class="preproc">%gs;
424         $highlighted_source =~ s%<span class="gtkdoc kwd">%<span class="function">%gs;
425         $highlighted_source =~ s%<span class="gtkdoc kwa">%<span class="keyword">%gs;
426         $highlighted_source =~ s%<span class="gtkdoc line">%<span class="linenum">%gs;
427         $highlighted_source =~ s%<span class="gtkdoc num">%<span class="number">%gs;
428         $highlighted_source =~ s%<span class="gtkdoc str">%<span class="string">%gs;
429         $highlighted_source =~ s%<span class="gtkdoc sym">%<span class="symbol">%gs;
430         # maybe also do
431         # $highlighted_source =~ s%</span>(.+)<span%</span><span class="normal">$1</span><span%gs;
432     }
433     # remove temp file
434     unlink ($temp_source_file)
435         || die "Can't delete $temp_source_file: $!";
437     return &HighlightSourcePostprocess($type, $highlighted_source);
440 sub HighlightSourceVim {
441     my ($type, $source) = @_;
443     # chop of leading and trailing empty lines
444     $source =~ s/^[\s\n]+//gs;
445     $source =~ s/[\s\n]+$//gs;
446     # avoid double entity replacement
447     $source =~ s/&lt;/</g;
448     $source =~ s/&gt;/>/g;
449     $source =~ s/&amp;/&/g;
451     # write source to a temp file
452     my $temp_source_file="$MODULE_DIR/_temp_src.$$.h";
453     open (NEWFILE, ">$temp_source_file") || die "Can't open $temp_source_file: $!";
454     print NEWFILE $source;
455     close (NEWFILE);
457     # format source
458     system "echo 'let html_number_lines=0|let html_use_css=1|let use_xhtml=1|syn on|e $temp_source_file|run! syntax/2html.vim|wa!|qa!' | @HIGHLIGHT@ -n -e -u /dev/null -T xterm >/dev/null";
460     my $highlighted_source;
461     {
462         local $/;
463         open (NEWFILE, "<$temp_source_file.html");
464         $highlighted_source = <NEWFILE>;
465         close (NEWFILE);
466     }
467     $highlighted_source =~ s#.*<pre>\n##s;
468     $highlighted_source =~ s#</pre>.*##s;
470     # need to rewrite the stylesheet classes
471     # FIXME: Vim has somewhat different syntax groups
472     $highlighted_source =~ s%<span class="Comment">%<span class="comment">%gs;
473     $highlighted_source =~ s%<span class="PreProc">%<span class="preproc">%gs;
474     $highlighted_source =~ s%<span class="Statement">%<span class="keyword">%gs;
475     $highlighted_source =~ s%<span class="Identifier">%<span class="function">%gs;
476     $highlighted_source =~ s%<span class="Constant">%<span class="number">%gs;
477     $highlighted_source =~ s%<span class="Special">%<span class="symbol">%gs;
478     $highlighted_source =~ s%<span class="Type">%<span class="type">%gs;
480     # remove temp files
481     unlink ($temp_source_file)
482         || die "Can't delete $temp_source_file: $!";
483     unlink ("$temp_source_file.html")
484         || die "Can't delete $temp_source_file.html: $!";
486     return &HighlightSourcePostprocess($type, $highlighted_source);
489 sub HighlightSourcePostprocess {
490     my ($type, $highlighted_source) = @_;
492     # chop of leading and trailing empty lines
493     $highlighted_source =~ s/^[\s\n]+//gs;
494     $highlighted_source =~ s/[\s\n]+$//gs;
496     # turn common urls in comments into links
497     $highlighted_source =~ s%<span class="url">(.*?)</span>%<span class="url"><a href="$1">$1</a></span>%gs;
499     # we do own line-numbering
500     my $source_lines="";
501     my $line_count = () = $highlighted_source =~ /\n/gs;
502     for (my $i=1; $i < ($line_count+2); $i++) {
503         $source_lines.="$i\n";
504     }
505     $source_lines =~ s/\n\Z//;
507     return <<END_OF_HTML
508 <div class="$type">
509   <table class="listing_frame" border="0" cellpadding="0" cellspacing="0">
510     <tbody>
511       <tr>
512         <td class="listing_lines" align="right"><pre>$source_lines</pre></td>
513         <td class="listing_code"><pre class="programlisting">$highlighted_source</pre></td>
514       </tr>
515     </tbody>
516   </table>
517 </div>
518 END_OF_HTML