##// END OF EJS Templates
tarball support v0.3...
tarball support v0.3 Hello, I'm slowly improving support for tarballs in Mercurial. Attached patch is made against current tip in Matt's repository - f859e9cba1b9, and contains everything done so far. Changes: - gzip and bzip2 tarballs are sent immediately without writing to temporary files (I was wrong Matt, it can be done very easy) - hgrc customization, you can choose which type (if any) you will support There's no easy way to support compression levels, since TarFile open() assume that it is 9. I tried to use gzopen(), and bz2open() methods instead, but it seems that headers of generated archives, are missing or wrong. We could eventually try to rewrite tarfile.py and include our own version into Mercurial, but I don't know if it's good idea... Wojtek

File last commit:

r1076:01db658c default
r1076:01db658c default
Show More
hgweb.py
991 lines | 32.3 KiB | text/x-python | PythonLexer
# hgweb.py - web interface to a mercurial repository
#
# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
# Copyright 2005 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
import os, cgi, time, re, difflib, socket, sys, zlib
from mercurial.hg import *
from mercurial.ui import *
def templatepath():
for f in "templates", "../templates":
p = os.path.join(os.path.dirname(__file__), f)
if os.path.isdir(p):
return p
def age(t):
def plural(t, c):
if c == 1:
return t
return t + "s"
def fmt(t, c):
return "%d %s" % (c, plural(t, c))
now = time.time()
delta = max(1, int(now - t))
scales = [["second", 1],
["minute", 60],
["hour", 3600],
["day", 3600 * 24],
["week", 3600 * 24 * 7],
["month", 3600 * 24 * 30],
["year", 3600 * 24 * 365]]
scales.reverse()
for t, s in scales:
n = delta / s
if n >= 2 or s == 1:
return fmt(t, n)
def nl2br(text):
return text.replace('\n', '<br/>\n')
def obfuscate(text):
return ''.join(['&#%d;' % ord(c) for c in text])
def up(p):
if p[0] != "/":
p = "/" + p
if p[-1] == "/":
p = p[:-1]
up = os.path.dirname(p)
if up == "/":
return "/"
return up + "/"
def httphdr(type, file="", size=0):
sys.stdout.write('Content-type: %s\n' % type)
if file:
sys.stdout.write('Content-disposition: attachment; filename=%s\n'
% file)
if size > 0:
sys.stdout.write('Content-length: %d\n' % size)
sys.stdout.write('\n')
def write(*things):
for thing in things:
if hasattr(thing, "__iter__"):
for part in thing:
write(part)
else:
sys.stdout.write(str(thing))
class templater:
def __init__(self, mapfile, filters={}, defaults={}):
self.cache = {}
self.map = {}
self.base = os.path.dirname(mapfile)
self.filters = filters
self.defaults = defaults
for l in file(mapfile):
m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
if m:
self.cache[m.group(1)] = m.group(2)
else:
m = re.match(r'(\S+)\s*=\s*(\S+)', l)
if m:
self.map[m.group(1)] = os.path.join(self.base, m.group(2))
else:
raise LookupError("unknown map entry '%s'" % l)
def __call__(self, t, **map):
m = self.defaults.copy()
m.update(map)
try:
tmpl = self.cache[t]
except KeyError:
tmpl = self.cache[t] = file(self.map[t]).read()
return self.template(tmpl, self.filters, **m)
def template(self, tmpl, filters={}, **map):
while tmpl:
m = re.search(r"#([a-zA-Z0-9]+)((%[a-zA-Z0-9]+)*)((\|[a-zA-Z0-9]+)*)#", tmpl)
if m:
yield tmpl[:m.start(0)]
v = map.get(m.group(1), "")
v = callable(v) and v(**map) or v
format = m.group(2)
fl = m.group(4)
if format:
q = v.__iter__
for i in q():
lm = map.copy()
lm.update(i)
yield self(format[1:], **lm)
v = ""
elif fl:
for f in fl.split("|")[1:]:
v = filters[f](v)
yield v
tmpl = tmpl[m.end(0):]
else:
yield tmpl
return
def rfc822date(x):
return time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(x))
common_filters = {
"escape": cgi.escape,
"age": age,
"date": (lambda x: time.asctime(time.gmtime(x))),
"addbreaks": nl2br,
"obfuscate": obfuscate,
"short": (lambda x: x[:12]),
"firstline": (lambda x: x.splitlines(1)[0]),
"permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
"rfc822date": rfc822date,
}
class hgweb:
def __init__(self, repo, name=None):
if type(repo) == type(""):
self.repo = repository(ui(), repo)
else:
self.repo = repo
self.mtime = -1
self.reponame = name or self.repo.ui.config("web", "name",
self.repo.root)
def refresh(self):
s = os.stat(os.path.join(self.repo.root, ".hg", "00changelog.i"))
if s.st_mtime != self.mtime:
self.mtime = s.st_mtime
self.repo = repository(self.repo.ui, self.repo.root)
self.maxchanges = self.repo.ui.config("web", "maxchanges", 10)
self.maxfiles = self.repo.ui.config("web", "maxchanges", 10)
self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
self.allowzip = self.repo.ui.configbool("web", "zip", True)
self.allowgz = self.repo.ui.configbool("web", "gz", True)
self.allowbz2 = self.repo.ui.configbool("web", "bz2", True)
def date(self, cs):
return time.asctime(time.gmtime(float(cs[2].split(' ')[0])))
def listfiles(self, files, mf):
for f in files[:self.maxfiles]:
yield self.t("filenodelink", node=hex(mf[f]), file=f)
if len(files) > self.maxfiles:
yield self.t("fileellipses")
def listfilediffs(self, files, changeset):
for f in files[:self.maxfiles]:
yield self.t("filedifflink", node=hex(changeset), file=f)
if len(files) > self.maxfiles:
yield self.t("fileellipses")
def parents(self, t1, nodes=[], rev=None,**args):
if not rev:
rev = lambda x: ""
for node in nodes:
if node != nullid:
yield self.t(t1, node=hex(node), rev=rev(node), **args)
def showtag(self, t1, node=nullid, **args):
for t in self.repo.nodetags(node):
yield self.t(t1, tag=t, **args)
def tarballbuttons(self, m):
s = ''
if self.allowzip:
s += '<a href="?cmd=tarball;manifest=%s;type=zip">zip</a>\n' % m
if self.allowgz:
s += '<a href="?cmd=tarball;manifest=%s;type=gz">gz</a>\n' % m
if self.allowbz2:
s += '<a href="?cmd=tarball;manifest=%s;type=bz2">bz2</a>\n' % m
return s
def diff(self, node1, node2, files):
def filterfiles(list, files):
l = [x for x in list if x in files]
for f in files:
if f[-1] != os.sep:
f += os.sep
l += [x for x in list if x.startswith(f)]
return l
parity = [0]
def diffblock(diff, f, fn):
yield self.t("diffblock",
lines=prettyprintlines(diff),
parity=parity[0],
file=f,
filenode=hex(fn or nullid))
parity[0] = 1 - parity[0]
def prettyprintlines(diff):
for l in diff.splitlines(1):
if l.startswith('+'):
yield self.t("difflineplus", line=l)
elif l.startswith('-'):
yield self.t("difflineminus", line=l)
elif l.startswith('@'):
yield self.t("difflineat", line=l)
else:
yield self.t("diffline", line=l)
r = self.repo
cl = r.changelog
mf = r.manifest
change1 = cl.read(node1)
change2 = cl.read(node2)
mmap1 = mf.read(change1[0])
mmap2 = mf.read(change2[0])
date1 = self.date(change1)
date2 = self.date(change2)
c, a, d, u = r.changes(node1, node2)
if files:
c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
for f in c:
to = r.file(f).read(mmap1[f])
tn = r.file(f).read(mmap2[f])
yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
for f in a:
to = None
tn = r.file(f).read(mmap2[f])
yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
for f in d:
to = r.file(f).read(mmap1[f])
tn = None
yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
def changelog(self, pos):
def changenav(**map):
def seq(factor=1):
yield 1 * factor
yield 3 * factor
#yield 5 * factor
for f in seq(factor * 10):
yield f
l = []
for f in seq():
if f < self.maxchanges / 2:
continue
if f > count:
break
r = "%d" % f
if pos + f < count:
l.append(("+" + r, pos + f))
if pos - f >= 0:
l.insert(0, ("-" + r, pos - f))
yield {"rev": 0, "label": "(0)"}
for label, rev in l:
yield {"label": label, "rev": rev}
yield {"label": "tip", "rev": ""}
def changelist(**map):
parity = (start - end) & 1
cl = self.repo.changelog
l = [] # build a list in forward order for efficiency
for i in range(start, end):
n = cl.node(i)
changes = cl.read(n)
hn = hex(n)
t = float(changes[2].split(' ')[0])
l.insert(0, {"parity": parity,
"author": changes[1],
"parent": self.parents("changelogparent",
cl.parents(n), cl.rev),
"changelogtag": self.showtag("changelogtag",n),
"manifest": hex(changes[0]),
"desc": changes[4],
"date": t,
"files": self.listfilediffs(changes[3], n),
"rev": i,
"node": hn})
parity = 1 - parity
for e in l:
yield e
cl = self.repo.changelog
mf = cl.read(cl.tip())[0]
count = cl.count()
start = max(0, pos - self.maxchanges + 1)
end = min(count, start + self.maxchanges)
pos = end - 1
yield self.t('changelog',
changenav=changenav,
manifest=hex(mf),
rev=pos, changesets=count, entries=changelist)
def search(self, query):
def changelist(**map):
cl = self.repo.changelog
count = 0
qw = query.lower().split()
def revgen():
for i in range(cl.count() - 1, 0, -100):
l = []
for j in range(max(0, i - 100), i):
n = cl.node(j)
changes = cl.read(n)
l.append((n, j, changes))
l.reverse()
for e in l:
yield e
for n, i, changes in revgen():
miss = 0
for q in qw:
if not (q in changes[1].lower() or
q in changes[4].lower() or
q in " ".join(changes[3][:20]).lower()):
miss = 1
break
if miss:
continue
count += 1
hn = hex(n)
t = float(changes[2].split(' ')[0])
yield self.t('searchentry',
parity=count & 1,
author=changes[1],
parent=self.parents("changelogparent",
cl.parents(n), cl.rev),
changelogtag=self.showtag("changelogtag",n),
manifest=hex(changes[0]),
desc=changes[4],
date=t,
files=self.listfilediffs(changes[3], n),
rev=i,
node=hn)
if count >= self.maxchanges:
break
cl = self.repo.changelog
mf = cl.read(cl.tip())[0]
yield self.t('search',
query=query,
manifest=hex(mf),
entries=changelist)
def changeset(self, nodeid):
n = bin(nodeid)
cl = self.repo.changelog
changes = cl.read(n)
p1 = cl.parents(n)[0]
t = float(changes[2].split(' ')[0])
files = []
mf = self.repo.manifest.read(changes[0])
for f in changes[3]:
files.append(self.t("filenodelink",
filenode=hex(mf.get(f, nullid)), file=f))
def diff(**map):
yield self.diff(p1, n, None)
yield self.t('changeset',
diff=diff,
rev=cl.rev(n),
node=nodeid,
parent=self.parents("changesetparent",
cl.parents(n), cl.rev),
changesettag=self.showtag("changesettag",n),
manifest=hex(changes[0]),
author=changes[1],
desc=changes[4],
date=t,
files=files,
tarballbuttons=self.tarballbuttons(hex(changes[0])))
def filelog(self, f, filenode):
cl = self.repo.changelog
fl = self.repo.file(f)
count = fl.count()
def entries(**map):
l = []
parity = (count - 1) & 1
for i in range(count):
n = fl.node(i)
lr = fl.linkrev(n)
cn = cl.node(lr)
cs = cl.read(cl.node(lr))
t = float(cs[2].split(' ')[0])
l.insert(0, {"parity": parity,
"filenode": hex(n),
"filerev": i,
"file": f,
"node": hex(cn),
"author": cs[1],
"date": t,
"parent": self.parents("filelogparent",
fl.parents(n),
fl.rev, file=f),
"desc": cs[4]})
parity = 1 - parity
for e in l:
yield e
yield self.t("filelog", file=f, filenode=filenode, entries=entries)
def filerevision(self, f, node):
fl = self.repo.file(f)
n = bin(node)
text = fl.read(n)
changerev = fl.linkrev(n)
cl = self.repo.changelog
cn = cl.node(changerev)
cs = cl.read(cn)
t = float(cs[2].split(' ')[0])
mfn = cs[0]
def lines():
for l, t in enumerate(text.splitlines(1)):
yield {"line": t,
"linenumber": "% 6d" % (l + 1),
"parity": l & 1}
yield self.t("filerevision",
file=f,
filenode=node,
path=up(f),
text=lines(),
rev=changerev,
node=hex(cn),
manifest=hex(mfn),
author=cs[1],
date=t,
parent=self.parents("filerevparent",
fl.parents(n), fl.rev, file=f),
permissions=self.repo.manifest.readflags(mfn)[f])
def fileannotate(self, f, node):
bcache = {}
ncache = {}
fl = self.repo.file(f)
n = bin(node)
changerev = fl.linkrev(n)
cl = self.repo.changelog
cn = cl.node(changerev)
cs = cl.read(cn)
t = float(cs[2].split(' ')[0])
mfn = cs[0]
def annotate(**map):
parity = 1
last = None
for r, l in fl.annotate(n):
try:
cnode = ncache[r]
except KeyError:
cnode = ncache[r] = self.repo.changelog.node(r)
try:
name = bcache[r]
except KeyError:
cl = self.repo.changelog.read(cnode)
name = cl[1]
f = name.find('@')
if f >= 0:
name = name[:f]
f = name.find('<')
if f >= 0:
name = name[f+1:]
bcache[r] = name
if last != cnode:
parity = 1 - parity
last = cnode
yield {"parity": parity,
"node": hex(cnode),
"rev": r,
"author": name,
"file": f,
"line": l}
yield self.t("fileannotate",
file=f,
filenode=node,
annotate=annotate,
path=up(f),
rev=changerev,
node=hex(cn),
manifest=hex(mfn),
author=cs[1],
date=t,
parent=self.parents("fileannotateparent",
fl.parents(n), fl.rev, file=f),
permissions=self.repo.manifest.readflags(mfn)[f])
def manifest(self, mnode, path):
mf = self.repo.manifest.read(bin(mnode))
rev = self.repo.manifest.rev(bin(mnode))
node = self.repo.changelog.node(rev)
mff=self.repo.manifest.readflags(bin(mnode))
files = {}
p = path[1:]
l = len(p)
for f,n in mf.items():
if f[:l] != p:
continue
remain = f[l:]
if "/" in remain:
short = remain[:remain.find("/") + 1] # bleah
files[short] = (f, None)
else:
short = os.path.basename(remain)
files[short] = (f, n)
def filelist(**map):
parity = 0
fl = files.keys()
fl.sort()
for f in fl:
full, fnode = files[f]
if not fnode:
continue
yield {"file": full,
"manifest": mnode,
"filenode": hex(fnode),
"parity": parity,
"basename": f,
"permissions": mff[full]}
parity = 1 - parity
def dirlist(**map):
parity = 0
fl = files.keys()
fl.sort()
for f in fl:
full, fnode = files[f]
if fnode:
continue
yield {"parity": parity,
"path": os.path.join(path, f),
"manifest": mnode,
"basename": f[:-1]}
parity = 1 - parity
yield self.t("manifest",
manifest=mnode,
rev=rev,
node=hex(node),
path=path,
up=up(path),
fentries=filelist,
dentries=dirlist)
def tags(self):
cl = self.repo.changelog
mf = cl.read(cl.tip())[0]
i = self.repo.tagslist()
i.reverse()
def entries(**map):
parity = 0
for k,n in i:
yield {"parity": parity,
"tag": k,
"node": hex(n)}
parity = 1 - parity
yield self.t("tags",
manifest=hex(mf),
entries=entries)
def filediff(self, file, changeset):
n = bin(changeset)
cl = self.repo.changelog
p1 = cl.parents(n)[0]
cs = cl.read(n)
mf = self.repo.manifest.read(cs[0])
def diff(**map):
yield self.diff(p1, n, file)
yield self.t("filediff",
file=file,
filenode=hex(mf.get(file, nullid)),
node=changeset,
rev=self.repo.changelog.rev(n),
parent=self.parents("filediffparent",
cl.parents(n), cl.rev),
diff=diff)
def ziparchive(self, mnode):
import zipfile
tmp = tempfile.mkstemp()[1]
zf = zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED)
mf = self.repo.manifest.read(bin(mnode))
rev = self.repo.manifest.rev(bin(mnode))
cnode = short(self.repo.changelog.node(rev))
name = os.path.basename(self.repo.path[:-4]) # without '/.hg' suffix
name += '-' + str(rev) + '-' + cnode + '/'
for fname in mf.keys():
r = self.repo.file(fname)
zf.writestr(name + fname, r.read(mf[fname]))
zf.close()
f = open(tmp, 'r')
httphdr('application/zip', name[:-1] + '.zip', os.path.getsize(tmp))
sys.stdout.write(f.read())
f.close()
os.unlink(tmp)
def tararchive(self, mnode, type):
import StringIO
import time
import tarfile
#if type == "gz":
# tf = tarfile.TarFile.gzopen('', 'w', sys.stdout, compressionlevel)
#else:
# tf = tarfile.TarFile.bz2open('', 'w', sys.stdout, compressionlevel)
tf = tarfile.TarFile.open(mode='w|' + type, fileobj=sys.stdout)
mf = self.repo.manifest.read(bin(mnode))
rev = self.repo.manifest.rev(bin(mnode))
cnode = short(self.repo.changelog.node(rev))
mff = self.repo.manifest.readflags(bin(mnode))
mtime = int(time.time())
name = os.path.basename(self.repo.path[:-4]) # without '/.hg' suffix
name += '-' + str(rev) + '-' + cnode + '/'
httphdr('application/octet-stream', name[:-1] + '.tar.' + type)
for fname in mf.keys():
r = self.repo.file(fname)
rcont = r.read(mf[fname])
finfo = tarfile.TarInfo(name + fname)
finfo.mtime = mtime
finfo.size = len(rcont)
finfo.mode = mff[fname] and 0755 or 0644
tf.addfile(finfo, StringIO.StringIO(rcont))
tf.close()
# add tags to things
# tags -> list of changesets corresponding to tags
# find tag, changeset, file
def run(self):
def header(**map):
yield self.t("header", **map)
def footer(**map):
yield self.t("footer", **map)
self.refresh()
args = cgi.parse()
t = self.repo.ui.config("web", "templates", templatepath())
m = os.path.join(t, "map")
style = self.repo.ui.config("web", "style", "")
if args.has_key('style'):
style = args['style'][0]
if style:
b = os.path.basename("map-" + style)
p = os.path.join(t, b)
if os.path.isfile(p):
m = p
port = os.environ["SERVER_PORT"]
port = port != "80" and (":" + port) or ""
uri = os.environ["REQUEST_URI"]
if "?" in uri:
uri = uri.split("?")[0]
url = "http://%s%s%s" % (os.environ["SERVER_NAME"], port, uri)
self.t = templater(m, common_filters,
{"url": url,
"repo": self.reponame,
"header": header,
"footer": footer,
})
if not args.has_key('cmd'):
args['cmd'] = [self.t.cache['default'],]
if args['cmd'][0] == 'changelog':
c = self.repo.changelog.count() - 1
hi = c
if args.has_key('rev'):
hi = args['rev'][0]
try:
hi = self.repo.changelog.rev(self.repo.lookup(hi))
except RepoError:
write(self.search(hi))
return
write(self.changelog(hi))
elif args['cmd'][0] == 'changeset':
write(self.changeset(args['node'][0]))
elif args['cmd'][0] == 'manifest':
write(self.manifest(args['manifest'][0], args['path'][0]))
elif args['cmd'][0] == 'tags':
write(self.tags())
elif args['cmd'][0] == 'filediff':
write(self.filediff(args['file'][0], args['node'][0]))
elif args['cmd'][0] == 'file':
write(self.filerevision(args['file'][0], args['filenode'][0]))
elif args['cmd'][0] == 'annotate':
write(self.fileannotate(args['file'][0], args['filenode'][0]))
elif args['cmd'][0] == 'filelog':
write(self.filelog(args['file'][0], args['filenode'][0]))
elif args['cmd'][0] == 'heads':
httphdr("application/mercurial-0.1")
h = self.repo.heads()
sys.stdout.write(" ".join(map(hex, h)) + "\n")
elif args['cmd'][0] == 'branches':
httphdr("application/mercurial-0.1")
nodes = []
if args.has_key('nodes'):
nodes = map(bin, args['nodes'][0].split(" "))
for b in self.repo.branches(nodes):
sys.stdout.write(" ".join(map(hex, b)) + "\n")
elif args['cmd'][0] == 'between':
httphdr("application/mercurial-0.1")
nodes = []
if args.has_key('pairs'):
pairs = [map(bin, p.split("-"))
for p in args['pairs'][0].split(" ")]
for b in self.repo.between(pairs):
sys.stdout.write(" ".join(map(hex, b)) + "\n")
elif args['cmd'][0] == 'changegroup':
httphdr("application/mercurial-0.1")
nodes = []
if not self.allowpull:
return
if args.has_key('roots'):
nodes = map(bin, args['roots'][0].split(" "))
z = zlib.compressobj()
f = self.repo.changegroup(nodes)
while 1:
chunk = f.read(4096)
if not chunk:
break
sys.stdout.write(z.compress(chunk))
sys.stdout.write(z.flush())
elif args['cmd'][0] == 'tarball':
manifest = args['manifest'][0]
type = args['type'][0]
if type == 'zip' and self.allowzip:
self.ziparchive(manifest)
elif type == 'gz' and self.allowgz:
self.tararchive(manifest, 'gz')
elif type == 'bz2' and self.allowbz2:
self.tararchive(manifest, 'bz2')
else:
write(self.t("error"))
else:
write(self.t("error"))
def create_server(repo):
def openlog(opt, default):
if opt and opt != '-':
return open(opt, 'w')
return default
address = repo.ui.config("web", "address", "")
port = int(repo.ui.config("web", "port", 8000))
use_ipv6 = repo.ui.configbool("web", "ipv6")
accesslog = openlog(repo.ui.config("web", "accesslog", "-"), sys.stdout)
errorlog = openlog(repo.ui.config("web", "errorlog", "-"), sys.stderr)
import BaseHTTPServer
class IPv6HTTPServer(BaseHTTPServer.HTTPServer):
address_family = getattr(socket, 'AF_INET6', None)
def __init__(self, *args, **kwargs):
if self.address_family is None:
raise RepoError('IPv6 not available on this system')
BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
def log_error(self, format, *args):
errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
self.log_date_time_string(),
format % args))
def log_message(self, format, *args):
accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
self.log_date_time_string(),
format % args))
def do_POST(self):
try:
self.do_hgweb()
except socket.error, inst:
if inst.args[0] != 32:
raise
def do_GET(self):
self.do_POST()
def do_hgweb(self):
query = ""
p = self.path.find("?")
if p:
query = self.path[p + 1:]
query = query.replace('+', ' ')
env = {}
env['GATEWAY_INTERFACE'] = 'CGI/1.1'
env['REQUEST_METHOD'] = self.command
env['SERVER_NAME'] = self.server.server_name
env['SERVER_PORT'] = str(self.server.server_port)
env['REQUEST_URI'] = "/"
if query:
env['QUERY_STRING'] = query
host = self.address_string()
if host != self.client_address[0]:
env['REMOTE_HOST'] = host
env['REMOTE_ADDR'] = self.client_address[0]
if self.headers.typeheader is None:
env['CONTENT_TYPE'] = self.headers.type
else:
env['CONTENT_TYPE'] = self.headers.typeheader
length = self.headers.getheader('content-length')
if length:
env['CONTENT_LENGTH'] = length
accept = []
for line in self.headers.getallmatchingheaders('accept'):
if line[:1] in "\t\n\r ":
accept.append(line.strip())
else:
accept = accept + line[7:].split(',')
env['HTTP_ACCEPT'] = ','.join(accept)
os.environ.update(env)
save = sys.argv, sys.stdin, sys.stdout, sys.stderr
try:
sys.stdin = self.rfile
sys.stdout = self.wfile
sys.argv = ["hgweb.py"]
if '=' not in query:
sys.argv.append(query)
self.send_response(200, "Script output follows")
hg.run()
finally:
sys.argv, sys.stdin, sys.stdout, sys.stderr = save
hg = hgweb(repo)
if use_ipv6:
return IPv6HTTPServer((address, port), hgwebhandler)
else:
return BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
def server(path, name, templates, address, port, use_ipv6=False,
accesslog=sys.stdout, errorlog=sys.stderr):
httpd = create_server(path, name, templates, address, port, use_ipv6,
accesslog, errorlog)
httpd.serve_forever()
# This is a stopgap
class hgwebdir:
def __init__(self, config):
self.cp = ConfigParser.SafeConfigParser()
self.cp.read(config)
def run(self):
try:
virtual = os.environ["PATH_INFO"]
except:
virtual = ""
if virtual[1:]:
real = self.cp.get("paths", virtual[1:])
h = hgweb(real)
h.run()
return
def header(**map):
yield tmpl("header", **map)
def footer(**map):
yield tmpl("footer", **map)
templates = templatepath()
m = os.path.join(templates, "map")
tmpl = templater(m, common_filters,
{"header": header, "footer": footer})
def entries(**map):
parity = 0
l = self.cp.items("paths")
l.sort()
for v,r in l:
cp2 = ConfigParser.SafeConfigParser()
cp2.read(os.path.join(r, ".hg", "hgrc"))
def get(sec, val, default):
try:
return cp2.get(sec, val)
except:
return default
url = os.environ["REQUEST_URI"] + "/" + v
url = url.replace("//", "/")
yield dict(author=get("web", "author", "unknown"),
name=get("web", "name", v),
url=url,
parity=parity,
shortdesc=get("web", "description", "unknown"),
lastupdate=os.stat(os.path.join(r, ".hg",
"00changelog.d")).st_mtime)
parity = 1 - parity
write(tmpl("index", entries=entries))