##// END OF EJS Templates
hgweb: sort repository list
mpm@selenic.com -
r957:305ab68c default
parent child Browse files
Show More
@@ -1,874 +1,876 b''
1 1 # hgweb.py - web interface to a mercurial repository
2 2 #
3 3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
5 5 #
6 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 9 import os, cgi, time, re, difflib, socket, sys, zlib
10 10 from mercurial.hg import *
11 11 from mercurial.ui import *
12 12
13 13 def templatepath():
14 14 for f in "templates", "../templates":
15 15 p = os.path.join(os.path.dirname(__file__), f)
16 16 if os.path.isdir(p): return p
17 17
18 18 def age(t):
19 19 def plural(t, c):
20 20 if c == 1: return t
21 21 return t + "s"
22 22 def fmt(t, c):
23 23 return "%d %s" % (c, plural(t, c))
24 24
25 25 now = time.time()
26 26 delta = max(1, int(now - t))
27 27
28 28 scales = [["second", 1],
29 29 ["minute", 60],
30 30 ["hour", 3600],
31 31 ["day", 3600 * 24],
32 32 ["week", 3600 * 24 * 7],
33 33 ["month", 3600 * 24 * 30],
34 34 ["year", 3600 * 24 * 365]]
35 35
36 36 scales.reverse()
37 37
38 38 for t, s in scales:
39 39 n = delta / s
40 40 if n >= 2 or s == 1: return fmt(t, n)
41 41
42 42 def nl2br(text):
43 43 return text.replace('\n', '<br/>\n')
44 44
45 45 def obfuscate(text):
46 46 return ''.join([ '&#%d;' % ord(c) for c in text ])
47 47
48 48 def up(p):
49 49 if p[0] != "/": p = "/" + p
50 50 if p[-1] == "/": p = p[:-1]
51 51 up = os.path.dirname(p)
52 52 if up == "/":
53 53 return "/"
54 54 return up + "/"
55 55
56 56 def httphdr(type):
57 57 sys.stdout.write('Content-type: %s\n\n' % type)
58 58
59 59 def write(*things):
60 60 for thing in things:
61 61 if hasattr(thing, "__iter__"):
62 62 for part in thing:
63 63 write(part)
64 64 else:
65 65 sys.stdout.write(str(thing))
66 66
67 67 def template(tmpl, filters = {}, **map):
68 68 while tmpl:
69 69 m = re.search(r"#([a-zA-Z0-9]+)((\|[a-zA-Z0-9]+)*)#", tmpl)
70 70 if m:
71 71 yield tmpl[:m.start(0)]
72 72 v = map.get(m.group(1), "")
73 73 v = callable(v) and v(**map) or v
74 74
75 75 fl = m.group(2)
76 76 if fl:
77 77 for f in fl.split("|")[1:]:
78 78 v = filters[f](v)
79 79
80 80 yield v
81 81 tmpl = tmpl[m.end(0):]
82 82 else:
83 83 yield tmpl
84 84 return
85 85
86 86 class templater:
87 87 def __init__(self, mapfile, filters = {}, defaults = {}):
88 88 self.cache = {}
89 89 self.map = {}
90 90 self.base = os.path.dirname(mapfile)
91 91 self.filters = filters
92 92 self.defaults = defaults
93 93
94 94 for l in file(mapfile):
95 95 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
96 96 if m:
97 97 self.cache[m.group(1)] = m.group(2)
98 98 else:
99 99 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
100 100 if m:
101 101 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
102 102 else:
103 103 raise "unknown map entry '%s'" % l
104 104
105 105 def __call__(self, t, **map):
106 106 m = self.defaults.copy()
107 107 m.update(map)
108 108 try:
109 109 tmpl = self.cache[t]
110 110 except KeyError:
111 111 tmpl = self.cache[t] = file(self.map[t]).read()
112 112 return template(tmpl, self.filters, **m)
113 113
114 114 def rfc822date(x):
115 115 return time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(x))
116 116
117 117 common_filters = {
118 118 "escape": cgi.escape,
119 119 "age": age,
120 120 "date": (lambda x: time.asctime(time.gmtime(x))),
121 121 "addbreaks": nl2br,
122 122 "obfuscate": obfuscate,
123 123 "short": (lambda x: x[:12]),
124 124 "firstline": (lambda x: x.splitlines(1)[0]),
125 125 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
126 126 "rfc822date": rfc822date,
127 127 }
128 128
129 129 class hgweb:
130 130 maxchanges = 10
131 131 maxfiles = 10
132 132
133 133 def __init__(self, path, name=None, templates=""):
134 134 self.templates = templates
135 135 self.reponame = name
136 136 self.path = path
137 137 self.mtime = -1
138 138 self.viewonly = 0
139 139
140 140 def refresh(self):
141 141 s = os.stat(os.path.join(self.path, ".hg", "00changelog.i"))
142 142 if s.st_mtime != self.mtime:
143 143 self.mtime = s.st_mtime
144 144 self.repo = repository(ui(), self.path)
145 145
146 146 def date(self, cs):
147 147 return time.asctime(time.gmtime(float(cs[2].split(' ')[0])))
148 148
149 149 def listfiles(self, files, mf):
150 150 for f in files[:self.maxfiles]:
151 151 yield self.t("filenodelink", node = hex(mf[f]), file = f)
152 152 if len(files) > self.maxfiles:
153 153 yield self.t("fileellipses")
154 154
155 155 def listfilediffs(self, files, changeset):
156 156 for f in files[:self.maxfiles]:
157 157 yield self.t("filedifflink", node = hex(changeset), file = f)
158 158 if len(files) > self.maxfiles:
159 159 yield self.t("fileellipses")
160 160
161 161 def parents(self, t1, nodes=[], rev=None,**args):
162 162 if not rev: rev = lambda x: ""
163 163 for node in nodes:
164 164 if node != nullid:
165 165 yield self.t(t1, node = hex(node), rev = rev(node), **args)
166 166
167 167 def showtag(self, t1, node=nullid, **args):
168 168 for t in self.repo.nodetags(node):
169 169 yield self.t(t1, tag = t, **args)
170 170
171 171 def diff(self, node1, node2, files):
172 172 def filterfiles(list, files):
173 173 l = [ x for x in list if x in files ]
174 174
175 175 for f in files:
176 176 if f[-1] != os.sep: f += os.sep
177 177 l += [ x for x in list if x.startswith(f) ]
178 178 return l
179 179
180 180 parity = [0]
181 181 def diffblock(diff, f, fn):
182 182 yield self.t("diffblock",
183 183 lines = prettyprintlines(diff),
184 184 parity = parity[0],
185 185 file = f,
186 186 filenode = hex(fn or nullid))
187 187 parity[0] = 1 - parity[0]
188 188
189 189 def prettyprintlines(diff):
190 190 for l in diff.splitlines(1):
191 191 if l.startswith('+'):
192 192 yield self.t("difflineplus", line = l)
193 193 elif l.startswith('-'):
194 194 yield self.t("difflineminus", line = l)
195 195 elif l.startswith('@'):
196 196 yield self.t("difflineat", line = l)
197 197 else:
198 198 yield self.t("diffline", line = l)
199 199
200 200 r = self.repo
201 201 cl = r.changelog
202 202 mf = r.manifest
203 203 change1 = cl.read(node1)
204 204 change2 = cl.read(node2)
205 205 mmap1 = mf.read(change1[0])
206 206 mmap2 = mf.read(change2[0])
207 207 date1 = self.date(change1)
208 208 date2 = self.date(change2)
209 209
210 210 c, a, d, u = r.changes(node1, node2)
211 211 if files:
212 212 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
213 213
214 214 for f in c:
215 215 to = r.file(f).read(mmap1[f])
216 216 tn = r.file(f).read(mmap2[f])
217 217 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
218 218 for f in a:
219 219 to = None
220 220 tn = r.file(f).read(mmap2[f])
221 221 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
222 222 for f in d:
223 223 to = r.file(f).read(mmap1[f])
224 224 tn = None
225 225 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
226 226
227 227 def changelog(self, pos):
228 228 def changenav(**map):
229 229 def seq(factor = 1):
230 230 yield 1 * factor
231 231 yield 3 * factor
232 232 #yield 5 * factor
233 233 for f in seq(factor * 10):
234 234 yield f
235 235
236 236 l = []
237 237 for f in seq():
238 238 if f < self.maxchanges / 2: continue
239 239 if f > count: break
240 240 r = "%d" % f
241 241 if pos + f < count: l.append(("+" + r, pos + f))
242 242 if pos - f >= 0: l.insert(0, ("-" + r, pos - f))
243 243
244 244 yield self.t("naventry", rev = 0, label="(0)")
245 245
246 246 for label, rev in l:
247 247 yield self.t("naventry", label = label, rev = rev)
248 248
249 249 yield self.t("naventry", label="tip")
250 250
251 251 def changelist(**map):
252 252 parity = (start - end) & 1
253 253 cl = self.repo.changelog
254 254 l = [] # build a list in forward order for efficiency
255 255 for i in range(start, end):
256 256 n = cl.node(i)
257 257 changes = cl.read(n)
258 258 hn = hex(n)
259 259 t = float(changes[2].split(' ')[0])
260 260
261 261 l.insert(0, self.t(
262 262 'changelogentry',
263 263 parity = parity,
264 264 author = changes[1],
265 265 parent = self.parents("changelogparent",
266 266 cl.parents(n), cl.rev),
267 267 changelogtag = self.showtag("changelogtag",n),
268 268 manifest = hex(changes[0]),
269 269 desc = changes[4],
270 270 date = t,
271 271 files = self.listfilediffs(changes[3], n),
272 272 rev = i,
273 273 node = hn))
274 274 parity = 1 - parity
275 275
276 276 yield l
277 277
278 278 cl = self.repo.changelog
279 279 mf = cl.read(cl.tip())[0]
280 280 count = cl.count()
281 281 start = max(0, pos - self.maxchanges + 1)
282 282 end = min(count, start + self.maxchanges)
283 283 pos = end - 1
284 284
285 285 yield self.t('changelog',
286 286 changenav = changenav,
287 287 manifest = hex(mf),
288 288 rev = pos, changesets = count, entries = changelist)
289 289
290 290 def search(self, query):
291 291
292 292 def changelist(**map):
293 293 cl = self.repo.changelog
294 294 count = 0
295 295 qw = query.lower().split()
296 296
297 297 def revgen():
298 298 for i in range(cl.count() - 1, 0, -100):
299 299 l = []
300 300 for j in range(max(0, i - 100), i):
301 301 n = cl.node(j)
302 302 changes = cl.read(n)
303 303 l.insert(0, (n, j, changes))
304 304 for e in l:
305 305 yield e
306 306
307 307 for n, i, changes in revgen():
308 308 miss = 0
309 309 for q in qw:
310 310 if not (q in changes[1].lower() or
311 311 q in changes[4].lower() or
312 312 q in " ".join(changes[3][:20]).lower()):
313 313 miss = 1
314 314 break
315 315 if miss: continue
316 316
317 317 count += 1
318 318 hn = hex(n)
319 319 t = float(changes[2].split(' ')[0])
320 320
321 321 yield self.t(
322 322 'searchentry',
323 323 parity = count & 1,
324 324 author = changes[1],
325 325 parent = self.parents("changelogparent",
326 326 cl.parents(n), cl.rev),
327 327 changelogtag = self.showtag("changelogtag",n),
328 328 manifest = hex(changes[0]),
329 329 desc = changes[4],
330 330 date = t,
331 331 files = self.listfilediffs(changes[3], n),
332 332 rev = i,
333 333 node = hn)
334 334
335 335 if count >= self.maxchanges: break
336 336
337 337 cl = self.repo.changelog
338 338 mf = cl.read(cl.tip())[0]
339 339
340 340 yield self.t('search',
341 341 query = query,
342 342 manifest = hex(mf),
343 343 entries = changelist)
344 344
345 345 def changeset(self, nodeid):
346 346 n = bin(nodeid)
347 347 cl = self.repo.changelog
348 348 changes = cl.read(n)
349 349 p1 = cl.parents(n)[0]
350 350 t = float(changes[2].split(' ')[0])
351 351
352 352 files = []
353 353 mf = self.repo.manifest.read(changes[0])
354 354 for f in changes[3]:
355 355 files.append(self.t("filenodelink",
356 356 filenode = hex(mf.get(f, nullid)), file = f))
357 357
358 358 def diff(**map):
359 359 yield self.diff(p1, n, None)
360 360
361 361 yield self.t('changeset',
362 362 diff = diff,
363 363 rev = cl.rev(n),
364 364 node = nodeid,
365 365 parent = self.parents("changesetparent",
366 366 cl.parents(n), cl.rev),
367 367 changesettag = self.showtag("changesettag",n),
368 368 manifest = hex(changes[0]),
369 369 author = changes[1],
370 370 desc = changes[4],
371 371 date = t,
372 372 files = files)
373 373
374 374 def filelog(self, f, filenode):
375 375 cl = self.repo.changelog
376 376 fl = self.repo.file(f)
377 377 count = fl.count()
378 378
379 379 def entries(**map):
380 380 l = []
381 381 parity = (count - 1) & 1
382 382
383 383 for i in range(count):
384 384
385 385 n = fl.node(i)
386 386 lr = fl.linkrev(n)
387 387 cn = cl.node(lr)
388 388 cs = cl.read(cl.node(lr))
389 389 t = float(cs[2].split(' ')[0])
390 390
391 391 l.insert(0, self.t("filelogentry",
392 392 parity = parity,
393 393 filenode = hex(n),
394 394 filerev = i,
395 395 file = f,
396 396 node = hex(cn),
397 397 author = cs[1],
398 398 date = t,
399 399 parent = self.parents("filelogparent",
400 400 fl.parents(n), fl.rev, file=f),
401 401 desc = cs[4]))
402 402 parity = 1 - parity
403 403
404 404 yield l
405 405
406 406 yield self.t("filelog",
407 407 file = f,
408 408 filenode = filenode,
409 409 entries = entries)
410 410
411 411 def filerevision(self, f, node):
412 412 fl = self.repo.file(f)
413 413 n = bin(node)
414 414 text = fl.read(n)
415 415 changerev = fl.linkrev(n)
416 416 cl = self.repo.changelog
417 417 cn = cl.node(changerev)
418 418 cs = cl.read(cn)
419 419 t = float(cs[2].split(' ')[0])
420 420 mfn = cs[0]
421 421
422 422 def lines():
423 423 for l, t in enumerate(text.splitlines(1)):
424 424 yield self.t("fileline", line = t,
425 425 linenumber = "% 6d" % (l + 1),
426 426 parity = l & 1)
427 427
428 428 yield self.t("filerevision", file = f,
429 429 filenode = node,
430 430 path = up(f),
431 431 text = lines(),
432 432 rev = changerev,
433 433 node = hex(cn),
434 434 manifest = hex(mfn),
435 435 author = cs[1],
436 436 date = t,
437 437 parent = self.parents("filerevparent",
438 438 fl.parents(n), fl.rev, file=f),
439 439 permissions = self.repo.manifest.readflags(mfn)[f])
440 440
441 441 def fileannotate(self, f, node):
442 442 bcache = {}
443 443 ncache = {}
444 444 fl = self.repo.file(f)
445 445 n = bin(node)
446 446 changerev = fl.linkrev(n)
447 447
448 448 cl = self.repo.changelog
449 449 cn = cl.node(changerev)
450 450 cs = cl.read(cn)
451 451 t = float(cs[2].split(' ')[0])
452 452 mfn = cs[0]
453 453
454 454 def annotate(**map):
455 455 parity = 1
456 456 last = None
457 457 for r, l in fl.annotate(n):
458 458 try:
459 459 cnode = ncache[r]
460 460 except KeyError:
461 461 cnode = ncache[r] = self.repo.changelog.node(r)
462 462
463 463 try:
464 464 name = bcache[r]
465 465 except KeyError:
466 466 cl = self.repo.changelog.read(cnode)
467 467 name = cl[1]
468 468 f = name.find('@')
469 469 if f >= 0:
470 470 name = name[:f]
471 471 f = name.find('<')
472 472 if f >= 0:
473 473 name = name[f+1:]
474 474 bcache[r] = name
475 475
476 476 if last != cnode:
477 477 parity = 1 - parity
478 478 last = cnode
479 479
480 480 yield self.t("annotateline",
481 481 parity = parity,
482 482 node = hex(cnode),
483 483 rev = r,
484 484 author = name,
485 485 file = f,
486 486 line = l)
487 487
488 488 yield self.t("fileannotate",
489 489 file = f,
490 490 filenode = node,
491 491 annotate = annotate,
492 492 path = up(f),
493 493 rev = changerev,
494 494 node = hex(cn),
495 495 manifest = hex(mfn),
496 496 author = cs[1],
497 497 date = t,
498 498 parent = self.parents("fileannotateparent",
499 499 fl.parents(n), fl.rev, file=f),
500 500 permissions = self.repo.manifest.readflags(mfn)[f])
501 501
502 502 def manifest(self, mnode, path):
503 503 mf = self.repo.manifest.read(bin(mnode))
504 504 rev = self.repo.manifest.rev(bin(mnode))
505 505 node = self.repo.changelog.node(rev)
506 506 mff=self.repo.manifest.readflags(bin(mnode))
507 507
508 508 files = {}
509 509
510 510 p = path[1:]
511 511 l = len(p)
512 512
513 513 for f,n in mf.items():
514 514 if f[:l] != p:
515 515 continue
516 516 remain = f[l:]
517 517 if "/" in remain:
518 518 short = remain[:remain.find("/") + 1] # bleah
519 519 files[short] = (f, None)
520 520 else:
521 521 short = os.path.basename(remain)
522 522 files[short] = (f, n)
523 523
524 524 def filelist(**map):
525 525 parity = 0
526 526 fl = files.keys()
527 527 fl.sort()
528 528 for f in fl:
529 529 full, fnode = files[f]
530 530 if fnode:
531 531 yield self.t("manifestfileentry",
532 532 file = full,
533 533 manifest = mnode,
534 534 filenode = hex(fnode),
535 535 parity = parity,
536 536 basename = f,
537 537 permissions = mff[full])
538 538 else:
539 539 yield self.t("manifestdirentry",
540 540 parity = parity,
541 541 path = os.path.join(path, f),
542 542 manifest = mnode, basename = f[:-1])
543 543 parity = 1 - parity
544 544
545 545 yield self.t("manifest",
546 546 manifest = mnode,
547 547 rev = rev,
548 548 node = hex(node),
549 549 path = path,
550 550 up = up(path),
551 551 entries = filelist)
552 552
553 553 def tags(self):
554 554 cl = self.repo.changelog
555 555 mf = cl.read(cl.tip())[0]
556 556
557 557 i = self.repo.tagslist()
558 558 i.reverse()
559 559
560 560 def entries(**map):
561 561 parity = 0
562 562 for k,n in i:
563 563 yield self.t("tagentry",
564 564 parity = parity,
565 565 tag = k,
566 566 node = hex(n))
567 567 parity = 1 - parity
568 568
569 569 yield self.t("tags",
570 570 manifest = hex(mf),
571 571 entries = entries)
572 572
573 573 def filediff(self, file, changeset):
574 574 n = bin(changeset)
575 575 cl = self.repo.changelog
576 576 p1 = cl.parents(n)[0]
577 577 cs = cl.read(n)
578 578 mf = self.repo.manifest.read(cs[0])
579 579
580 580 def diff(**map):
581 581 yield self.diff(p1, n, file)
582 582
583 583 yield self.t("filediff",
584 584 file = file,
585 585 filenode = hex(mf.get(file, nullid)),
586 586 node = changeset,
587 587 rev = self.repo.changelog.rev(n),
588 588 parent = self.parents("filediffparent",
589 589 cl.parents(n), cl.rev),
590 590 diff = diff)
591 591
592 592 # add tags to things
593 593 # tags -> list of changesets corresponding to tags
594 594 # find tag, changeset, file
595 595
596 596 def run(self):
597 597 def header(**map):
598 598 yield self.t("header", **map)
599 599
600 600 def footer(**map):
601 601 yield self.t("footer", **map)
602 602
603 603 self.refresh()
604 604 args = cgi.parse()
605 605
606 606 t = self.templates or self.repo.ui.config("web", "templates",
607 607 templatepath())
608 608 m = os.path.join(t, "map")
609 609 if args.has_key('style'):
610 610 b = os.path.basename("map-" + args['style'][0])
611 611 p = os.path.join(self.templates, b)
612 612 if os.path.isfile(p): m = p
613 613
614 614 port = os.environ["SERVER_PORT"]
615 615 port = port != "80" and (":" + port) or ""
616 616 uri = os.environ["REQUEST_URI"]
617 617 if "?" in uri: uri = uri.split("?")[0]
618 618 url = "http://%s%s%s" % (os.environ["SERVER_NAME"], port, uri)
619 619
620 620 name = self.reponame or self.repo.ui.config("web", "name", os.getcwd())
621 621
622 622 self.t = templater(m, common_filters,
623 623 {"url":url,
624 624 "repo":name,
625 625 "header":header,
626 626 "footer":footer,
627 627 })
628 628
629 629 if not args.has_key('cmd'):
630 630 args['cmd'] = [self.t.cache['default'],]
631 631
632 632 if args['cmd'][0] == 'changelog':
633 633 c = self.repo.changelog.count() - 1
634 634 hi = c
635 635 if args.has_key('rev'):
636 636 hi = args['rev'][0]
637 637 try:
638 638 hi = self.repo.changelog.rev(self.repo.lookup(hi))
639 639 except RepoError:
640 640 write(self.search(hi))
641 641 return
642 642
643 643 write(self.changelog(hi))
644 644
645 645 elif args['cmd'][0] == 'changeset':
646 646 write(self.changeset(args['node'][0]))
647 647
648 648 elif args['cmd'][0] == 'manifest':
649 649 write(self.manifest(args['manifest'][0], args['path'][0]))
650 650
651 651 elif args['cmd'][0] == 'tags':
652 652 write(self.tags())
653 653
654 654 elif args['cmd'][0] == 'filediff':
655 655 write(self.filediff(args['file'][0], args['node'][0]))
656 656
657 657 elif args['cmd'][0] == 'file':
658 658 write(self.filerevision(args['file'][0], args['filenode'][0]))
659 659
660 660 elif args['cmd'][0] == 'annotate':
661 661 write(self.fileannotate(args['file'][0], args['filenode'][0]))
662 662
663 663 elif args['cmd'][0] == 'filelog':
664 664 write(self.filelog(args['file'][0], args['filenode'][0]))
665 665
666 666 elif args['cmd'][0] == 'heads':
667 667 httphdr("application/mercurial-0.1")
668 668 h = self.repo.heads()
669 669 sys.stdout.write(" ".join(map(hex, h)) + "\n")
670 670
671 671 elif args['cmd'][0] == 'branches':
672 672 httphdr("application/mercurial-0.1")
673 673 nodes = []
674 674 if args.has_key('nodes'):
675 675 nodes = map(bin, args['nodes'][0].split(" "))
676 676 for b in self.repo.branches(nodes):
677 677 sys.stdout.write(" ".join(map(hex, b)) + "\n")
678 678
679 679 elif args['cmd'][0] == 'between':
680 680 httphdr("application/mercurial-0.1")
681 681 nodes = []
682 682 if args.has_key('pairs'):
683 683 pairs = [ map(bin, p.split("-"))
684 684 for p in args['pairs'][0].split(" ") ]
685 685 for b in self.repo.between(pairs):
686 686 sys.stdout.write(" ".join(map(hex, b)) + "\n")
687 687
688 688 elif args['cmd'][0] == 'changegroup':
689 689 httphdr("application/mercurial-0.1")
690 690 nodes = []
691 691 if self.viewonly:
692 692 return
693 693
694 694 if args.has_key('roots'):
695 695 nodes = map(bin, args['roots'][0].split(" "))
696 696
697 697 z = zlib.compressobj()
698 698 f = self.repo.changegroup(nodes)
699 699 while 1:
700 700 chunk = f.read(4096)
701 701 if not chunk: break
702 702 sys.stdout.write(z.compress(chunk))
703 703
704 704 sys.stdout.write(z.flush())
705 705
706 706 else:
707 707 write(self.t("error"))
708 708
709 709 def create_server(path, name, templates, address, port, use_ipv6 = False,
710 710 accesslog = sys.stdout, errorlog = sys.stderr):
711 711
712 712 def openlog(opt, default):
713 713 if opt and opt != '-':
714 714 return open(opt, 'w')
715 715 return default
716 716
717 717 u = ui()
718 718 repo = repository(u, path)
719 719 if not address:
720 720 address = u.config("web", "address", "")
721 721 if not port:
722 722 port = int(u.config("web", "port", 8000))
723 723 if not use_ipv6:
724 724 use_ipv6 = u.configbool("web", "ipv6")
725 725
726 726 accesslog = openlog(accesslog or u.config("web", "accesslog", "-"),
727 727 sys.stdout)
728 728 errorlog = openlog(errorlog or u.config("web", "errorlog", "-"),
729 729 sys.stderr)
730 730
731 731 import BaseHTTPServer
732 732
733 733 class IPv6HTTPServer(BaseHTTPServer.HTTPServer):
734 734 address_family = getattr(socket, 'AF_INET6', None)
735 735
736 736 def __init__(self, *args, **kwargs):
737 737 if self.address_family is None:
738 738 raise RepoError('IPv6 not available on this system')
739 739 BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
740 740
741 741 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
742 742 def log_error(self, format, *args):
743 743 errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
744 744 self.log_date_time_string(),
745 745 format % args))
746 746
747 747 def log_message(self, format, *args):
748 748 accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
749 749 self.log_date_time_string(),
750 750 format % args))
751 751
752 752 def do_POST(self):
753 753 try:
754 754 self.do_hgweb()
755 755 except socket.error, inst:
756 756 if inst.args[0] != 32: raise
757 757
758 758 def do_GET(self):
759 759 self.do_POST()
760 760
761 761 def do_hgweb(self):
762 762 query = ""
763 763 p = self.path.find("?")
764 764 if p:
765 765 query = self.path[p + 1:]
766 766 query = query.replace('+', ' ')
767 767
768 768 env = {}
769 769 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
770 770 env['REQUEST_METHOD'] = self.command
771 771 env['SERVER_NAME'] = self.server.server_name
772 772 env['SERVER_PORT'] = str(self.server.server_port)
773 773 env['REQUEST_URI'] = "/"
774 774 if query:
775 775 env['QUERY_STRING'] = query
776 776 host = self.address_string()
777 777 if host != self.client_address[0]:
778 778 env['REMOTE_HOST'] = host
779 779 env['REMOTE_ADDR'] = self.client_address[0]
780 780
781 781 if self.headers.typeheader is None:
782 782 env['CONTENT_TYPE'] = self.headers.type
783 783 else:
784 784 env['CONTENT_TYPE'] = self.headers.typeheader
785 785 length = self.headers.getheader('content-length')
786 786 if length:
787 787 env['CONTENT_LENGTH'] = length
788 788 accept = []
789 789 for line in self.headers.getallmatchingheaders('accept'):
790 790 if line[:1] in "\t\n\r ":
791 791 accept.append(line.strip())
792 792 else:
793 793 accept = accept + line[7:].split(',')
794 794 env['HTTP_ACCEPT'] = ','.join(accept)
795 795
796 796 os.environ.update(env)
797 797
798 798 save = sys.argv, sys.stdin, sys.stdout, sys.stderr
799 799 try:
800 800 sys.stdin = self.rfile
801 801 sys.stdout = self.wfile
802 802 sys.argv = ["hgweb.py"]
803 803 if '=' not in query:
804 804 sys.argv.append(query)
805 805 self.send_response(200, "Script output follows")
806 806 hg.run()
807 807 finally:
808 808 sys.argv, sys.stdin, sys.stdout, sys.stderr = save
809 809
810 810 hg = hgweb(path, name, templates)
811 811 if use_ipv6:
812 812 return IPv6HTTPServer((address, port), hgwebhandler)
813 813 else:
814 814 return BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
815 815
816 816 def server(path, name, templates, address, port, use_ipv6 = False,
817 817 accesslog = sys.stdout, errorlog = sys.stderr):
818 818 httpd = create_server(path, name, templates, address, port, use_ipv6,
819 819 accesslog, errorlog)
820 820 httpd.serve_forever()
821 821
822 822 # This is a stopgap
823 823 class hgwebdir:
824 824 def __init__(self, config):
825 825 self.cp = ConfigParser.SafeConfigParser()
826 826 self.cp.read(config)
827 827
828 828 def run(self):
829 829 try:
830 830 virtual = os.environ["PATH_INFO"]
831 831 except:
832 832 virtual = ""
833 833
834 834 if virtual:
835 835 real = self.cp.get("paths", virtual[1:])
836 836 h = hgweb(real)
837 837 h.run()
838 838 return
839 839
840 840 def header(**map):
841 841 yield tmpl("header", **map)
842 842
843 843 def footer(**map):
844 844 yield tmpl("footer", **map)
845 845
846 846 templates = templatepath()
847 847 m = os.path.join(templates, "map")
848 848 tmpl = templater(m, common_filters,
849 849 {"header": header, "footer": footer})
850 850
851 851 def entries(**map):
852 852 parity = 0
853 for v,r in self.cp.items("paths"):
853 l = self.cp.items("paths")
854 l.sort()
855 for v,r in l:
854 856 cp2 = ConfigParser.SafeConfigParser()
855 857 cp2.read(os.path.join(r, ".hg", "hgrc"))
856 858
857 859 def get(sec, val, default):
858 860 try:
859 861 return cp2.get(sec, val)
860 862 except:
861 863 return default
862 864
863 865 yield tmpl("indexentry",
864 866 author = get("web", "author", "unknown"),
865 867 name = get("web", "name", v),
866 868 url = os.environ["REQUEST_URI"] + "/" + v,
867 869 parity = parity,
868 870 shortdesc = get("web", "description", "unknown"),
869 871 lastupdate = os.stat(os.path.join(r, ".hg",
870 872 "00changelog.d")).st_mtime)
871 873
872 874 parity = 1 - parity
873 875
874 876 write(tmpl("index", entries = entries))
General Comments 0
You need to be logged in to leave comments. Login now