4 # gtk-doc - GTK DocBook documentation generator.
5 # Copyright (C) 1998 Damon Chaplin
6 # 2007 David Necas (Yeti)
7 # 2007-2016 Stefan Sauer
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program; if not, write to the Free Software
21 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #############################################################################
25 # Script : gtkdoc-rebase
26 # Description : Rebases URI references in installed HTML documentation.
27 #############################################################################
29 from __future__ import print_function
31 import os, sys, argparse, subprocess, re
34 # These two point to the last seen URI of given type for a package:
35 # OnlineMap: package => on-line URI
36 # LocalMap: package => local URI
37 # This maps all seen URIs of a package to fix broken links in the process:
38 # RevMap: URI => package
42 # Remember what mangling we did.
46 parser = argparse.ArgumentParser()
48 parser.add_argument('--version', action='version', version='@VERSION@')
49 parser.add_argument('--html-dir', dest='html_dir', default='')
50 parser.add_argument('--other-dir', dest='other_dir', default=[], action='append')
51 parser.add_argument('--dest-dir', dest='dest_dir', default='')
52 parser.add_argument('--aggressive', action='store_true', default=False)
53 parser.add_argument('--verbose', action='store_true', default=False)
54 group = parser.add_mutually_exclusive_group()
55 group.add_argument('--online', action='store_true', default=False)
56 group.add_argument('--relative', action='store_true', default=False)
58 def log(options, *msg):
63 options = parser.parse_args()
66 if (options.html_dir == ''):
67 sys.exit("No HTML directory (--html-dir) given.")
69 # We scan the directory containing GLib and any directories in GNOME2_PATH
70 # first, but these will be overriden by any later scans.
71 if "GNOME2_PATH" in os.environ:
72 for dir in os.environ["GNOME2_PATH"].split(':'):
73 dir = os.path.join(dir, "/share/gtk-doc/html")
74 if os.path.isdir(dir):
75 log(options, "Prepending GNOME2_PATH directory:", dir)
76 other_dirs = [dir] + other_dirs
78 dir = subprocess.check_output(['@PKG_CONFIG@', '--variable=prefix', 'glib-2.0'], universal_newlines=True)
80 dir = os.path.join(dir, "/share/gtk-doc/html")
81 log(options, "Prepending GLib directory", dir)
82 other_dirs = [dir] + other_dirs
84 # Check all other dirs, but skip already scanned dirs ord subdirs of those
86 for dir in other_dirs:
87 ScanDirectory(dir, options);
90 RelativizeLocalMap(options.html_dir, options);
92 RebaseReferences(options.html_dir, options)
96 def ScanDirectory(dir, options):
97 # This array holds any subdirectories found.
101 log(options, "Scanning documentation directory " + dir)
103 if dir == options.html_dir:
104 log(options, "Excluding self")
107 if not os.path.isdir(dir):
108 print('Cannot open dir "%s"' % dir)
112 for entry in os.listdir(dir):
113 if os.path.isdir(entry):
114 subdirs.push_back(entry)
117 if entry.endswith('.devhelp2'):
118 log(options, "Reading index from " + entry)
119 o = ReadDevhelp(dir, entry);
120 # Prefer this location over possibly stale index.sgml
125 if onlinedir and entry == "index.sgml":
126 log(options, "Reading index from index.sgml")
127 onlinedir = ReadIndex(dir, entry);
129 elif entry == "index.sgml.gz" and not os.path.exists(os.path.join(dir, 'index.sgml')):
130 # debian/ubuntu started to compress this as index.sgml.gz :/
131 print(''' Please fix https://bugs.launchpad.net/ubuntu/+source/gtk-doc/+bug/77138 . For now run:
134 elif entry.endswith('.devhelp2.gz') and not os.path.exists(os.path.join(dir, entry, 'devhelp2')):
135 # debian/ubuntu started to compress this as *devhelp2.gz :/
136 print('''Please fix https://bugs.launchpad.net/ubuntu/+source/gtk-doc/+bug/1466210 . For now run:
139 # we could consider supporting: gzip module
142 AddMap(dir, onlinedir);
144 # Now recursively scan the subdirectories.
145 for subdir in subdirs:
146 ScanDirectory(os.path.join(dir, subdir), options);
149 def ReadDevhelp(dir, file):
152 for line in open(os.path.join(dir, file)):
153 # online must come before chapter/functions
154 if '<chapters' in line or '<functions' in line:
156 match = re.search(r' online="([^"]*)"/')
158 # Remove trailing non-directory component.
159 onlinedir = re.sub(r'(.*/).*', r'\1', match.groups(1))
163 def ReadIndex(dir, file):
166 for line in open(os.path.join(dir, file)):
167 # ONLINE must come before any ANCHORs
168 if '<ANCHOR' in line:
170 match = re.match(r'''^<ONLINE\s+href\s*=\s*"([^"]+)"\s*>''', line)
172 # Remove trailing non-directory component.
173 onlinedir = re.sub(r'''(.*/).*''', r'\1', match.groups(1))
177 def AddMap(dir, onlinerdir, options):
180 package = os.path.split(dir)[1]
181 if options.dest_dir != '' and dir.startswith(options.dest_dir):
182 dir = dir[len(options.dest_dir)-1:]
185 log(options, "On-line location of %s." % onlinedir)
186 OnlineMap[package] = onlinedir;
187 RevMap[onlinedir] = package;
189 log(options, "No On-line location for %s found" % package)
191 log(options, "Local location of $package: " + dir)
192 LocalMap[package] = dir;
193 RevMap[dir] = package;
196 def RelativizeLocalMap(dirname, options):
200 dirname = os.path.realpath(dirname)
201 prefix = os.path.split(dirname)
202 for package, dir in LocalMap.items():
203 if dir.startswith(prefix):
204 dir = os.path.join("..", dir[len(prefix):])
205 LocalMap[package] = dir
206 log(options, "Relativizing local location of $package to " + dir)
208 def RebaseReferences(dirname, options):
209 for ifile in os.listdir(dirname):
210 if ifile.endswith('.html'):
211 RebaseFile(os.path.join(dirname, ifile), options)
214 def RebaseFile(filename, options):
215 log(options, "Fixing file: " + filename)
216 regex = re.compile(r'''(<a(?:\s+\w+=(?:"[^"]*"|'[^']*'))*\s+href=")([^"]*)(")''',
219 def repl_func(match):
220 return match.group(1) + RebaseLink(match.group(2), options) + match.group(3)
222 contents = open(filename).read()
223 processed = re.sub(regex, repl_func, contents)
224 newfilename = filename + '.new'
225 open(newfilename, 'w').write(processed)
227 os.rename(newfilename, filename)
230 def RebaseLink(href, options):
231 match = re.match(r'^(.*/)([^/]*)$', href)
236 dir = origdir = match.group(1)
237 file = match.group(2)
239 package = RevMap[dir]
241 match = re.match(r'\.\./([^/]+)', href)
242 if match is not None:
243 package = match.groups(1)
244 elif options.aggressive:
245 match = re.search(r'''([^/]+)/$''', href)
246 package = match.groups(1);
249 if options.online and package in OnlineMap:
250 dir = OnlineMap[package]
251 elif package in LocalMap:
252 dir = LocalMap[package]
253 href = os.path.join(dir, file)
255 log(options, "Can't determine package for '%s'" % href);
258 if origdir in Mapped:
259 Mapped[origdir][1] += 1
261 Mapped[origdir] = [dir, 1]
265 def PrintWhatWeHaveDone():
266 for origdir in sorted(Mapped.keys()):
267 info = Mapped[origdir]
268 print(origdir, "->", info[0], "(%s)" % info[1])
270 if __name__== '__main__':