chiark / gitweb /
agpl.py: Exclude the root directory from listers.
[chopwood] / cgi.py
diff --git a/cgi.py b/cgi.py
index 0bd66cc5ff642049b378cec4f3e41d6ff80202ba..0ecdfe20fc7485ef2b9e040571753097184f6789 100644 (file)
--- a/cgi.py
+++ b/cgi.py
@@ -59,7 +59,7 @@ CONF.DEFAULTS.update(
 ## Some handy regular expressions.
 R_URLESC = RX.compile('%([0-9a-fA-F]{2})')
 R_URLBAD = RX.compile('[^-\\w,.!]')
-R_HTMLBAD = RX.compile('[&<>]')
+R_HTMLBAD = RX.compile('[&<>\'"]')
 
 def urldecode(s):
   """Decode a single form-url-encoded string S."""
@@ -77,17 +77,18 @@ def htmlescape(s):
 
 ## Some standard character sequences, and HTML entity names for prettier
 ## versions.
-_quotify = U.StringSubst({
+html_quotify = U.StringSubst({
+  "<": '&lt;',
+  ">": '&gt;',
+  "&": '&amp;',
   "`": '&lsquo;',
   "'": '&rsquo;',
+  '"': '&quot;',
   "``": '&ldquo;',
   "''": '&rdquo;',
   "--": '&ndash;',
   "---": '&mdash;'
 })
-def html_quotify(s):
-  """Return a pretty HTML version of S."""
-  return _quotify(htmlescape(s))
 
 ###--------------------------------------------------------------------------
 ### Output machinery.
@@ -196,7 +197,8 @@ def set_template_keywords():
     package = PACKAGE,
     version = VERSION,
     script = CFG.SCRIPT_NAME,
-    static = CFG.STATIC)
+    static = CFG.STATIC,
+    allowop = CFG.ALLOWOP)
 
 class TemplateFinder (object):
   """
@@ -211,7 +213,7 @@ class TemplateFinder (object):
     with open(OS.path.join(me._dir, key)) as f: tmpl = f.read()
     me._cache[key] = tmpl
     return tmpl
-TMPL = TemplateFinder(TMPLDIR)
+STATE.kw['TMPL'] = TMPL = TemplateFinder(TMPLDIR)
 
 @CTX.contextmanager
 def tmplkw(**kw):
@@ -229,13 +231,36 @@ class FormatHTML (F.SimpleFormatOperation):
   """
   ~H: escape output suitable for inclusion in HTML.
 
-  With `:', instead apply form-urlencoding.
+  With `:', additionally apply quotification.
   """
   def _convert(me, arg):
     if me.colonp: return html_quotify(arg)
     else: return htmlescape(arg)
 FORMATOPS['H'] = FormatHTML
 
+class FormatWrap (F.BaseFormatOperation):
+  """
+  ~<...~@>: wrap enclosed material in another formatting control string.
+
+  The argument is a formatting control.  The enclosed material is split into
+  pieces separated by `~;' markers.  The formatting control is performed, and
+  passed the list of pieces (as compiled formatting operations) in the
+  keyword argument `wrapped'.
+  """
+  def __init__(me, *args):
+    super(FormatWrap, me).__init__(*args)
+    pieces = []
+    while True:
+      piece, delim = F.collect_subformat('>;')
+      pieces.append(piece)
+      if delim.char == '>': break
+    me.pieces = pieces
+  def _format(me, atp, colonp):
+    op = F.compile(me.getarg.get())
+    with F.FORMAT.bind(argmap = dict(F.FORMAT.argmap, wrapped = me.pieces)):
+      op.format()
+FORMATOPS['<'] = FormatWrap
+
 def format_tmpl(control, **kw):
   with F.COMPILE.bind(opmaps = [FORMATOPS, F.BASEOPS]):
     with tmplkw(**kw):