From f4aa64e934f6ddd73a91fdeb6fec492811f09207 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 16 Jan 2026 17:47:40 +0000 Subject: [PATCH] Add `html.escape_string` --- core/html/escape.odin | 59 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 core/html/escape.odin diff --git a/core/html/escape.odin b/core/html/escape.odin new file mode 100644 index 000000000..dfdcc6f92 --- /dev/null +++ b/core/html/escape.odin @@ -0,0 +1,59 @@ +package html + +// escape_string escapes special characters like '&' to become '&'. +// It escapes only 5 different characters: & ' < > and ". +@(require_results) +escape_string :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (output: string, was_allocation: bool) { + /* + & -> & + ' -> ' // ' is shorter than ' (NOTE: ' was not available until HTML 5) + < -> < + > -> > + " -> " // " is shorter than " + */ + + b := transmute([]byte)s + + extra_bytes_needed := 0 + + for c in b { + switch c { + case '&': extra_bytes_needed += 4 + case '\'': extra_bytes_needed += 4 + case '<': extra_bytes_needed += 3 + case '>': extra_bytes_needed += 3 + case '"': extra_bytes_needed += 4 + } + } + + if extra_bytes_needed == 0 { + return s, false + } + + t, err := make([]byte, len(s) + extra_bytes_needed, allocator, loc) + if err != nil { + return + } + was_allocation = true + + w := 0 + for c in b { + s := "" + switch c { + case '&': s = "&" + case '\'': s = "'" + case '<': s = "<" + case '>': s = ">" + case '"': s = """ + } + if s != "" { + copy(t[w:], s) + w += len(s) + } else { + t[w] = c + w += 1 + } + } + output = string(t[0:w]) + return +} \ No newline at end of file