#!/usr/bin/python3 # This file is part of Elixir, a source code cross-referencer. # # Copyright (C) 2017 Mikaƫl Bouillot # # # Elixir is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Elixir is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . from io import StringIO from urllib import parse realprint = print outputBuffer = StringIO() def print (arg, end='\n'): global outputBuffer outputBuffer.write (arg + end) # Enable CGI Trackback Manager for debugging (https://docs.python.org/fr/3/library/cgitb.html) import cgitb cgitb.enable() import cgi import os from re import search, sub ident = '' status = 200 # Split the URL into its components (project, version, cmd, arg) m = search ('^/([^/]*)/([^/]*)/([^/]*)(.*)$', os.environ['SCRIPT_URL']) if m: project = m.group (1) version = m.group (2) cmd = m.group (3) arg = m.group (4) if not (project and search ('^[A-Za-z0-9-]+$', project)) \ or not (version and search ('^[A-Za-z0-9._-]+$', version)): status = 302 location = '/linux/latest/'+cmd+arg cmd = '' if cmd == 'source': path = arg if len (path) > 0 and path[-1] == '/': path = path[:-1] status = 301 location = '/'+project+'/'+version+'/source'+path else: mode = 'source' if not search ('^[A-Za-z0-9_/.,+-]*$', path): path = 'INVALID' url = 'source'+path elif cmd == 'ident': ident = arg[1:] form = cgi.FieldStorage() ident2 = form.getvalue ('i') if ident == '' and ident2: status = 302 ident2 = parse.quote(ident2.strip()) location = '/'+project+'/'+version+'/ident/'+ident2 else: mode = 'ident' if not (ident and search ('^[A-Za-z0-9_-]*$', ident)): ident = '' url = 'ident/'+ident else: status = 404 if status == 301: realprint ('Status: 301 Moved Permanently') realprint ('Location: '+location+'\n') exit() elif status == 302: realprint ('Status: 302 Found') realprint ('Location: '+location+'\n') exit() elif status == 404: realprint ('Status: 404 Not Found\n') exit() basedir = os.environ['LXR_PROJ_DIR'] os.environ['LXR_DATA_DIR'] = basedir + '/' + project + '/data'; os.environ['LXR_REPO_DIR'] = basedir + '/' + project + '/repo'; projects = [] for (dirpath, dirnames, filenames) in os.walk (basedir): projects.extend (dirnames) break projects.sort () import sys sys.path = [ sys.path[0] + '/..' ] + sys.path import query def call_query(*args): cwd = os.getcwd() os.chdir ('..') ret = query.query (*args) os.chdir (cwd) return ret if version == 'latest': tag = call_query ('latest') else: tag = version data = { 'baseurl': '/' + project + '/', 'tag': tag, 'version': version, 'url': url, 'project': project, 'projects': projects, 'ident': ident, 'breadcrumb': '/' } versions = call_query ('versions') v = '' b = 1 for topmenu in versions: submenus = versions[topmenu] v += '
  • \n' v += '\t'+topmenu+'\n' v += '\t
      \n' b += 1 for submenu in submenus: tags = submenus[submenu] if submenu == tags[0] and len(tags) == 1: if submenu == tag: v += '\t\t\n' else: v += '\t\t\n' else: v += '\t\t
    • \n' v += '\t\t\t'+submenu+'\n' v += '\t\t\t
        \n' for _tag in tags: if _tag == tag: v += '\t\t\t\t\n' else: v += '\t\t\t\t\n' v += '\t\t\t
    • \n' v += '\t
  • \n' data['versions'] = v if mode == 'source': p2 = '' p3 = path.split ('/') [1:] links = [] for p in p3: p2 += '/'+p links.append (''+p+'') if links: data['breadcrumb'] += '/'.join (links) data['ident'] = ident data['title'] = project.capitalize ()+' source code: '+path[1:]+' ('+tag+') - Bootlin' lines = ['null - -'] type = call_query ('type', tag, path) if len (type) > 0: if type == 'tree': lines += call_query ('dir', tag, path) elif type == 'blob': blob_content = call_query ('file', tag, path) lines += blob_content.split("\n")[:-1] else: print ('

    This file does not exist.

    ') status = 404 if type == 'tree': if path != '': lines[0] = 'back - -' print ('
    ') print ('\n') for l in lines: type, name, size = l.split (' ') if type == 'null': continue elif type == 'tree': size = '' path2 = path+'/'+name name = name elif type == 'blob': size = size+' bytes' path2 = path+'/'+name elif type == 'back': size = '' path2 = os.path.dirname (path[:-1]) if path2 == '/': path2 = '' name = 'Parent directory' print (' \n') print (' \n') print (' \n') print (' \n') print ('
    '+name+''+size+'
    ', end='') print ('
    ') elif type == 'blob': del (lines[0]) import pygments import pygments.lexers import pygments.formatters links = [] code = StringIO() def keep_links(match): links.append (match.group (1)) g = match.group(1) return '__KEEPLINKS__' + str(len(links)) def replace_links(match): i = links[int (match.group (1)) - 1] return ''+i+'' for l in lines: l = sub ('\033\[31m(.*?)\033\[0m', keep_links, l) l = sub ('\033\[32m', '', l) l = sub ('\033\[33m', '', l) l = sub ('\033\[0m', '', l) code.write (l + '\n') code = code.getvalue() try: lexer = pygments.lexers.guess_lexer_for_filename (path, code) except: lexer = pygments.lexers.get_lexer_by_name ('text') lexer.stripnl = False formatter = pygments.formatters.HtmlFormatter (linenos=True, anchorlinenos=True) result = pygments.highlight (code, lexer, formatter) result = sub ('href="#-(\d+)', 'name="L\\1" id="L\\1" href="'+version+'/source'+path+'#L\\1', result) result = sub ('__KEEPLINKS__(\d+)', replace_links, result) print ('
    ' + result + '
    ') elif mode == 'ident': data['title'] = project.capitalize ()+' source code: '+ident+' identifier ('+tag+') - Bootlin' symbol_definitions, symbol_references = call_query ('ident', tag, ident) print ('
    ') if len(symbol_definitions): print ('

    Defined in '+str(len(symbol_definitions))+' files:

    ') print ('
      ') for symbol_definition in symbol_definitions: print ('
    • {f}, line {n} (as a {t})'.format( v=version, f=symbol_definition.path, n=symbol_definition.line, t=symbol_definition.type )) print ('
    ') print ('

    Referenced in '+str(len(symbol_references))+' files:

    ') print ('
      ') for symbol_reference in symbol_references: ln = symbol_reference.line.split (',') if len (ln) == 1: n = ln[0] print ('
    • {f}, line {n}'.format( v=version, f=symbol_reference.path, n=n )) else: if len(symbol_references) > 100: # Concise display n = len (ln) print ('
    • {f}, {n} times'.format( v=version, f=symbol_reference.path, n=n )) else: # Verbose display print ('
    • {f}'.format( v=version, f=symbol_reference.path, n=ln[0] )) print ('
        ') for n in ln: print ('
      • line {n}'.format( v=version, f=symbol_reference.path, n=n )) print ('
      ') print ('
    ') else: if ident != '': print ('

    Identifier not used

    ') status = 404 print ('
    ') else: print ('Invalid request') if status == 404: realprint ('Status: 404 Not Found') import jinja2 loader = jinja2.FileSystemLoader (os.path.join (os.path.dirname (__file__), '../templates/')) environment = jinja2.Environment (loader=loader) template = environment.get_template ('layout.html') realprint ('Content-Type: text/html;charset=utf-8\n') data['main'] = outputBuffer.getvalue() realprint (template.render(data), end='')