diff options
author | bptato <nincsnevem662@gmail.com> | 2023-12-13 15:00:34 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2023-12-13 15:00:34 +0100 |
commit | 53bc47531543fe55997f4c6875fa03745a17e754 (patch) | |
tree | a7d3b3de2f68580a79415818bad84bf607e58c14 | |
parent | 4818cb28debf4601213707f6c1b9b22348b51fbc (diff) | |
download | chawan-53bc47531543fe55997f4c6875fa03745a17e754.tar.gz |
Various fixes
* Makefile: fix parallel build, add new binaries to install target * twtstr: split out libunicode-related stuff to luwrap * config: quote default gopher2html URL env var for unquote * adapter/: get rid of types/url dependency, use CURL url in all cases
-rw-r--r-- | Makefile | 44 | ||||
-rw-r--r-- | adapter/protocol/curlwrap.nim | 7 | ||||
-rw-r--r-- | adapter/protocol/file.nim | 9 | ||||
-rw-r--r-- | adapter/protocol/ftp.nim | 28 | ||||
-rw-r--r-- | adapter/protocol/gopher.nim | 51 | ||||
-rw-r--r-- | adapter/protocol/http.nim | 4 | ||||
-rw-r--r-- | res/urimethodmap | 12 | ||||
-rw-r--r-- | src/config/config.nim | 2 | ||||
-rw-r--r-- | src/layout/engine.nim | 1 | ||||
-rw-r--r-- | src/local/container.nim | 1 | ||||
-rw-r--r-- | src/local/select.nim | 4 | ||||
-rw-r--r-- | src/types/url.nim | 1 | ||||
-rw-r--r-- | src/utils/luwrap.nim | 46 | ||||
-rw-r--r-- | src/utils/twtstr.nim | 37 |
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), |