File: Synopsis/SXRServer.py
  1#
  2# Copyright (C) 2006 Stefan Seefeld
  3# All rights reserved.
  4# Licensed to the public under the terms of the GNU LGPL (>= 2),
  5# see the file COPYING for details.
  6#
  7
  8from Synopsis import IR
  9import os, fnmatch
 10
 11def escape(text):
 12    """escape special characters ('&', '\"', '<', '>')"""
 13
 14    text = text.replace('&', '&amp;')
 15    text = text.replace('"', '&quot;')
 16    text = text.replace('<', '&lt;')
 17    text = text.replace('>', '&gt;')
 18    return text
 19
 20default_template = """
 21<html>
 22  <head><title>Synopsis Cross-Reference</title></head>
 23  <body>
 24    <h1>Synopsis Cross-Reference</h1>
 25    @CONTENT@
 26  </body>
 27</html>
 28"""
 29
 30file_search_form = """
 31<table class=\"form\">
 32  <tr>
 33    <td>Enter a file name to search:</td>
 34    <td>
 35      <form method=\"get\" action=\"%(script)s/file\">
 36        <input type="text" name="string" value="" size="15"/>
 37        <input type="submit" value="Find"/>
 38      </form>
 39    </td>
 40  </tr>
 41</table>
 42"""
 43
 44ident_search_form = """
 45<table class=\"form\">
 46  <tr>
 47    <td>Enter a variable, type, or function name to search:</td>
 48    <td>
 49      <form method=\"get\" action=\"%(script)s/ident\">
 50        <input type="text" name="string" value="" size="15"/>
 51        <input type="submit" value="Find"/>
 52      </form>
 53    </td>
 54  </tr>
 55</table>
 56"""
 57
 58class SXRServer:
 59
 60    def __init__(self, root, cgi_url, src_url,
 61                 template_file = None,
 62                 sxr_prefix='/sxr'):
 63
 64        self.cgi_url = cgi_url
 65        self.src_url = src_url
 66        self.src_dir = os.path.join(root, 'Source')
 67        ir = IR.load(os.path.join(root, 'sxr.syn'))
 68        self.data = ir.sxr
 69        self.index = ir.sxr.index()
 70
 71        if template_file:
 72            template = open(template_file).read()
 73        else:
 74            template = default_template
 75        self.template = template.split("@CONTENT@")
 76
 77
 78    def ident_ref(self, file, line, scope):
 79
 80        if len(scope):
 81           text = '::'.join(scope)
 82        else:
 83           text = '<global scope>'
 84        return '<a href=\"%s/Source/%s.html#%s\">%s:%s: %s</a>'%(self.src_url, file, line, file, line, escape(text))
 85
 86
 87    def file_ref(self, file, name = None):
 88
 89        if not name: name = file
 90        name = name[:-5] # strip of trailing '.html'
 91        return "<a href=\"%s/Source/%s\">%s</a>"%(self.src_url, file, name)
 92
 93
 94    def list_refs(self, data, name):
 95
 96        html = ''
 97        if not data.has_key(name): return '\n'
 98        html += '<h3>%s</h3>\n'%escape('::'.join(name))
 99        entry = data[name]
100        if entry.definitions:
101            html += '<li>Defined at:<br/>\n'
102            html += '<ul>\n'
103            for file, line, scope in entry.definitions:
104                html +=  '<li>%s</li>\n'%(self.ident_ref(file, line, scope))
105            html += '</ul></li>\n'
106        if entry.calls:
107            html += '<li>Called from:<br/>\n'
108            html += '<ul>\n'
109            for file, line, scope in entry.calls:
110                html += '<li>%s</li>\n'%(self.ident_ref(file, line, scope))
111            html += '</ul></li>\n'
112        if entry.references:
113            html += '<li>Referenced from:<br/>\n'
114            html += '<ul>\n'
115            for file, line, scope in entry.references:
116                html += '<li>%s</li>\n'%(self.ident_ref(file, line, scope))
117            html += '</ul></li>\n'
118        return html
119
120
121    def search_file(self, pattern):
122        """Generate a file search listing."""
123
124        html = self.template[0]
125        html += file_search_form%{'script': self.cgi_url}
126
127        base_path_len = len(self.src_dir)
128        def find(result, base, files):
129
130           result.extend([os.path.join(base[base_path_len + 1:], file)
131                          for file in files
132                          if os.path.isfile(os.path.join(base, file)) and
133                          fnmatch.fnmatch(os.path.splitext(file)[0], pattern)])
134
135        result = []
136        os.path.walk(self.src_dir, find, result)
137        if result:
138            html += '<ul>\n'
139            for f in result:
140                html += '<li>%s</li>\n'%(self.file_ref(f.strip()))
141            html += '</ul>\n'
142        html += self.template[1]
143
144        return html
145
146
147    def search_ident(self, name, qualified = False):
148        """Generate an identifier listing."""
149
150        html = self.template[0]
151        html += ident_search_form%{'script' : self.cgi_url}
152
153        if qualified:
154            if '::' in name:
155                name = tuple(name.split('::'))
156            else:
157                name = tuple(name.split('.'))
158            found = False
159            # Check for exact match
160            if self.data.has_key(name):
161                html += 'Found exact match:<br/>\n'
162                html += self.list_refs(self.data, name)
163                found = True
164            # Search for last part of name in index
165            if self.index.has_key(name[-1]):
166                matches = self.index[name[-1]]
167                html += 'Found (%d) possible matches:<br/>\n'%(len(matches))
168                html += '<ul>\n'
169                for name in matches:
170                    html += self.list_refs(self.data, name)
171                html += '</ul>\n'
172                found = True
173            if not found:
174                html += "No matches found<br/>\n"
175
176        elif self.index.has_key(name):
177            matches = self.index[name]
178            html += 'Found (%d) possible matches:<br/>\n'%(len(matches))
179            html += '<ul>\n'
180            for name in matches:
181                html += self.list_refs(self.data, name)
182            html += '</ul>\n'
183        else:
184            html += 'No matches found<br/>\n'
185        html += self.template[1]
186
187        return html
188