about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Makefile44
-rw-r--r--adapter/protocol/curlwrap.nim7
-rw-r--r--adapter/protocol/file.nim9
-rw-r--r--adapter/protocol/ftp.nim28
-rw-r--r--adapter/protocol/gopher.nim51
-rw-r--r--adapter/protocol/http.nim4
-rw-r--r--res/urimethodmap12
-rw-r--r--src/config/config.nim2
-rw-r--r--src/layout/engine.nim1
-rw-r--r--src/local/container.nim1
-rw-r--r--src/local/select.nim4
-rw-r--r--src/types/url.nim1
-rw-r--r--src/utils/luwrap.nim46
-rw-r--r--src/utils/twtstr.nim37
14 files changed, 145 insertions, 102 deletions
diff --git a/Makefile b/Makefile
index 023f66db..552da3d8 100644
--- a/Makefile
+++ b/Makefile
@@ -38,8 +38,6 @@ else ifeq ($(TARGET),release1)
 FLAGS += -d:release --debugger:native
 endif
 
-FLAGS += --nimcache:"$(OBJDIR)/$(TARGET)"
-
 .PHONY: all
 all: $(OUTDIR_BIN)/cha $(OUTDIR_CGI_BIN)/http \
 	$(OUTDIR_CGI_BIN)/gmifetch $(OUTDIR_LIBEXEC)/gmi2html \
@@ -49,7 +47,8 @@ all: $(OUTDIR_BIN)/cha $(OUTDIR_CGI_BIN)/http \
 
 $(OUTDIR_BIN)/cha: lib/libquickjs.a src/*.nim src/**/*.nim res/* res/**/*
 	@mkdir -p "$(OUTDIR)/$(TARGET)/bin"
-	$(NIMC) -d:libexecPath=$(LIBEXECDIR) $(FLAGS) -o:"$(OUTDIR_BIN)/cha" src/main.nim
+	$(NIMC) --nimcache:"$(OBJDIR)/$(TARGET)/cha" -d:libexecPath=$(LIBEXECDIR) \
+		$(FLAGS) -o:"$(OUTDIR_BIN)/cha" src/main.nim
 	ln -sf "$(OUTDIR)/$(TARGET)/bin/cha" cha
 
 $(OUTDIR_LIBEXEC)/gopher2html: adapter/format/gopher2html.nim \
@@ -71,33 +70,33 @@ $(OUTDIR_CGI_BIN)/cha-finger: adapter/protocol/cha-finger
 
 $(OUTDIR_CGI_BIN)/http: adapter/protocol/http.nim adapter/protocol/curlwrap.nim \
 		adapter/protocol/curlerrors.nim src/bindings/curl.nim \
-		src/types/opt.nim src/utils/twtstr.nim
-	$(NIMC) $(FLAGS) -d:curlLibName:$(CURLLIBNAME) \
+		src/utils/twtstr.nim
+	$(NIMC) $(FLAGS) --nimcache:"$(OBJDIR)/$(TARGET)/http" -d:curlLibName:$(CURLLIBNAME) \
 		-o:"$(OUTDIR_CGI_BIN)/http" adapter/protocol/http.nim
 
 $(OUTDIR_CGI_BIN)/about: adapter/protocol/about.nim
-	$(NIMC) $(FLAGS) -o:"$(OUTDIR_CGI_BIN)/about" adapter/protocol/about.nim
+	$(NIMC) $(FLAGS) --nimcache:"$(OBJDIR)/$(TARGET)/about" -o:"$(OUTDIR_CGI_BIN)/about" adapter/protocol/about.nim
 
 $(OUTDIR_CGI_BIN)/data: adapter/protocol/data.nim src/utils/twtstr.nim
