/* Copyright 2011 * Kaz Kylheku * Vancouver, Canada * All rights reserved. * * BSD License: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include #include #include #include #include "config.h" #include "lib.h" #include "hash.h" #include "unwind.h" #include "filter.h" static val make_trie(void) { return make_hash(nil, nil); } static val trie_add(val trie, val key, val value) { val node, i, len = length_str(key); for (node = trie, i = zero; lt(i, len); i = plus(i, one)) { val ch = chr_str(key, i); val newnode_p; val *loc = gethash_l(node, ch, &newnode_p); if (newnode_p) *loc = make_hash(nil, nil); node = *loc; } set_hash_userdata(node, value); return node; } /* * Reduce the storage requirement for a the trie, by applying * these rules: * * 1. All leaf-level nodes (i.e. empty hash tables) are replaced by * just the node value (hash user data). * 2. All hash tables with a single transition (i.e. hash tables * containing one element) and which have no node value * are replaced by a cons cell, whose CAR is the transition * character, and whose CDR is the transition. */ static void trie_compress(val *ptrie) { val trie = *ptrie; if (hashp(trie)) { val count = hash_count(trie); val value = get_hash_userdata(trie); if (zerop(count)) { *ptrie = value; } else if (eq(count, one) && nullp(value)) { val iter = hash_begin(trie); val cell = hash_next(&iter); *ptrie = cons(car(cell), cdr(cell)); trie_compress(cdr_l(*ptrie)); } else { val cell, iter = hash_begin(trie); for (cell = hash_next(&iter); iter; cell = hash_next(&iter)) trie_compress(cdr_l(cell)); } } else if (consp(trie)) { trie_compress(cdr_l(trie)); } } val trie_lookup_begin(val trie) { return trie; } val trie_value_at(val node) { if (hashp(node)) return get_hash_userdata(node); if (consp(node)) return nil; if (functionp(node)) return nil; return node; } val trie_lookup_feed_char(val node, val ch) { if (hashp(node)) return gethash(node, ch); if (functionp(node)) return funcall1(node, ch); if (consp(node) && eq(ch,car(node))) return cdr(node); return nil; } val get_filter_trie(val sym) { return gethash(filters, sym); } struct filter_pair { wchar_t *key, *value; }; static val build_filter(struct filter_pair *pair, val compress_p) { int i; val trie = make_trie(); for (i = 0; pair[i].key; i++) trie_add(trie, static_str(pair[i].key), static_str(pair[i].value)); if (compress_p) trie_compress(&trie); return trie; } static val build_filter_from_list(val list) { val trie = make_trie(); val iter; for (iter = list; iter; iter = cdr(iter)) { val pair = car(iter); trie_add(trie, first(pair), second(pair)); } trie_compress(&trie); return trie; } static val trie_filter_string(val filter, val str) { val len = length_str(str); val i; val out = string(L""); for (i = zero; lt(i, len); ) { val node = trie_lookup_begin(filter); val match = nil; val subst; val j; for (j = i; lt(j, len); j = plus(j, one)) { val ch = chr_str(str, j); val nnode = trie_lookup_feed_char(node, ch); val nsubst; if (!nnode) break; if ((nsubst = trie_value_at(nnode))) { match = j; subst = nsubst; } node = nnode; } if (match) { string_extend(out, subst); i = plus(match, one); } else { string_extend(out, chr_str(str, i)); i = plus(i, one); } } return out; } val filter_string(val filter, val str) { val type = typeof(filter); if (eq(type, null)) return str; if (eq(type, hash_s) || eq(type, cons_s)) return trie_filter_string(filter, str); else if (type == fun_s) return funcall1(filter, str); return str; uw_throwf(error_s, lit("filter_string: invalid filter ~a"), filter, nao); } val register_filter(val sym, val table) { return sethash(filters, sym, build_filter_from_list(table)); } static struct filter_pair to_html_table[] = { { L"<", L"<" }, { L">", L">" }, { L"&", L"&" }, { L"\"", L""" }, { 0, 0 } }; static struct filter_pair from_html_table[] = { { L""", L"\"" }, { L"&", L"&" }, { L"'", L"'" }, { L"<", L"<" }, { L">", L">" }, { L" ", L"\x00A0" }, { L"¡", L"\x00A1" }, { L"¢", L"\x00A2" }, { L"£", L"\x00A3" }, { L"¤", L"\x00A4" }, { L"¥", L"\x00A5" }, { L"¦", L"\x00A6" }, { L"§", L"\x00A7" }, { L"¨", L"\x00A8" }, { L"©", L"\x00A9" }, { L"ª", L"\x00AA" }, { L"«", L"\x00AB" }, { L"¬", L"\x00AC" }, { L"­", L"\x00AD" }, { L"®", L"\x00AE" }, { L"¯", L"\x00AF" }, { L"°", L"\x00B0" }, { L"±", L"\x00B1" }, { L"²", L"\x00B2" }, { L"³", L"\x00B3" }, { L"´", L"\x00B4" }, { L"µ", L"\x00B5" }, { L"¶", L"\x00B6" }, { L"·", L"\x00B7" }, { L"¸", L"\x00B8" }, { L"¹", L"\x00B9" }, { L"º", L"\x00BA" }, { L"»", L"\x00BB" }, { L"¼", L"\x00BC" }, { L"½", L"\x00BD" }, { L"¾", L"\x00BE" }, { L"¿", L"\x00BF" }, { L"À", L"\x00C0" }, { L"Á", L"\x00C1" }, { L"Â", L"\x00C2" }, { L"Ã", L"\x00C3" }, { L"Ä", L"\x00C4" }, { L"Å", L"\x00C5" }, { L"Æ", L"\x00C6" }, { L"Ç", L"\x00C7" }, { L"È", L"\x00C8" }, { L"É", L"\x00C9" }, { L"Ê", L"\x00CA" }, { L"Ë", L"\x00CB" }, { L"Ì", L"\x00CC" }, { L"Í", L"\x00CD" }, { L"Î", L"\x00CE" }, { L"Ï", L"\x00CF" }, { L"Ð", L"\x00D0" }, { L"Ñ", L"\x00D1" }, { L"Ò", L"\x00D2" }, { L"Ó", L"\x00D3" }, { L"Ô", L"\x00D4" }, { L"Õ", L"\x00D5" }, { L"Ö", L"\x00D6" }, { L"×", L"\x00D7" }, { L"Ø", L"\x00D8" }, { L"Ù", L"\x00D9" }, { L"Ú", L"\x00DA" }, { L"Û", L"\x00DB" }, { L"Ü", L"\x00DC" }, { L"Ý", L"\x00DD" }, { L"Þ", L"\x00DE" }, { L"ß", L"\x00DF" }, { L"à", L"\x00E0" }, { L"á", L"\x00E1" }, { L"â", L"\x00E2" }, { L"ã", L"\x00E3" }, { L"ä", L"\x00E4" }, { L"å", L"\x00E5" }, { L"æ", L"\x00E6" }, { L"ç", L"\x00E7" }, { L"è", L"\x00E8" }, { L"é", L"\x00E9" }, { L"ê", L"\x00EA" }, { L"ë", L"\x00EB" }, { L"ì", L"\x00EC" }, { L"í", L"\x00ED" }, { L"î", L"\x00EE" }, { L"ï", L"\x00EF" }, { L"ð", L"\x00F0" }, { L"ñ", L"\x00F1" }, { L"ò", L"\x00F2" }, { L"ó", L"\x00F3" }, { L"ô", L"\x00F4" }, { L"õ", L"\x00F5" }, { L"ö", L"\x00F6" }, { L"÷", L"\x00F7" }, { L"ø", L"\x00F8" }, { L"ù", L"\x00F9" }, { L"ú", L"\x00FA" }, { L"û", L"\x00FB" }, { L"ü", L"\x00FC" }, { L"ý", L"\x00FD" }, { L"þ", L"\x00FE" }, { L"ÿ", L"\x00FF" }, { L"Œ", L"\x0152" }, { L"œ", L"\x0153" }, { L"Š", L"\x0160" }, { L"š", L"\x0161" }, { L"Ÿ", L"\x0178" }, { L"ƒ", L"\x0192" }, { L"ˆ", L"\x02C6" }, { L"˜", L"\x02DC" }, { L"Α", L"\x0391" }, { L"Β", L"\x0392" }, { L"Γ", L"\x0393" }, { L"Δ", L"\x0394" }, { L"Ε", L"\x0395" }, { L"Ζ", L"\x0396" }, { L"Η", L"\x0397" }, { L"Θ", L"\x0398" }, { L"Ι", L"\x0399" }, { L"Κ", L"\x039A" }, { L"Λ", L"\x039B" }, { L"Μ", L"\x039C" }, { L"Ν", L"\x039D" }, { L"Ξ", L"\x039E" }, { L"Ο", L"\x039F" }, { L"Π", L"\x03A0" }, { L"Ρ", L"\x03A1" }, { L"Σ", L"\x03A3" }, { L"Τ", L"\x03A4" }, { L"Υ", L"\x03A5" }, { L"Φ", L"\x03A6" }, { L"Χ", L"\x03A7" }, { L"Ψ", L"\x03A8" }, { L"Ω", L"\x03A9" }, { L"α", L"\x03B1" }, { L"β", L"\x03B2" }, { L"γ", L"\x03B3" }, { L"δ", L"\x03B4" }, { L"ε", L"\x03B5" }, { L"ζ", L"\x03B6" }, { L"η", L"\x03B7" }, { L"θ", L"\x03B8" }, { L"ι", L"\x03B9" }, { L"κ", L"\x03BA" }, { L"λ", L"\x03BB" }, { L"μ", L"\x03BC" }, { L"ν", L"\x03BD" }, { L"ξ", L"\x03BE" }, { L"ο", L"\x03BF" }, { L"π", L"\x03C0" }, { L"ρ", L"\x03C1" }, { L"ς", L"\x03C2" }, { L"σ", L"\x03C3" }, { L"τ", L"\x03C4" }, { L"υ", L"\x03C5" }, { L"φ", L"\x03C6" }, { L"χ", L"\x03C7" }, { L"ψ", L"\x03C8" }, { L"ω", L"\x03C9" }, { L"ϑ", L"\x03D1" }, { L"ϒ", L"\x03D2" }, { L"ϖ", L"\x03D6" }, { L" ", L"\x2002" }, { L" ", L"\x2003" }, { L" ", L"\x2009" }, { L"‌", L"\x200C" }, { L"‍", L"\x200D" }, { L"‎", L"\x200E" }, { L"‏", L"\x200F" }, { L"–", L"\x2013" }, { L"—", L"\x2014" }, { L"‘", L"\x2018" }, { L"’", L"\x2019" }, { L"‚", L"\x201A" }, { L"“", L"\x201C" }, { L"”", L"\x201D" }, { L"„", L"\x201E" }, { L"†", L"\x2020" }, { L"‡", L"\x2021" }, { L"•", L"\x2022" }, { L"…", L"\x2026" }, { L"‰", L"\x2030" }, { L"′", L"\x2032" }, { L"″", L"\x2033" }, { L"‹", L"\x2039" }, { L"›", L"\x203A" }, { L"‾", L"\x203E" }, { L"⁄", L"\x2044" }, { L"€", L"\x20AC" }, { L"ℑ", L"\x2111" }, { L"℘", L"\x2118" }, { L"ℜ", L"\x211C" }, { L"™", L"\x2122" }, { L"ℵ", L"\x2135" }, { L"←", L"\x2190" }, { L"↑", L"\x2191" }, { L"→", L"\x2192" }, { L"↓", L"\x2193" }, { L"↔", L"\x2194" }, { L"↵", L"\x21B5" }, { L"⇐", L"\x21D0" }, { L"⇑", L"\x21D1" }, { L"⇒", L"\x21D2" }, { L"⇓", L"\x21D3" }, { L"⇔", L"\x21D4" }, { L"∀", L"\x2200" }, { L"∂", L"\x2202" }, { L"∃", L"\x2203" }, { L"∅", L"\x2205" }, { L"∇", L"\x2207" }, { L"∈", L"\x2208" }, { L"∉", L"\x2209" }, { L"∋", L"\x220B" }, { L"∏", L"\x220F" }, { L"∑", L"\x2211" }, { L"−", L"\x2212" }, { L"∗", L"\x2217" }, { L"√", L"\x221A" }, { L"∝", L"\x221D" }, { L"∞", L"\x221E" }, { L"∠", L"\x2220" }, { L"∧", L"\x2227" }, { L"∨", L"\x2228" }, { L"∩", L"\x2229" }, { L"∪", L"\x222A" }, { L"∫", L"\x222B" }, { L"∴", L"\x2234" }, { L"∼", L"\x223C" }, { L"≅", L"\x2245" }, { L"≈", L"\x2248" }, { L"≠", L"\x2260" }, { L"≡", L"\x2261" }, { L"≤", L"\x2264" }, { L"≥", L"\x2265" }, { L"⊂", L"\x2282" }, { L"⊃", L"\x2283" }, { L"⊄", L"\x2284" }, { L"⊆", L"\x2286" }, { L"⊇", L"\x2287" }, { L"⊕", L"\x2295" }, { L"⊗", L"\x2297" }, { L"⊥", L"\x22A5" }, { L"⋅", L"\x22C5" }, { L"⌈", L"\x2308" }, { L"⌉", L"\x2309" }, { L"⌊", L"\x230A" }, { L"⌋", L"\x230B" }, { L"⟨", L"\x2329" }, { L"⟩", L"\x232A" }, { L"◊", L"\x25CA" }, { L"♠", L"\x2660" }, { L"♣", L"\x2663" }, { L"♥", L"\x2665" }, { L"♦", L"\x2666" }, { 0, 0 } }; static val html_hex_continue(val hexlist, val ch) { static wchar_t *hexdigs = L"0123456789ABCDEF"; if (iswxdigit(c_chr(ch))) { return func_f1(cons(ch, hexlist), html_hex_continue); } if (eq(ch, chr(';'))) { wchar_t out[2] = { 0 }; val iter; if (nullp(hexlist)) return nil; for (iter = nreverse(hexlist); iter; iter = cdr(iter)) { val hexch = car(iter); int val = wcschr(hexdigs, towupper(c_chr(hexch))) - hexdigs; out[0] <<= 4; out[0] |= val; } return string(out); } else { return nil; } } static val html_dec_continue(val declist, val ch) { if (iswdigit(c_chr(ch))) { return func_f1(cons(ch, declist), html_dec_continue); } if (eq(ch, chr(';'))) { wchar_t out[2] = { 0 }; val iter; for (iter = nreverse(declist); iter; iter = cdr(iter)) { val decch = car(iter); int val = c_chr(decch) - '0'; out[0] *= 10; out[0] += val; } return string(out); } else { return nil; } } static val html_numeric_handler(val ch) { if (eq(ch, chr('x'))) return func_f1(nil, html_hex_continue); if (!iswdigit(c_chr(ch))) return nil; return func_f1(cons(ch, nil), html_dec_continue); } val filters; val filter_k, to_html_k, from_html_k; void filter_init(void) { protect(&filters, (val *) 0); filters = make_hash(nil, nil); filter_k = intern(lit("filter"), keyword_package); to_html_k = intern(lit("to_html"), keyword_package); from_html_k = intern(lit("from_html"), keyword_package); sethash(filters, to_html_k, build_filter(to_html_table, t)); { val trie = build_filter(from_html_table, nil); trie_add(trie, lit("&#"), func_n1(html_numeric_handler)); trie_compress(&trie); sethash(filters, from_html_k, trie); } }