# hgweb/common.py - Utility functions needed by hgweb_mod and hgwebdir_mod # # Copyright 21 May 2005 - (c) 2005 Jake Edge # Copyright 2005, 2006 Matt Mackall # # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. import errno, mimetypes, os class ErrorResponse(Exception): def __init__(self, code, message=None): Exception.__init__(self) self.code = code if message: self.message = message else: self.message = _statusmessage(code) def _statusmessage(code): from BaseHTTPServer import BaseHTTPRequestHandler responses = BaseHTTPRequestHandler.responses return responses.get(code, ('Error', 'Unknown error'))[0] def statusmessage(code): return '%d %s' % (code, _statusmessage(code)) def get_mtime(repo_path): store_path = os.path.join(repo_path, ".hg") if not os.path.isdir(os.path.join(store_path, "data")): store_path = os.path.join(store_path, "store") cl_path = os.path.join(store_path, "00changelog.i") if os.path.exists(cl_path): return os.stat(cl_path).st_mtime else: return os.stat(store_path).st_mtime def staticfile(directory, fname, req): """return a file inside directory with guessed content-type header fname always uses '/' as directory separator and isn't allowed to contain unusual path components. Content-type is guessed using the mimetypes module. Return an empty string if fname is illegal or file not found. """ parts = fname.split('/') path = directory for part in parts: if (part in ('', os.curdir, os.pardir) or os.sep in part or os.altsep is not None and os.altsep in part): return "" path = os.path.join(path, part) try: os.stat(path) ct = mimetypes.guess_type(path)[0] or "text/plain" req.header([('Content-type', ct), ('Content-length', str(os.path.getsize(path)))]) return file(path, 'rb').read() except TypeError: raise ErrorResponse(500, 'illegal file name') except OSError, err: if err.errno == errno.ENOENT: raise ErrorResponse(404) else: raise ErrorResponse(500, err.strerror) def style_map(templatepath, style): """Return path to mapfile for a given style. Searches mapfile in the following locations: 1. templatepath/style/map 2. templatepath/map-style 3. templatepath/map """ locations = style and [os.path.join(style, "map"), "map-"+style] or [] locations.append("map") for location in locations: mapfile = os.path.join(templatepath, location) if os.path.isfile(mapfile): return mapfile raise RuntimeError("No hgweb templates found in %r" % templatepath) def paritygen(stripecount, offset=0): """count parity of horizontal stripes for easier reading""" if stripecount and offset: # account for offset, e.g. due to building the list in reverse count = (stripecount + offset) % stripecount parity = (stripecount + offset) / stripecount & 1 else: count = 0 parity = 0 while True: yield parity count += 1 if stripecount and count >= stripecount: parity = 1 - parity count = 0