annotate mercurial/hgweb/webutil.py @ 7175:5d8626b2c1db

parsers.c: do not try to untrack after a failure
author Benoit Boissinot <benoit.boissinot@ens-lyon.org>
date Sun, 19 Oct 2008 20:17:06 +0200
parents fb42030d79d6
children f933076a19fc
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
6392
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
1 # hgweb/webutil.py - utility library for the web interface.
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
2 #
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
5 #
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
6 # This software may be used and distributed according to the terms
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
7 # of the GNU General Public License, incorporated herein by reference.
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
8
6393
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
9 import os
6392
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
10 from mercurial.node import hex, nullid
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
11 from mercurial.repo import RepoError
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
12 from mercurial import util
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
13
6393
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
14 def up(p):
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
15 if p[0] != "/":
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
16 p = "/" + p
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
17 if p[-1] == "/":
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
18 p = p[:-1]
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
19 up = os.path.dirname(p)
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
20 if up == "/":
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
21 return "/"
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
22 return up + "/"
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
23
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
24 def revnavgen(pos, pagelen, limit, nodefunc):
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
25 def seq(factor, limit=None):
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
26 if limit:
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
27 yield limit
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
28 if limit >= 20 and limit <= 40:
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
29 yield 50
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
30 else:
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
31 yield 1 * factor
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
32 yield 3 * factor
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
33 for f in seq(factor * 10):
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
34 yield f
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
35
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
36 def nav(**map):
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
37 l = []
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
38 last = 0
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
39 for f in seq(1, pagelen):
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
40 if f < pagelen or f <= last:
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
41 continue
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
42 if f > limit:
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
43 break
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
44 last = f
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
45 if pos + f < limit:
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
46 l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
47 if pos - f >= 0:
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
48 l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
49
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
50 try:
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
51 yield {"label": "(0)", "node": hex(nodefunc('0').node())}
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
52
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
53 for label, node in l:
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
54 yield {"label": label, "node": node}
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
55
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
56 yield {"label": "tip", "node": "tip"}
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
57 except RepoError:
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
58 pass
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
59
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
60 return nav
894875eae49b hgweb: refactor hgweb code
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6392
diff changeset
61
6392
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
62 def siblings(siblings=[], hiderev=None, **args):
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
63 siblings = [s for s in siblings if s.node() != nullid]
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
64 if len(siblings) == 1 and siblings[0].rev() == hiderev:
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
65 return
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
66 for s in siblings:
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
67 d = {'node': hex(s.node()), 'rev': s.rev()}
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
68 if hasattr(s, 'path'):
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
69 d['file'] = s.path()
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
70 d.update(args)
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
71 yield d
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
72
6434
62e0bb41e682 hgweb: minor improvements for new web style
Matt Mackall <mpm@selenic.com>
parents: 6413
diff changeset
73 def renamelink(fctx):
6437
101526031d06 hgweb: fix merge breakage
Matt Mackall <mpm@selenic.com>
parents: 6434
diff changeset
74 r = fctx.renamed()
6392
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
75 if r:
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
76 return [dict(file=r[0], node=hex(r[1]))]
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
77 return []
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
78
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
79 def nodetagsdict(repo, node):
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
80 return [{"name": i} for i in repo.nodetags(node)]
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
81
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
82 def nodebranchdict(repo, ctx):
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
83 branches = []
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
84 branch = ctx.branch()
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
85 # If this is an empty repo, ctx.node() == nullid,
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
86 # ctx.branch() == 'default', but branchtags() is
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
87 # an empty dict. Using dict.get avoids a traceback.
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
88 if repo.branchtags().get(branch) == ctx.node():
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
89 branches.append({"name": branch})
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
90 return branches
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
91
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
92 def nodeinbranch(repo, ctx):
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
93 branches = []
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
94 branch = ctx.branch()
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
95 if branch != 'default' and repo.branchtags().get(branch) != ctx.node():
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
96 branches.append({"name": branch})
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
97 return branches
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
98
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
99 def nodebranchnodefault(ctx):
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
100 branches = []
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
101 branch = ctx.branch()
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
102 if branch != 'default':
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
103 branches.append({"name": branch})
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
104 return branches
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
105
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
106 def showtag(repo, tmpl, t1, node=nullid, **args):
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
107 for t in repo.nodetags(node):
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
108 yield tmpl(t1, tag=t, **args)
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
109
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
110 def cleanpath(repo, path):
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
111 path = path.lstrip('/')
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
112 return util.canonpath(repo.root, '', path)
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
113
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
114 def changectx(repo, req):
6750
fb42030d79d6 add __len__ and __iter__ methods to repo and revlog
Matt Mackall <mpm@selenic.com>
parents: 6747
diff changeset
115 changeid = "tip"
6392
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
116 if 'node' in req.form:
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
117 changeid = req.form['node'][0]
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
118 elif 'manifest' in req.form:
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
119 changeid = req.form['manifest'][0]
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
120
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
121 try:
6747
f6c00b17387c use repo[changeid] to get a changectx
Matt Mackall <mpm@selenic.com>
parents: 6437
diff changeset
122 ctx = repo[changeid]
6392
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
123 except RepoError:
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
124 man = repo.manifest
6747
f6c00b17387c use repo[changeid] to get a changectx
Matt Mackall <mpm@selenic.com>
parents: 6437
diff changeset
125 ctx = repo[man.linkrev(man.lookup(changeid))]
6392
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
126
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
127 return ctx
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
128
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
129 def filectx(repo, req):
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
130 path = cleanpath(repo, req.form['file'][0])
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
131 if 'node' in req.form:
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
132 changeid = req.form['node'][0]
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
133 else:
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
134 changeid = req.form['filenode'][0]
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
135 try:
6747
f6c00b17387c use repo[changeid] to get a changectx
Matt Mackall <mpm@selenic.com>
parents: 6437
diff changeset
136 fctx = repo[changeid][path]
6392
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
137 except RepoError:
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
138 fctx = repo.filectx(path, fileid=changeid)
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
139
2540521dc7c1 hgweb: separate out utility functions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
diff changeset
140 return fctx