about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--adapter/format/ansi2html.nim32
-rwxr-xr-xbonus/git.cgi13
-rw-r--r--res/mailcap2
-rw-r--r--src/config/mailcap.nim1
-rw-r--r--src/utils/twtstr.nim38
5 files changed, 58 insertions, 28 deletions
diff --git a/adapter/format/ansi2html.nim b/adapter/format/ansi2html.nim
index 4266a3ba..9abc6925 100644
--- a/adapter/format/ansi2html.nim
+++ b/adapter/format/ansi2html.nim
@@ -356,11 +356,39 @@ proc processData(state: var State, buf: openArray[char]) =
     of '\0': state.puts("\uFFFD") # HTML eats NUL, so replace it here
     else: state.putc(c)
 
+proc usage() =
+  stderr.write("Usage: ansihtml [-s] [-t title]\n")
+  quit(1)
+
 proc main() =
   var state = State()
-  let standalone = paramCount() >= 1 and paramStr(1) == "-s"
+  # parse args
+  let H = paramCount()
+  var i = 1
+  var standalone = false
+  var title = ""
+  while i <= H:
+    let s = paramStr(i)
+    if s == "":
+      inc i
+    if s[0] != '-':
+      usage()
+    for j in 1 ..< s.len:
+      case s[j]
+      of 's':
+        standalone = true
+      of 't':
+        inc i
+        if i > H: usage()
+        title = paramStr(i).percentDecode()
+      else: discard
+    inc i
+  if standalone:
+    state.puts("<!DOCTYPE html>\n")
+  if title != "":
+    state.puts("<title>" & title.htmlEscape() & "</title>\n")
   if standalone:
-    state.puts("<!DOCTYPE html>\n<body>")
+    state.puts("<body>\n")
   state.puts("<pre style='margin: 0'>\n")
   let ofl = fcntl(STDIN_FILENO, F_GETFL, 0)
   doAssert ofl != -1
diff --git a/bonus/git.cgi b/bonus/git.cgi
index 1f93862a..387f3a64 100755
--- a/bonus/git.cgi
+++ b/bonus/git.cgi
@@ -27,12 +27,7 @@ for (const p of std.getenv("QUERY_STRING").split('&')) {
 }
 
 function startGitCmd(config, params) {
-	const titleParams = params.join(' ').replace(/[&<>]/g,
-		x => ({'&': '&amp', '<': '&lt', '>': '&gt'}[x]));
-	std.out.puts(`Content-Type: text/html
-
-<!DOCTYPE html>
-<title>git ${titleParams}</title>`);
+	std.out.puts("Content-Type: text/html\n\n");
 	std.out.flush();
 	const [read_fd, write_fd] = os.pipe();
 	const [read_fd2, write_fd2] = os.pipe();
@@ -43,7 +38,8 @@ function startGitCmd(config, params) {
 	os.close(write_fd);
 	const libexecDir = std.getenv("CHA_LIBEXEC_DIR") ??
 		'/usr/local/libexec/chawan';
-	os.exec([libexecDir + "/ansi2html"], {
+	const title = encodeURIComponent('git ' + params.join(' '));
+	os.exec([libexecDir + "/ansi2html", "-st", title], {
 		stdin: read_fd,
 		stdout: write_fd2,
 		block: false
@@ -78,7 +74,8 @@ if (params[0] == "log") {
 	}
 	f.close();
 } else {
-	std.out.puts("Content-Type: text/x-ansi\n\n");
+	const title = encodeURIComponent('git ' + params.join(' '));
+	std.out.puts(`Content-Type: text/x-ansi;title=${title}\n\n`);
 	std.out.flush();
 	const pid = os.exec(["git", ...config, ...params], {
 		block: false,
diff --git a/res/mailcap b/res/mailcap
index 4d6619d7..56c6582e 100644
--- a/res/mailcap
+++ b/res/mailcap
@@ -3,4 +3,4 @@
 text/gopher;	"$CHA_LIBEXEC_DIR"/gopher2html -u "$MAILCAP_URL"; x-htmloutput
 text/gemini;	"$CHA_LIBEXEC_DIR"/gmi2html; x-htmloutput
 text/markdown;	"$CHA_LIBEXEC_DIR"/md2html; x-htmloutput
-text/x-ansi;	"$CHA_LIBEXEC_DIR"/ansi2html -s; x-htmloutput
+text/x-ansi;	"$CHA_LIBEXEC_DIR"/ansi2html -st '%{title}'; x-htmloutput
diff --git a/src/config/mailcap.nim b/src/config/mailcap.nim
index b108ca20..41575ca8 100644
--- a/src/config/mailcap.nim
+++ b/src/config/mailcap.nim
@@ -308,6 +308,7 @@ proc unquoteCommand*(ecmd, contentType, outpath: string; url: URL;
         let s = contentType.getContentTypeAttr(attrname)
         cmd &= quoteFile(s, qs)
         attrname = ""
+        state = STATE_NORMAL
       elif c == '\\':
         state = STATE_ATTR_QUOTED
       else:
diff --git a/src/utils/twtstr.nim b/src/utils/twtstr.nim
index 6f40ae7b..f0a64546 100644
--- a/src/utils/twtstr.nim
+++ b/src/utils/twtstr.nim
@@ -678,22 +678,26 @@ func strictParseEnum*[T: enum](s: string): Opt[T] =
   return err()
 
 proc getContentTypeAttr*(contentType, attrname: string): string =
-  let kvs = contentType.after(';')
-  var i = kvs.find(attrname)
+  var i = contentType.find(';')
+  if i == -1:
+    return ""
+  i = contentType.find(attrname, i)
+  if i == -1:
+    return ""
+  i = contentType.skipBlanks(i + attrname.len)
+  if i >= contentType.len or contentType[i] != '=':
+    return ""
+  i = contentType.skipBlanks(i + 1)
+  var q = false
   var s = ""
-  if i != -1 and kvs.len > i + attrname.len and
-      kvs[i + attrname.len] == '=':
-    i += attrname.len + 1
-    while i < kvs.len and kvs[i] in AsciiWhitespace:
-      inc i
-    var q = false
-    for j, c in kvs.toOpenArray(i, kvs.high):
-      if q:
-        s &= c
-      elif c == '\\':
-        q = true
-      elif c == ';' or c in AsciiWhitespace:
-        break
-      else:
-        s &= c
+  for c in contentType.toOpenArray(i, contentType.high):
+    if q:
+      s &= c
+      q = false
+    elif c == '\\':
+      q = true
+    elif c in AsciiWhitespace + {';'}:
+      break
+    else:
+      s &= c
   return s