-	$(NIMC) $(FLAGS) -o:"$(OUTDIR_CGI_BIN)/data" adapter/protocol/data.nim
+	$(NIMC) $(FLAGS) --nimcache:"$(OBJDIR)/$(TARGET)/data" -o:"$(OUTDIR_CGI_BIN)/data" adapter/protocol/data.nim
 
 $(OUTDIR_CGI_BIN)/file: adapter/protocol/file.nim adapter/protocol/dirlist.nim \
 		src/utils/twtstr.nim src/utils/strwidth.nim src/data/charwidth.nim \
 		res/map/EastAsianWidth.txt src/loader/connecterror.nim
-	$(NIMC) $(FLAGS) -o:"$(OUTDIR_CGI_BIN)/file" adapter/protocol/file.nim
+	$(NIMC) $(FLAGS) --nimcache:"$(OBJDIR)/$(TARGET)/file" -o:"$(OUTDIR_CGI_BIN)/file" adapter/protocol/file.nim
 
-$(OUTDIR_CGI_BIN)/ftp: adapter/protocol/ftp.nim src/bindings/curl.nim \
-		adapter/protocol/dirlist.nim src/utils/twtstr.nim src/types/url.nim \
+$(OUTDIR_CGI_BIN)/ftp: adapter/protocol/ftp.nim adapter/protocol/dirlist.nim \
 		src/utils/twtstr.nim src/utils/strwidth.nim src/data/charwidth.nim \
-		res/map/EastAsianWidth.txt src/types/opt.nim src/loader/connecterror.nim
-	$(NIMC) $(FLAGS) -d:curlLibName:$(CURLLIBNAME) \
+		res/map/EastAsianWidth.txt src/loader/connecterror.nim \
+		src/types/opt.nim src/bindings/curl.nim
+	$(NIMC) $(FLAGS) -d:curlLibName:$(CURLLIBNAME) --nimcache:"$(OBJDIR)/$(TARGET)/ftp" \
 		-o:"$(OUTDIR_CGI_BIN)/ftp" adapter/protocol/ftp.nim
 
 $(OUTDIR_CGI_BIN)/gopher: adapter/protocol/gopher.nim adapter/protocol/curlwrap.nim \
 		adapter/protocol/curlerrors.nim adapter/gophertypes.nim \
 		src/bindings/curl.nim src/loader/connecterror.nim \
-		src/utils/twtstr.nim src/types/url.nim src/types/opt.nim
-	$(NIMC) $(FLAGS) -d:curlLibName:$(CURLLIBNAME) \
+		src/utils/twtstr.nim
+	$(NIMC) $(FLAGS) -d:curlLibName:$(CURLLIBNAME) --nimcache:"$(OBJDIR)/$(TARGET)/gopher" \
 		-o:"$(OUTDIR_CGI_BIN)/gopher" adapter/protocol/gopher.nim
 
 CFLAGS = -g -Wall -O2 -DCONFIG_VERSION=\"$(shell cat lib/quickjs/VERSION)\"
@@ -154,6 +153,12 @@ install:
 	install -m755 "$(OUTDIR_BIN)/cha" "$(DESTDIR)$(PREFIX)/bin"
 	@# intentionally not quoted
 	mkdir -p $(LIBEXECDIR_CHAWAN)/cgi-bin
+	install -m755 "$(OUTDIR_CGI_BIN)/http" $(LIBEXECDIR_CHAWAN)/cgi-bin
+	install -m755 "$(OUTDIR_CGI_BIN)/about" $(LIBEXECDIR_CHAWAN)/cgi-bin
+	install -m755 "$(OUTDIR_CGI_BIN)/data" $(LIBEXECDIR_CHAWAN)/cgi-bin
+	install -m755 "$(OUTDIR_CGI_BIN)/file" $(LIBEXECDIR_CHAWAN)/cgi-bin
+	install -m755 "$(OUTDIR_CGI_BIN)/ftp" $(LIBEXECDIR_CHAWAN)/cgi-bin
+	install -m755 "$(OUTDIR_CGI_BIN)/gopher" $(LIBEXECDIR_CHAWAN)/cgi-bin
 	install -m755 "$(OUTDIR_LIBEXEC)/gopher2html" $(LIBEXECDIR_CHAWAN)
 	install -m755 "$(OUTDIR_LIBEXEC)/gmi2html" $(LIBEXECDIR_CHAWAN)
 	install -m755 "$(OUTDIR_CGI_BIN)/gmifetch" $(LIBEXECDIR_CHAWAN)/cgi-bin
