size: 2 KiB

1const std = @import("std");
2const t = std.testing;
3const Io = std.Io;
4
5pub fn escapeAlloc(arena: std.mem.Allocator, text: []const u8) ![]const u8 {
6 var w = std.Io.Writer.Allocating.init(arena);
7 try escape(&w.writer, text);
8 return w.written();
9}
10
11pub fn escape(out: *std.Io.Writer, text: []const u8) !void {
12 for (text) |char| {
13 switch (char) {
14 '&' => try out.writeAll("&"),
15 '"' => try out.writeAll("""),
16 '\'' => try out.writeAll("'"),
17 '<' => try out.writeAll("&lt;"),
18 '>' => try out.writeAll("&gt;"),
19 else => try out.writeByte(char),
20 }
21 }
22}
23
24test "escape" {
25 var buf: [4096]u8 = undefined;
26 inline for (.{
27 .{
28 "<script>alert(\"jello\")</script>",
29 "&lt;script&gt;alert(&quot;jello&quot;)&lt;/script&gt;",
30 },
31 .{
32 "&\"'<>",
33 "&amp;&quot;&#39;&lt;&gt;",
34 },
35 }) |case| {
36 const input = case[0];
37 const expected = case[1];
38 var w = std.Io.Writer.fixed(&buf);
39 try escape(&w, input);
40 try t.expectEqualStrings(expected, w.buffered());
41 }
42}
43
44pub fn percentEncode(out: *std.Io.Writer, text: []const u8) !void {
45 for (text) |char| {
46 switch (char) {
47 ':' => try out.writeAll("%3A"),
48 '/' => try out.writeAll("%2F"),
49 '?' => try out.writeAll("%3F"),
50 '#' => try out.writeAll("%23"),
51 '[' => try out.writeAll("%5B"),
52 ']' => try out.writeAll("%5D"),
53 '@' => try out.writeAll("%40"),
54 '!' => try out.writeAll("%21"),
55 '$' => try out.writeAll("%24"),
56 '&' => try out.writeAll("%26"),
57 '\'' => try out.writeAll("%27"),
58 '(' => try out.writeAll("%28"),
59 ')' => try out.writeAll("%29"),
60 '*' => try out.writeAll("%2A"),
61 '+' => try out.writeAll("%2B"),
62 ',' => try out.writeAll("%2C"),
63 ';' => try out.writeAll("%3B"),
64 '=' => try out.writeAll("%3D"),
65 '%' => try out.writeAll("%25"),
66 ' ' => try out.writeAll("%20"),
67 else => try out.writeByte(char),
68 }
69 }
70}
71
72pub fn percentEncodeAlloc(arena: std.mem.Allocator, text: []const u8) ![]const u8 {
73 var w = std.Io.Writer.Allocating.init(arena);
74 try percentEncode(&w.writer, text);
75 return w.written();
76}
77
78pub fn tag(w: *Io.Writer, tag_name: []const u8, text: []const u8) !void {
79 try w.print("<{s}>", .{tag_name});
80 try escape(w, text);
81 try w.print("</{s}>\n", .{tag_name});
82}