Updated Spanish translation
[gtk-doc.git] / gtkdoc-fixxref.in
blob3d9e8d0a1da0efbea501138a297878b3fd2f025f
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;
43 my $SRC_LANG;
45 # This contains all the entities and their relative URLs.
46 my %Links;
48 # failing link targets we don't warn about even once
49 my %NoLinks = (
50     'char' => 1,
51     'double' => 1,
52     'float' => 1,
53     'int' => 1,
54     'long' => 1,
55     'main' => 1,
56     'signed' => 1,
57     'unsigned' => 1,
58     'va-list' => 1,
59     'void' => 1,
60     'GBoxed' => 1,
61     'GEnum' => 1,
62     'GFlags' => 1,
63     'GInterface' => 1
66 # Cache of dirs we already scanned for index files
67 my %DirCache;
69 run() unless caller; # Run program unless loaded as a module
72 sub run {
73     my %optctl = ('module' => \$MODULE,
74                   'module-dir' => \$MODULE_DIR,
75                   'html-dir' => \$HTML_DIR,
76                   'extra-dir' => \@EXTRA_DIRS,
77                   'version' => \$PRINT_VERSION,
78                   'help' => \$PRINT_HELP,
79                   'src-lang' => \$SRC_LANG);
80     
81     GetOptions(\%optctl, "module=s", "module-dir=s", "html-dir:s", "extra-dir=s@",
82               "src-lang=s", "version", "help");
83     
84     if ($PRINT_VERSION) {
85         print "@VERSION@\n";
86         exit 0;
87     }
88     
89     if ($PRINT_HELP) {
90             print <<EOF;
91 gtkdoc-fixxref version @VERSION@ - fix cross references in html files
93 --module=MODULE_NAME    Name of the doc module being parsed
94 --module-dir=MODULE_DIR The directory which contains the generated HTML
95 --html-dir=HTML_DIR     The directory where gtk-doc generated documentation is
96                         installed
97 --extra-dir=EXTRA_DIR   Directories to recursively scan for indices (index.sgml)
98                         in addition to HTML_DIR
99                         May be used more than once for multiple directories
100 --src-lang=SRC_LANG     Programing language used for syntax highlighting. The
101                         available languages depend on the source source
102                         highlighter you use.
103 --version               Print the version of this program
104 --help                  Print this help
106         exit 0;
107     }
108     
109     if (!$SRC_LANG) {
110         $SRC_LANG="c"
111     }
112       
113     my $path_prefix="";
114     if ($HTML_DIR =~ m%(.*?)/share/gtk-doc/html%) {
115         $path_prefix=$1;
116         @TRACE@("Path prefix: $path_prefix");
117     }
118     
119     if (!defined $MODULE_DIR) {
120         $MODULE_DIR="$HTML_DIR/$MODULE";
121     }
122     
123     my $dir;
124     
125     # We scan the directory containing GLib and any directories in GNOME2_PATH
126     # first, but these will be overriden by any later scans.
127     $dir = `pkg-config --variable=prefix glib-2.0`;
128     $dir =~ s/\s+$//;
129     $dir = $dir . "/share/gtk-doc/html";
130     if (-d $dir) {
131         # Some predefined link targets to get links into type hierarchies as these
132         # have no targets. These are always absolute for now.
133         $Links{'GBoxed'} = "$dir/gobject/gobject-Boxed-Types.html";
134         $Links{'GEnum'} = "$dir/gobject/gobject-Enumeration-and-Flag-Types.html";
135         $Links{'GFlags'} = "$dir/gobject/gobject-Enumeration-and-Flag-Types.html";
136         $Links{'GInterface'} = "$dir/gobject/GTypeModule.html";
137     
138         if ($dir ne $HTML_DIR) {
139             @TRACE@("Scanning GLib directory: $dir");
140             if ($dir !~ m%^\Q$path_prefix\E/%) {
141                 &ScanIndices ($dir, 1);
142             } else {
143                 &ScanIndices ($dir, 0);
144             }
145         }
146     }
147     
148     if (defined ($ENV{"GNOME2_PATH"})) {
149         foreach $dir (split (/:/, $ENV{"GNOME2_PATH"})) {
150             $dir = $dir . "/share/gtk-doc/html";
151             if (-d $dir && $dir ne $HTML_DIR) {
152                 @TRACE@("Scanning GNOME2_PATH directory: $dir");
153                 if ($dir !~ m%^\Q$path_prefix\E/%) {
154                     &ScanIndices ($dir, 1);
155                 } else {
156                     &ScanIndices ($dir, 0);
157                 }
158             }
159             # ubuntu started to compress this as index.sgml.gz :/
160             # https://bugs.launchpad.net/ubuntu/+source/gtk-doc/+bug/77138
161         }
162     }
163     
164     @TRACE@("Scanning HTML_DIR directory: $HTML_DIR");
165     &ScanIndices ($HTML_DIR, 0);
166     @TRACE@("Scanning HTML_DIR directory: $MODULE_DIR");
167     &ScanIndices ($MODULE_DIR, 0);
168     
169     # check all extra dirs, but skip already scanned dirs or subdirs of those
170     foreach my $dir (@EXTRA_DIRS) {
171         my $vdir;
173         $dir =~ s#/$##;
174         @TRACE@("Scanning EXTRA_DIR directory: $dir");
175     
176         # If the --extra-dir option is not relative and is not sharing the same
177         # prefix as the target directory of the docs, we need to use absolute
178         # directories for the links
179         if ($dir !~m/^\.\./ &&  $dir !~ m%\Q$path_prefix\E/%) {
180             &ScanIndices ($dir, 1);
181         } else {
182             &ScanIndices ($dir, 0);
183         }
184     }
185     
186     &ReadSections ();
187     
188     &FixCrossReferences ($MODULE_DIR);
192 sub ScanIndices {
193     my ($scan_dir, $use_absolute_links) = @_;
194     
195     if (exists $DirCache{$scan_dir}) {
196         return;
197     }
198     $DirCache{$scan_dir} = 1;
200     @TRACE@("Scanning source directory: $scan_dir absolute: $use_absolute_links");
202     # This array holds any subdirectories found.
203     my (@subdirs) = ();
205     opendir (HTMLDIR, $scan_dir) || return;
206     my $file;
207     foreach $file (readdir (HTMLDIR)) {
208         if ($file eq '.' || $file eq '..') {
209             next;
210         } elsif (-d "$scan_dir/$file") {
211             push (@subdirs, $file);
212             next;
213         }
214         if ($file =~ m/\.devhelp2$/) {
215             # if devhelp-file is good don't read index.sgml
216             &ReadDevhelp ("$scan_dir/$file", $use_absolute_links);
217         }
218         elsif ($file eq "index.sgml.gz") {
219             # debian/ubuntu started to compress this as index.sgml.gz :/
220             print <<EOF;
221 Please fix https://bugs.launchpad.net/ubuntu/+source/gtk-doc/+bug/77138 . For now run:
222 gunzip $scan_dir/$file
224         }
225         elsif ($file =~ m/\.devhelp2.gz$/) {
226             # debian/ubuntu started to compress this as *devhelp2.gz :/
227             print <<EOF;
228 Please fix https://bugs.launchpad.net/ubuntu/+source/gtk-doc/+bug/1466210 . For now run:
229 gunzip $scan_dir/$file
231         }
232         # we could consider supporting: use IO::Zlib;
233     }
234     closedir (HTMLDIR);
236     # Now recursively scan the subdirectories.
237     my $dir;
238     foreach $dir (sort(@subdirs)) {
239         &ScanIndices ("$scan_dir/$dir", $use_absolute_links);
240     }
244 sub ReadDevhelp {
245     my ($file, $use_absolute_links) = @_;
247     # Determine the absolute directory, to be added to links in $file
248     # if we need to use an absolute link.
249     # $file will be something like /prefix/gnome/share/gtk-doc/html/gtk/$file
250     # We want the part up to 'html/.*' since the links in $file include
251     # the rest.
252     my $dir = "../";
253     if ($use_absolute_links) {
254         # For uninstalled index.sgml files we'd need to map the path to where it
255         # will be installed to
256         if ($file !~ /\.\//) {
257             $file =~ /(.*\/)(.*?)\/.*?\.devhelp2/;
258             $dir = "$1$2";
259         }
260     } else {
261         if ($file =~ /(.*\/)(.*?)\/.*?\.devhelp2/) {
262             $dir .= "$2/";
263         } else {
264             $dir = "";
265         }
266     }
268     @TRACE@("Scanning index file=$file, absolute=$use_absolute_links, dir=$dir");
270     open (INDEXFILE, $file)
271         || die "Can't open $file: $!";
272     while (<INDEXFILE>) {
273         if (m/ link="([^#]*)#([^"]*)"/) {
274             @TRACE@("Found id: $2 href: $1#$2");
275             $Links{$2} = "$dir$1#$2";
276         }
277     }
278     close (INDEXFILE);
282 sub ReadSections {
283     if (!defined($MODULE)) {
284         return;
285     }
287     open (INPUT, "$MODULE-sections.txt")
288             || die "Can't open $MODULE-sections.txt: $!";
289     my $subsection = "";
290     while (<INPUT>) {
291         if (m/^#/) {
292             next;
294         } elsif (m/^<SECTION>/) {
295             $subsection = "";
296         } elsif (m/^<SUBSECTION\s*(.*)>/i) {
297             $subsection = $1;
298         } elsif (m/^<SUBSECTION>/) {
299             next;
300         } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
301             next;
302         } elsif (m/^<FILE>(.*)<\/FILE>/) {
303             next;
304         } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
305             next;
306         } elsif (m/^<\/SECTION>/) {
307             next;
308         } elsif (m/^(\S+)/) {
309             my $symbol=$1;
311             if ($subsection eq "Standard" || $subsection eq "Private") {
312                 $NoLinks{CreateValidSGMLID($symbol)} = 1;
313             }
314         }
315     }
316     close (INPUT);
320 sub FixCrossReferences {
321     my ($scan_dir) = @_;
323     opendir (HTMLDIR, $scan_dir)
324         || die "Can't open HTML directory $scan_dir: $!";
325     my $file;
326     foreach $file (readdir (HTMLDIR)) {
327         if ($file eq '.' || $file eq '..') {
328             next;
329         } elsif ($file =~ m/.html?$/) {
330             &FixHTMLFile ("$scan_dir/$file");
331         }
332     }
333     closedir (HTMLDIR);
337 sub FixHTMLFile {
338     my ($file) = @_;
339     @TRACE@("Fixing file: $file");
341     open (HTMLFILE, $file)
342         || die "Can't open $file: $!";
343     undef $/;
344     my $entire_file = <HTMLFILE>;
345     close (HTMLFILE);
347     if ("@HIGHLIGHT@" ne "") {
348         # FIXME: ideally we'd pass a clue about the example language to the highligher
349         # unfortunately the "language" attribute is not appearing in the html output
350         # we could patch the customization to have <code class="xxx"> inside of <pre>
351         if ("@HIGHLIGHT@" =~ m%/vim$%) {
352             $entire_file =~ s%<div class=\"(example-contents|informalexample)\"><pre class=\"programlisting\">(.*?)</pre></div>%&HighlightSourceVim($1,$2);%gse;
353         }
354         else {
355             $entire_file =~ s%<div class=\"(example-contents|informalexample)\"><pre class=\"programlisting\">(.*?)</pre></div>%&HighlightSource($1,$2);%gse;
356         }
357         # this just broke existing GTKDOCLINK tags
358         # &lt;GTKDOCLINK HREF=&quot;GST-PAD-SINK:CAPS&quot;&gt;GST_PAD_SINK&lt;/GTKDOCLINK&gt;
359         $entire_file =~ s%\&lt;GTKDOCLINK\s+HREF=\&quot;(.*?)\&quot;\&gt;(.*?)\&lt;/GTKDOCLINK\&gt;%\<GTKDOCLINK\ HREF=\"$1\"\>$2\</GTKDOCLINK\>%gs;
361         # from the highlighter we get all the functions marked up
362         # now we could turn them into GTKDOCLINK items
363         $entire_file =~ s%(<span class=\"function\">)(.*?)(</span>)%&MakeGtkDocLink($1,$2,$3);%gse;
364         # we could also try the first item in stuff marked up as 'normal'
365         $entire_file =~ s%(<span class=\"normal\">\s*)(.+?)((\s+.+?)?\s*</span>)%&MakeGtkDocLink($1,$2,$3);%gse;
366     }
368     my @lines = split(/\n/, $entire_file);
369     for (my $i=0; $i<$#lines; $i++) {
370         $lines[$i] =~ s%<GTKDOCLINK\s+HREF="([^"]*)"\s*>(.*?)</GTKDOCLINK\s*>% &MakeXRef($file,$i+1,$1,$2); %ge;
371         #if ($lines[$i] =~ m/GTKDOCLINK/) {
372         #    print "make xref failed for line: ",$lines[$i], "\n";
373         #}
374     }
375     $entire_file = join("\n",@lines);
377     open (NEWFILE, ">$file.new")
378         || die "Can't open $file: $!";
379     print NEWFILE $entire_file;
380     close (NEWFILE);
382     unlink ($file)
383         || die "Can't delete $file: $!";
384     rename ("$file.new", $file)
385         || die "Can't rename $file.new: $!";
388 sub MakeXRef {
389     my ($file, $line, $id, $text) = @_;
391     my $href = $Links{$id};
393     # this is a workaround for some inconsistency we have with CreateValidSGMLID
394     if (!$href && $id =~ m/:/) {
395         my $tid = $id;
396         $tid =~ s/:/--/g;
397         $href = $Links{$tid};
398     }
399     # poor mans plural support
400     if (!$href && $id =~ m/s$/) {
401         my $tid = $id;
402         $tid =~ s/s$//g;
403         $href = $Links{$tid};
404         if (!$href && defined $Links{"$tid-struct"}) {
405             $href = $Links{"$tid-struct"};
406         }
407     }
408     if (!$href && defined $Links{"$id-struct"}) {
409         $href = $Links{"$id-struct"};
410     }
412     if ($href) {
413         # if it is a link to same module, remove path to make it work
414         # uninstalled
415         if (defined($MODULE) && $href =~ m%^\.\./$MODULE/(.*)$%) {
416             $href=$1;
417             @TRACE@("  Fixing link to uninstalled doc: $id, $href, $text");
418         } else {
419             @TRACE@("  Fixing link: $id, $href, $text");
420         }
421         return "<a href=\"$href\">$text</a>";
422     } else {
423         my $warn = 1;
424         @TRACE@("  no link for: $id, $text");
426         # don't warn multiple times and also skip blacklisted (ctypes)
427         $warn = 0 if exists $NoLinks{$id};
428         # if it's a function, don't warn if it does not contain a "_"
429         # (transformed to "-")
430         # - gnome coding style would use '_'
431         # - will avoid wrong warnings for ansi c functions
432         $warn = 0 if ($text =~ m/ class=\"function\"/ && $id !~ m/-/);
433         # if it's a 'return value', don't warn (implicitly created link)
434         $warn = 0 if ($text =~ m/ class=\"returnvalue\"/);
435         # if it's a 'type', don't warn if it starts with lowercase
436         # - gnome coding style would use CamelCase
437         $warn = 0 if ($text =~ m/ class=\"type\"/ && ($id =~ m/^[a-z]/));
438         # don't warn for self links
439         $warn = 0 if ($text eq $id);
441         if ($warn == 1) {
442           &LogWarning ($file, $line, "no link for: '$id' -> ($text).");
443           $NoLinks{$id} = 1;
444         }
445         return $text;
446     }
450 sub MakeGtkDocLink {
451     my ($pre,$symbol,$post) = @_;
453     my $id=CreateValidSGMLID($symbol);
455     # these are implicitely created links in highlighed sources
456     # we don't want warnings for those if the links cannot be resolved.
457     $NoLinks{$id} = 1;
459     #return "<span class=\"$type\"><GTKDOCLINK HREF=\"$id\">$symbol</GTKDOCLINK></span>";
460     return "$pre<GTKDOCLINK HREF=\"$id\">$symbol</GTKDOCLINK>$post";
464 sub HighlightSource {
465     my ($type, $source) = @_;
467     # chop of leading and trailing empty lines
468     $source =~ s/^\s*\n+//gs;
469     $source =~ s/[\s\n]+$//gs;
470     # cut common indent
471     $source =~ m/^(\s*)/;
472     $source =~ s/^$1//gms;
473     # avoid double entity replacement
474     $source =~ s/&lt;/</g;
475     $source =~ s/&gt;/>/g;
476     $source =~ s/&amp;/&/g;
478     # write source to a temp file
479     # FIXME: use .c for now to hint the language to the highlighter
480     my $temp_source_file="$MODULE_DIR/_temp_src.$$.c";
481     open (NEWFILE, ">$temp_source_file") || die "Can't open $temp_source_file: $!";
482     print NEWFILE $source;
483     close (NEWFILE);
485     @TRACE@(" running @HIGHLIGHT@ @HIGHLIGHT_OPTIONS@$temp_source_file ");
487     # format source
488     my $highlighted_source=`@HIGHLIGHT@ @HIGHLIGHT_OPTIONS@$temp_source_file`;
489     if ("@HIGHLIGHT@" =~ m%/source-highlight$%) {
490         $highlighted_source =~ s%^<\!-- .*? -->%%gs;
491         $highlighted_source =~ s%<pre><tt>(.*?)</tt></pre>%$1%gs;
492     }
493     elsif ("@HIGHLIGHT@" =~ m%/highlight$%) {
494         # need to rewrite the stylesheet classes
495         $highlighted_source =~ s%<span class="gtkdoc com">%<span class="comment">%gs;
496         $highlighted_source =~ s%<span class="gtkdoc dir">%<span class="preproc">%gs;
497         $highlighted_source =~ s%<span class="gtkdoc kwd">%<span class="function">%gs;
498         $highlighted_source =~ s%<span class="gtkdoc kwa">%<span class="keyword">%gs;
499         $highlighted_source =~ s%<span class="gtkdoc line">%<span class="linenum">%gs;
500         $highlighted_source =~ s%<span class="gtkdoc num">%<span class="number">%gs;
501         $highlighted_source =~ s%<span class="gtkdoc str">%<span class="string">%gs;
502         $highlighted_source =~ s%<span class="gtkdoc sym">%<span class="symbol">%gs;
503         # maybe also do
504         # $highlighted_source =~ s%</span>(.+)<span%</span><span class="normal">$1</span><span%gs;
505     }
506     # remove temp file
507     unlink ($temp_source_file)
508         || die "Can't delete $temp_source_file: $!";
510     return &HighlightSourcePostprocess($type, $highlighted_source);
514 sub HighlightSourceVim {
515     my ($type, $source) = @_;
517     # chop of leading and trailing empty lines
518     $source =~ s/^\s*\n+//gs;
519     $source =~ s/[\s\n]+$//gs;
520     # cut common indent
521     $source =~ m/^(\s*)/;
522     $source =~ s/^$1//gms;
523     # avoid double entity replacement
524     $source =~ s/&lt;/</g;
525     $source =~ s/&gt;/>/g;
526     $source =~ s/&amp;/&/g;
528     # write source to a temp file
529     my $temp_source_file="$MODULE_DIR/_temp_src.$$.h";
530     open (NEWFILE, ">$temp_source_file") || die "Can't open $temp_source_file: $!";
531     print NEWFILE $source;
532     close (NEWFILE);
534     # format source
535     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";
537     my $highlighted_source;
538     {
539         local $/;
540         open (NEWFILE, "<$temp_source_file.html");
541         $highlighted_source = <NEWFILE>;
542         close (NEWFILE);
543     }
544     $highlighted_source =~ s#.*<pre\b[^>]*>\n##s;
545     $highlighted_source =~ s#</pre>.*##s;
547     # need to rewrite the stylesheet classes
548     # FIXME: Vim has somewhat different syntax groups
549     $highlighted_source =~ s%<span class="Comment">%<span class="comment">%gs;
550     $highlighted_source =~ s%<span class="PreProc">%<span class="preproc">%gs;
551     $highlighted_source =~ s%<span class="Statement">%<span class="keyword">%gs;
552     $highlighted_source =~ s%<span class="Identifier">%<span class="function">%gs;
553     $highlighted_source =~ s%<span class="Constant">%<span class="number">%gs;
554     $highlighted_source =~ s%<span class="Special">%<span class="symbol">%gs;
555     $highlighted_source =~ s%<span class="Type">%<span class="type">%gs;
557     # remove temp files
558     unlink ($temp_source_file)
559         || die "Can't delete $temp_source_file: $!";
560     unlink ("$temp_source_file.html")
561         || die "Can't delete $temp_source_file.html: $!";
563     return &HighlightSourcePostprocess($type, $highlighted_source);
567 sub HighlightSourcePostprocess {
568     my ($type, $highlighted_source) = @_;
570     # chop of leading and trailing empty lines
571     $highlighted_source =~ s/^[\s\n]+//gs;
572     $highlighted_source =~ s/[\s\n]+$//gs;
574     # turn common urls in comments into links
575     $highlighted_source =~ s%<span class="url">(.*?)</span>%<span class="url"><a href="$1">$1</a></span>%gs;
577     # we do own line-numbering
578     my $source_lines="";
579     my $line_count = () = $highlighted_source =~ /\n/gs;
580     for (my $i=1; $i < ($line_count+2); $i++) {
581         $source_lines.="$i\n";
582     }
583     $source_lines =~ s/\n\Z//;
585     return <<END_OF_HTML
586 <div class="$type">
587   <table class="listing_frame" border="0" cellpadding="0" cellspacing="0">
588     <tbody>
589       <tr>
590         <td class="listing_lines" align="right"><pre>$source_lines</pre></td>
591         <td class="listing_code"><pre class="programlisting">$highlighted_source</pre></td>
592       </tr>
593     </tbody>
594   </table>
595 </div>
596 END_OF_HTML