@@ -173,16 +178,17 @@ install:
 uninstall:
 	rm -f "$(DESTDIR)$(PREFIX)/bin/cha"
 	@# intentionally not quoted
-	rm -f $(LIBEXECDIR_CHAWAN)/gopher2html
-	rm -f $(LIBEXECDIR_CHAWAN)/gmi2html
 	rm -f $(LIBEXECDIR_CHAWAN)/cgi-bin/http
 	rm -f $(LIBEXECDIR_CHAWAN)/cgi-bin/about
 	rm -f $(LIBEXECDIR_CHAWAN)/cgi-bin/data
-	rm -f $(LIBEXECDIR_CHAWAN)/cgi-bin/gmifetch
-	rm -f $(LIBEXECDIR_CHAWAN)/cgi-bin/cha-finger
 	rm -f $(LIBEXECDIR_CHAWAN)/cgi-bin/ftp
 	rm -f $(LIBEXECDIR_CHAWAN)/cgi-bin/gopher
-	rmdir $(LIBEXECDIR_CHAWAN)/cgi-bin && rmdir $(LIBEXECDIR_CHAWAN) || true
+	rm -f $(LIBEXECDIR_CHAWAN)/cgi-bin/gmifetch
+	rm -f $(LIBEXECDIR_CHAWAN)/cgi-bin/cha-finger
+	rmdir $(LIBEXECDIR_CHAWAN)/cgi-bin || true
+	rm -f $(LIBEXECDIR_CHAWAN)/gopher2html
+	rm -f $(LIBEXECDIR_CHAWAN)/gmi2html
+	rmdir $(LIBEXECDIR_CHAWAN) || true
 	rm -f "$(DESTDIR)$(MANPREFIX5)/cha-config.5"
 	rm -f "$(DESTDIR)$(MANPREFIX5)/cha-mailcap.5"
 	rm -f "$(DESTDIR)$(MANPREFIX5)/cha-mime.types.5"
diff --git a/adapter/protocol/curlwrap.nim b/adapter/protocol/curlwrap.nim
index d5014b26..41ef5355 100644
--- a/adapter/protocol/curlwrap.nim
+++ b/adapter/protocol/curlwrap.nim
@@ -11,3 +11,10 @@ template getinfo*(curl: CURL, info: CURLINFO, arg: typed) =
 
 template set*(url: CURLU, part: CURLUPart, content: string, flags: cuint) =
   discard curl_url_set(url, part, cstring(content), flags)
+
+template get*(url: CURLU, part: CURLUPart, flags: cuint): cstring =
+  var outs: cstring
+  if curl_url_get(url, part, addr outs, flags) == CURLUE_OK:
+    outs
+  else:
+    nil
diff --git a/adapter/protocol/file.nim b/adapter/protocol/file.nim
index 168be58b..d05d558c 100644
--- a/adapter/protocol/file.nim
+++ b/adapter/protocol/file.nim
@@ -9,11 +9,11 @@ import dirlist
 import loader/connecterror
 import utils/twtstr
 
-proc loadDir(path: string) =
+proc loadDir(path, opath: string) =
   var path = path
   if path[^1] != '/': #TODO dos/windows
     path &= '/'
-  var base = getEnv("QUERY_STRING")
+  var base = "file://" & opath
   if base[^1] != '/': #TODO dos/windows
     base &= '/'
   stdout.write("Content-Type: text/html\n\n")
@@ -96,11 +96,12 @@ proc loadFile(istream: Stream) =
         break
 
 proc main() =
-  let path = percentDecode(getEnv("MAPPED_URI_PATH"))
+  let opath = getEnv("MAPPED_URI_PATH")
+  let path = percentDecode(opath)
   let istream = newFileStream(path, fmRead)
   if istream == nil:
     if dirExists(path):
-      loadDir(path)
+      loadDir(path, opath)
     elif symlinkExists(path):
       loadSymlink(path)
     else:
diff --git a/adapter/protocol/ftp.nim b/adapter/protocol/ftp.nim
index 7d072471..1a406743 100644
--- a/adapter/protocol/ftp.nim
+++ b/adapter/protocol/ftp.nim
@@ -9,7 +9,6 @@ import dirlist
 import bindings/curl
 import loader/connecterror
 import types/opt
-import types/url
 import utils/twtstr
 
 type FtpHandle = ref object
@@ -153,17 +152,27 @@ proc finish(op: FtpHandle) =
 proc main() =
   let curl = curl_easy_init()
   doAssert curl != nil
-  let surl = getEnv("QUERY_STRING")
   let opath = getEnv("MAPPED_URI_PATH")
   let path = percentDecode(opath)
+  let url = curl_url()
+  const flags = cuint(CURLU_PATH_AS_IS)
+  url.set(CURLUPART_SCHEME, getEnv("MAPPED_URI_SCHEME"), flags)
+  let username = getEnv("MAPPED_URI_USERNAME")
+  if username != "":
+    url.set(CURLUPART_USER, username, flags)
+  let password = getEnv("MAPPED_URI_PASSWORD")
+  if password != "":
+    url.set(CURLUPART_PASSWORD, password, flags)
+  url.set(CURLUPART_HOST, getEnv("MAPPED_URI_HOST"), flags)
+  let port = getEnv("MAPPED_URI_PORT")
+  if port != "":
+    url.set(CURLUPART_PORT, port, flags)
   # By default, cURL CWD's into relative paths, and an extra slash is
   # necessary to specify absolute paths.
   # This is incredibly confusing, and probably not what the user wanted.
   # So we work around it by adding the extra slash ourselves.
-  let hackurl = newURL(surl).get
-  hackurl.setPathname('/' & opath)
-  let csurl = hackurl.serialize()
-  curl.setopt(CURLOPT_URL, csurl)
+  url.set(CURLUPART_PATH, '/' & opath, flags)
+  curl.setopt(CURLOPT_CURLU, url)
   let dirmode = path.len > 0 and path[^1] == '/'
   let op = FtpHandle(
     curl: curl,
@@ -175,7 +184,12 @@ proc main() =
   curl.setopt(CURLOPT_WRITEFUNCTION, curlWriteBody)
   curl.setopt(CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD)
   if dirmode:
-    op.base = surl
+    const flags = cuint(CURLU_PUNY2IDN)
+    let surl = url.get(CURLUPART_URL, flags)
+    if surl == nil:
+      stdout.write("Cha-Control: ConnectionError " & $int(ERROR_INVALID_URL))
+      return
+    op.base = $surl
     op.path = path
   let purl = getEnv("ALL_PROXY")
   if purl != "":
diff --git a/adapter/protocol/gopher.nim b/adapter/protocol/gopher.nim
index 91875a1e..7ee7b625 100644
--- a/adapter/protocol/gopher.nim
+++ b/adapter/protocol/gopher.nim
@@ -1,5 +1,4 @@
 import std/envvars
-import std/options
 
 import curlwrap
 import curlerrors
@@ -8,8 +7,6 @@ import ../gophertypes
 
 import bindings/curl
 import loader/connecterror
-import types/opt
-import types/url
 import utils/twtstr
 
 type GopherHandle = ref object
@@ -63,35 +60,43 @@ proc main() =
   if getEnv("REQUEST_METHOD") != "GET":
     stdout.write("Cha-Control: ConnectionError " & $int(ERROR_INVALID_METHOD))
     return
-  var url = newURL(getEnv("QUERY_STRING")).get
-  var path = url.pathname
+  var path = getEnv("MAPPED_URI_PATH")
   if path.len < 1:
     path &= '/'
   if path.len < 2:
     path &= '1'
-    url = newURL(url)
-    url.setPathname(path)
+  let url = curl_url()
+  const flags = cuint(CURLU_PATH_AS_IS)
+  url.set(CURLUPART_SCHEME, getEnv("MAPPED_URI_SCHEME"), flags)
+  url.set(CURLUPART_HOST, getEnv("MAPPED_URI_HOST"), flags)
+  let port = getEnv("MAPPED_URI_PORT")
+  if port != "":
+    url.set(CURLUPART_PORT, port, flags)
+  url.set(CURLUPART_PATH, path, flags)
+  let query = getEnv("MAPPED_URI_QUERY")
+  if query != "":
+    url.set(CURLUPART_QUERY, query.after('='), flags)
   let op = GopherHandle(
     curl: curl,
     t: gopherType(path[1])
   )
-  if op.t == SEARCH:
-    if url.query.isNone:
-      op.loadSearch(url.serialize())
-      return
+  if op.t == SEARCH and query == "":
+    const flags = cuint(CURLU_PUNY2IDN)
+    let surl = url.get(CURLUPART_URL, flags)
+    if surl == nil:
+      stdout.write("Cha-Control: ConnectionError " & $int(ERROR_INVALID_URL))
     else:
-      url.query = some(url.query.get.after('='))
-  let surl = url.serialize()
-  #TODO avoid re-parsing
-  curl.setopt(CURLOPT_URL, surl)
-  curl.setopt(CURLOPT_WRITEDATA, op)
-  curl.setopt(CURLOPT_WRITEFUNCTION, curlWriteBody)
-  let proxy = getEnv("ALL_PROXY")
-  if proxy != "":
-    curl.setopt(CURLOPT_PROXY, proxy)
-  let res = curl_easy_perform(curl)
-  if res != CURLE_OK and not op.statusline:
-    stdout.write(getCurlConnectionError(res))
+      op.loadSearch($surl)
+  else:
+    curl.setopt(CURLOPT_CURLU, url)
+    curl.setopt(CURLOPT_WRITEDATA, op)
+    curl.setopt(CURLOPT_WRITEFUNCTION, curlWriteBody)
+    let proxy = getEnv("ALL_PROXY")
+    if proxy != "":
+      curl.setopt(CURLOPT_PROXY, proxy)
+    let res = curl_easy_perform(curl)
+    if res != CURLE_OK and not op.statusline:
+      stdout.write(getCurlConnectionError(res))
   curl_easy_cleanup(curl)
 
 main()
diff --git a/adapter/protocol/http.nim b/adapter/protocol/http.nim
index e59cac0a..45a78c46 100644
--- a/adapter/protocol/http.nim
+++ b/adapter/protocol/http.nim
@@ -1,12 +1,10 @@
 import std/envvars
-import std/options
 import std/strutils
 
 import curlerrors
 import curlwrap
 
 import bindings/curl
-import types/opt
 import utils/twtstr
 
 type
@@ -111,7 +109,7 @@ proc main() =
     curl.setopt(CURLOPT_HTTPGET, 1)
   of "POST":
     curl.setopt(CURLOPT_POST, 1)
-    let len = parseInt64(getEnv("CONTENT_LENGTH")).get
+    let len = parseInt(getEnv("CONTENT_LENGTH"))
     # > For any given platform/compiler curl_off_t must be typedef'ed to
     # a 64-bit
     # > wide signed integral data type. The width of this data type must remain
diff --git a/res/urimethodmap b/res/urimethodmap
index 3eddde2f..556c6c5a 100644
--- a/res/urimethodmap
+++ b/res/urimethodmap
@@ -6,9 +6,9 @@ finger:		cgi-bin:cha-finger
 gemini:		cgi-bin:gmifetch?%s
 about:		cgi-bin:about
 data:		cgi-bin:data
-file:		cgi-bin:file?%s
-ftp:		cgi-bin:ftp?%s
-sftp:		cgi-bin:sftp?%s
-ftps:		cgi-bin:ftps?%s
-gopher:		cgi-bin:gopher?%s
-gophers:	cgi-bin:gophers?%s
+file:		cgi-bin:file
+ftp:		cgi-bin:ftp
+sftp:		cgi-bin:sftp
+ftps:		cgi-bin:ftps
+gopher:		cgi-bin:gopher
+gophers:	cgi-bin:gophers
diff --git a/src/config/config.nim b/src/config/config.nim
index eec0bffd..38ab6ffb 100644
--- a/src/config/config.nim
+++ b/src/config/config.nim
@@ -350,7 +350,7 @@ proc readUserStylesheet(dir, file: string): string =
 # of several individual configuration files known as mailcap files.
 proc getMailcap*(config: Config): tuple[mailcap: Mailcap, errs: seq[string]] =
   let configDir = getConfigDir() / "chawan" #TODO store this in config?
-  const gopherPath0 = ChaPath("${%CHA_LIBEXEC_DIR}/gopher2html -u $MAILCAP_URL")
+  const gopherPath0 = ChaPath("${%CHA_LIBEXEC_DIR}/gopher2html -u \\$MAILCAP_URL")
   let gopherPath = gopherPath0.unquote().get
   const geminiPath0 = ChaPath("${%CHA_LIBEXEC_DIR}/gmi2html")
   let geminiPath = geminiPath0.unquote().get
diff --git a/src/layout/engine.nim b/src/layout/engine.nim
index 637285a6..cc80d1d8 100644
--- a/src/layout/engine.nim
+++ b/src/layout/engine.nim
@@ -8,6 +8,7 @@ import css/values
 import display/winattrs
 import layout/box
 import layout/layoutunit
+import utils/luwrap
 import utils/strwidth
 import utils/twtstr
 
diff --git a/src/local/container.nim b/src/local/container.nim
index 09ec5a37..8a0ae37e 100644
--- a/src/local/container.nim
+++ b/src/local/container.nim
@@ -23,6 +23,7 @@ import types/cell
 import types/color
 import types/cookie
 import types/url
+import utils/luwrap
 import utils/mimeguess
 import utils/strwidth
 import utils/twtstr
diff --git a/src/local/select.nim b/src/local/select.nim
index 95c89117..0be38104 100644
--- a/src/local/select.nim
+++ b/src/local/select.nim
@@ -1,10 +1,10 @@
-import unicode
+import std/unicode
 
 import js/regex
 import server/buffer
 import types/cell
+import utils/luwrap
 import utils/strwidth
-import utils/twtstr
 
 type
   SubmitSelect* = proc(selected: seq[int])
diff --git a/src/types/url.nim b/src/types/url.nim
index d5fbed9e..0f65e727 100644
--- a/src/types/url.nim
+++ b/src/types/url.nim
@@ -12,6 +12,7 @@ import js/error
 import js/javascript
 import lib/punycode
 import types/blob
+import utils/luwrap
 import utils/twtstr
 
 type
diff --git a/src/utils/luwrap.nim b/src/utils/luwrap.nim
new file mode 100644
index 00000000..41a58701
--- /dev/null
+++ b/src/utils/luwrap.nim
@@ -0,0 +1,46 @@
+import std/strutils
+import std/unicode
+
+import bindings/libunicode
+import utils/charcategory
+
+proc passRealloc(opaque: pointer, p: pointer, size: csize_t): pointer
+    {.cdecl.} =
+  return realloc(p, size)
+
+proc mnormalize*(rs: var seq[Rune], form = UNICODE_NFC) = {.cast(noSideEffect).}:
+  if rs.len == 0: return
+  var outbuf: ptr uint32
+  let p = cast[ptr uint32](unsafeAddr rs[0])
+  let out_len = unicode_normalize(addr outbuf, p, cint(rs.len), form, nil,
+    passRealloc)
+  if out_len < 0:
+    raise newException(Defect, "Unicode normalization failed")
+  if out_len == 0:
+    return
+  rs = cast[seq[Rune]](newSeqUninitialized[uint32](out_len))
+  copyMem(addr rs[0], outbuf, out_len * sizeof(uint32))
+  dealloc(outbuf)
+
+#TODO maybe a utf8 normalization procedure?
+proc mnormalize*(s: var string) =
+  if NonAscii notin s:
+    return # no need to normalize ascii
+  var rs = s.toRunes()
+  rs.mnormalize()
+  s = $rs
+
+func normalize*(rs: seq[Rune], form = UNICODE_NFC): seq[Rune] =
+  {.cast(noSideEffect).}:
+    if rs.len == 0: return
+    var outbuf: ptr uint32
+    let p = cast[ptr uint32](unsafeAddr rs[0])
+    let out_len = unicode_normalize(addr outbuf, p, cint(rs.len), form,
+      nil, passRealloc)
+    if out_len < 0:
+      raise newException(Defect, "Unicode normalization failed")
+    if out_len == 0:
+      return
+    result = cast[seq[Rune]](newSeqUninitialized[uint32](out_len))
+    copyMem(addr result[0], outbuf, out_len * sizeof(uint32))
+    dealloc(outbuf)
diff --git a/src/utils/twtstr.nim b/src/utils/twtstr.nim
index b235651f..da9af19a 100644
--- a/src/utils/twtstr.nim
+++ b/src/utils/twtstr.nim
@@ -7,7 +7,6 @@ import strutils
 import tables
 import unicode
 
-import bindings/libunicode
 import types/opt
 import utils/charcategory
 import utils/map
@@ -576,42 +575,6 @@ proc passRealloc*(opaque: pointer, p: pointer, size: csize_t): pointer
     {.cdecl.} =
   return realloc(p, size)
 
-proc mnormalize*(rs: var seq[Rune], form = UNICODE_NFC) = {.cast(noSideEffect).}:
-  if rs.len == 0: return
-  var outbuf: ptr uint32
-  let p = cast[ptr uint32](unsafeAddr rs[0])
-  let out_len = unicode_normalize(addr outbuf, p, cint(rs.len), form, nil,
-    passRealloc)
-  if out_len < 0:
-    raise newException(Defect, "Unicode normalization failed")
-  if out_len == 0:
-    return
-  rs = cast[seq[Rune]](newSeqUninitialized[uint32](out_len))
-  copyMem(addr rs[0], outbuf, out_len * sizeof(uint32))
-  dealloc(outbuf)
-
-#TODO maybe a utf8 normalization procedure?
-proc mnormalize*(s: var string) =
-  if s.isAscii():
-    return # no need to normalize ascii
-  var rs = s.toRunes()
-  rs.mnormalize()
-  s = $rs
-
-func normalize*(rs: seq[Rune], form = UNICODE_NFC): seq[Rune] = {.cast(noSideEffect).}:
-  if rs.len == 0: return
-  var outbuf: ptr uint32
-  let out_len = unicode_normalize(addr outbuf,
-                                  cast[ptr uint32](unsafeAddr rs[0]),
-                                  cint(rs.len), form, nil, passRealloc)
-  if out_len < 0:
-    raise newException(Defect, "Unicode normalization failed")
-  if out_len == 0:
-    return
-  result = cast[seq[Rune]](newSeqUninitialized[uint32](out_len))
-  copyMem(addr result[0], outbuf, out_len * sizeof(uint32))
-  dealloc(outbuf)
-
 # https://www.w3.org/TR/xml/#NT-Name
 const NameStartCharRanges = [
   (0xC0, 0xD6),