haml 5.2.2 → 6.0.0.beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -0
- data/.github/workflows/test.yml +13 -9
- data/.gitignore +16 -16
- data/CHANGELOG.md +13 -3
- data/Gemfile +18 -11
- data/MIT-LICENSE +1 -1
- data/README.md +13 -19
- data/Rakefile +95 -93
- data/bin/bench +66 -0
- data/bin/console +11 -0
- data/bin/ruby +3 -0
- data/bin/setup +7 -0
- data/bin/stackprof +27 -0
- data/bin/test +24 -0
- data/exe/haml +6 -0
- data/ext/haml/extconf.rb +10 -0
- data/ext/haml/haml.c +537 -0
- data/ext/haml/hescape.c +108 -0
- data/ext/haml/hescape.h +20 -0
- data/haml.gemspec +39 -37
- data/lib/haml/ambles.rb +20 -0
- data/lib/haml/attribute_builder.rb +135 -179
- data/lib/haml/attribute_compiler.rb +85 -194
- data/lib/haml/attribute_parser.rb +86 -126
- data/lib/haml/cli.rb +154 -0
- data/lib/haml/compiler/children_compiler.rb +126 -0
- data/lib/haml/compiler/comment_compiler.rb +39 -0
- data/lib/haml/compiler/doctype_compiler.rb +46 -0
- data/lib/haml/compiler/script_compiler.rb +116 -0
- data/lib/haml/compiler/silent_script_compiler.rb +24 -0
- data/lib/haml/compiler/tag_compiler.rb +76 -0
- data/lib/haml/compiler.rb +63 -296
- data/lib/haml/dynamic_merger.rb +67 -0
- data/lib/haml/engine.rb +42 -227
- data/lib/haml/error.rb +3 -52
- data/lib/haml/escapable.rb +6 -70
- data/lib/haml/filters/base.rb +12 -0
- data/lib/haml/filters/cdata.rb +20 -0
- data/lib/haml/filters/coffee.rb +17 -0
- data/lib/haml/filters/css.rb +33 -0
- data/lib/haml/filters/erb.rb +10 -0
- data/lib/haml/filters/escaped.rb +22 -0
- data/lib/haml/filters/javascript.rb +33 -0
- data/lib/haml/filters/less.rb +20 -0
- data/lib/haml/filters/markdown.rb +11 -0
- data/lib/haml/filters/plain.rb +29 -0
- data/lib/haml/filters/preserve.rb +22 -0
- data/lib/haml/filters/ruby.rb +10 -0
- data/lib/haml/filters/sass.rb +15 -0
- data/lib/haml/filters/scss.rb +15 -0
- data/lib/haml/filters/text_base.rb +25 -0
- data/lib/haml/filters/tilt_base.rb +49 -0
- data/lib/haml/filters.rb +54 -378
- data/lib/haml/force_escapable.rb +29 -0
- data/lib/haml/haml_error.rb +66 -0
- data/lib/haml/helpers.rb +3 -697
- data/lib/haml/html.rb +22 -0
- data/lib/haml/identity.rb +13 -0
- data/lib/haml/object_ref.rb +30 -0
- data/lib/haml/parser.rb +179 -49
- data/lib/haml/rails_helpers.rb +51 -0
- data/lib/haml/rails_template.rb +55 -0
- data/lib/haml/railtie.rb +7 -45
- data/lib/haml/ruby_expression.rb +32 -0
- data/lib/haml/string_splitter.rb +20 -0
- data/lib/haml/template.rb +15 -34
- data/lib/haml/temple_line_counter.rb +2 -1
- data/lib/haml/util.rb +17 -15
- data/lib/haml/version.rb +1 -2
- data/lib/haml.rb +8 -20
- metadata +211 -57
- data/.gitmodules +0 -3
- data/.yardopts +0 -22
- data/TODO +0 -24
- data/benchmark.rb +0 -70
- data/bin/haml +0 -9
- data/lib/haml/.gitattributes +0 -1
- data/lib/haml/buffer.rb +0 -182
- data/lib/haml/exec.rb +0 -347
- data/lib/haml/generator.rb +0 -42
- data/lib/haml/helpers/action_view_extensions.rb +0 -60
- data/lib/haml/helpers/action_view_mods.rb +0 -132
- data/lib/haml/helpers/action_view_xss_mods.rb +0 -60
- data/lib/haml/helpers/safe_erubi_template.rb +0 -20
- data/lib/haml/helpers/safe_erubis_template.rb +0 -33
- data/lib/haml/helpers/xss_mods.rb +0 -114
- data/lib/haml/options.rb +0 -273
- data/lib/haml/plugin.rb +0 -54
- data/lib/haml/sass_rails_filter.rb +0 -47
- data/lib/haml/template/options.rb +0 -27
- data/lib/haml/temple_engine.rb +0 -124
- data/yard/default/.gitignore +0 -1
- data/yard/default/fulldoc/html/css/common.sass +0 -15
- data/yard/default/layout/html/footer.erb +0 -12
    
        data/ext/haml/haml.c
    ADDED
    
    | @@ -0,0 +1,537 @@ | |
| 1 | 
            +
            #include <ruby.h>
         | 
| 2 | 
            +
            #include <ruby/encoding.h>
         | 
| 3 | 
            +
            #ifndef TRUFFLERUBY
         | 
| 4 | 
            +
            #include "hescape.h"
         | 
| 5 | 
            +
            #include "string.h"
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            VALUE mAttributeBuilder, mObjectRef;
         | 
| 8 | 
            +
            static ID id_flatten, id_keys, id_parse, id_prepend, id_tr, id_uniq_bang;
         | 
| 9 | 
            +
            static ID id_xhtml;
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            static VALUE str_aria, str_data, str_equal, str_hyphen, str_space, str_underscore;
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            static void
         | 
| 14 | 
            +
            delete_falsey_values(VALUE values)
         | 
| 15 | 
            +
            {
         | 
| 16 | 
            +
              VALUE value;
         | 
| 17 | 
            +
              long i;
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              for (i = RARRAY_LEN(values) - 1; 0 <= i; i--) {
         | 
| 20 | 
            +
                value = rb_ary_entry(values, i);
         | 
| 21 | 
            +
                if (!RTEST(value)) {
         | 
| 22 | 
            +
                  rb_ary_delete_at(values, i);
         | 
| 23 | 
            +
                }
         | 
| 24 | 
            +
              }
         | 
| 25 | 
            +
            }
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            static int
         | 
| 28 | 
            +
            str_eq(VALUE str, const char *cstr, long n)
         | 
| 29 | 
            +
            {
         | 
| 30 | 
            +
              return RSTRING_LEN(str) == n && memcmp(RSTRING_PTR(str), cstr, n) == 0;
         | 
| 31 | 
            +
            }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            static VALUE
         | 
| 34 | 
            +
            to_s(VALUE value)
         | 
| 35 | 
            +
            {
         | 
| 36 | 
            +
              return rb_convert_type(value, T_STRING, "String", "to_s");
         | 
| 37 | 
            +
            }
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            static VALUE
         | 
| 40 | 
            +
            hyphenate(VALUE str)
         | 
| 41 | 
            +
            {
         | 
| 42 | 
            +
              long i;
         | 
| 43 | 
            +
             | 
| 44 | 
            +
              if (OBJ_FROZEN(str)) str = rb_str_dup(str);
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              for (i = 0; i < RSTRING_LEN(str); i++) {
         | 
| 47 | 
            +
                if (RSTRING_PTR(str)[i] == '_') {
         | 
| 48 | 
            +
                  rb_str_update(str, i, 1, str_hyphen);
         | 
| 49 | 
            +
                }
         | 
| 50 | 
            +
              }
         | 
| 51 | 
            +
              return str;
         | 
| 52 | 
            +
            }
         | 
| 53 | 
            +
             | 
| 54 | 
            +
            static VALUE
         | 
| 55 | 
            +
            escape_html(VALUE str)
         | 
| 56 | 
            +
            {
         | 
| 57 | 
            +
              char *buf;
         | 
| 58 | 
            +
              unsigned int size;
         | 
| 59 | 
            +
              Check_Type(str, T_STRING);
         | 
| 60 | 
            +
             | 
| 61 | 
            +
              size = hesc_escape_html(&buf, RSTRING_PTR(str), RSTRING_LEN(str));
         | 
| 62 | 
            +
              if (size > RSTRING_LEN(str)) {
         | 
| 63 | 
            +
                str = rb_enc_str_new(buf, size, rb_utf8_encoding());
         | 
| 64 | 
            +
                free((void *)buf);
         | 
| 65 | 
            +
              }
         | 
| 66 | 
            +
             | 
| 67 | 
            +
              return str;
         | 
| 68 | 
            +
            }
         | 
| 69 | 
            +
             | 
| 70 | 
            +
            static VALUE
         | 
| 71 | 
            +
            escape_attribute(VALUE escape_attrs, VALUE str)
         | 
| 72 | 
            +
            {
         | 
| 73 | 
            +
              if (RTEST(escape_attrs)) {
         | 
| 74 | 
            +
                return escape_html(str);
         | 
| 75 | 
            +
              } else {
         | 
| 76 | 
            +
                return str;
         | 
| 77 | 
            +
              }
         | 
| 78 | 
            +
            }
         | 
| 79 | 
            +
             | 
| 80 | 
            +
            static VALUE
         | 
| 81 | 
            +
            rb_escape_html(RB_UNUSED_VAR(VALUE self), VALUE value)
         | 
| 82 | 
            +
            {
         | 
| 83 | 
            +
              return escape_html(to_s(value));
         | 
| 84 | 
            +
            }
         | 
| 85 | 
            +
             | 
| 86 | 
            +
            static VALUE
         | 
| 87 | 
            +
            haml_build_id(VALUE escape_attrs, VALUE values)
         | 
| 88 | 
            +
            {
         | 
| 89 | 
            +
              VALUE attr_value;
         | 
| 90 | 
            +
             | 
| 91 | 
            +
              values = rb_funcall(values, id_flatten, 0);
         | 
| 92 | 
            +
              delete_falsey_values(values);
         | 
| 93 | 
            +
             | 
| 94 | 
            +
              attr_value = rb_ary_join(values, str_underscore);
         | 
| 95 | 
            +
              return escape_attribute(escape_attrs, attr_value);
         | 
| 96 | 
            +
            }
         | 
| 97 | 
            +
             | 
| 98 | 
            +
            static VALUE
         | 
| 99 | 
            +
            haml_build_single_class(VALUE escape_attrs, VALUE value)
         | 
| 100 | 
            +
            {
         | 
| 101 | 
            +
              switch (TYPE(value)) {
         | 
| 102 | 
            +
                case T_STRING:
         | 
| 103 | 
            +
                  break;
         | 
| 104 | 
            +
                case T_ARRAY:
         | 
| 105 | 
            +
                  value = rb_funcall(value, id_flatten, 0);
         | 
| 106 | 
            +
                  delete_falsey_values(value);
         | 
| 107 | 
            +
                  value = rb_ary_join(value, str_space);
         | 
| 108 | 
            +
                  break;
         | 
| 109 | 
            +
                default:
         | 
| 110 | 
            +
                  if (RTEST(value)) {
         | 
| 111 | 
            +
                    value = to_s(value);
         | 
| 112 | 
            +
                  } else {
         | 
| 113 | 
            +
                    return rb_str_new_cstr("");
         | 
| 114 | 
            +
                  }
         | 
| 115 | 
            +
                  break;
         | 
| 116 | 
            +
              }
         | 
| 117 | 
            +
              return escape_attribute(escape_attrs, value);
         | 
| 118 | 
            +
            }
         | 
| 119 | 
            +
             | 
| 120 | 
            +
            static VALUE
         | 
| 121 | 
            +
            haml_build_multi_class(VALUE escape_attrs, VALUE values)
         | 
| 122 | 
            +
            {
         | 
| 123 | 
            +
              long i, j;
         | 
| 124 | 
            +
              VALUE value, buf;
         | 
| 125 | 
            +
             | 
| 126 | 
            +
              buf = rb_ary_new2(RARRAY_LEN(values));
         | 
| 127 | 
            +
             | 
| 128 | 
            +
              for (i = 0; i < RARRAY_LEN(values); i++) {
         | 
| 129 | 
            +
                value = rb_ary_entry(values, i);
         | 
| 130 | 
            +
                switch (TYPE(value)) {
         | 
| 131 | 
            +
                  case T_STRING:
         | 
| 132 | 
            +
                    rb_ary_concat(buf, rb_str_split(value, " "));
         | 
| 133 | 
            +
                    break;
         | 
| 134 | 
            +
                  case T_ARRAY:
         | 
| 135 | 
            +
                    value = rb_funcall(value, id_flatten, 0);
         | 
| 136 | 
            +
                    delete_falsey_values(value);
         | 
| 137 | 
            +
                    for (j = 0; j < RARRAY_LEN(value); j++) {
         | 
| 138 | 
            +
                      rb_ary_push(buf, to_s(rb_ary_entry(value, j)));
         | 
| 139 | 
            +
                    }
         | 
| 140 | 
            +
                    break;
         | 
| 141 | 
            +
                  default:
         | 
| 142 | 
            +
                    if (RTEST(value)) {
         | 
| 143 | 
            +
                      rb_ary_push(buf, to_s(value));
         | 
| 144 | 
            +
                    }
         | 
| 145 | 
            +
                    break;
         | 
| 146 | 
            +
                }
         | 
| 147 | 
            +
              }
         | 
| 148 | 
            +
             | 
| 149 | 
            +
              rb_funcall(buf, id_uniq_bang, 0);
         | 
| 150 | 
            +
             | 
| 151 | 
            +
              return escape_attribute(escape_attrs, rb_ary_join(buf, str_space));
         | 
| 152 | 
            +
            }
         | 
| 153 | 
            +
             | 
| 154 | 
            +
            static VALUE
         | 
| 155 | 
            +
            haml_build_class(VALUE escape_attrs, VALUE array)
         | 
| 156 | 
            +
            {
         | 
| 157 | 
            +
              if (RARRAY_LEN(array) == 1) {
         | 
| 158 | 
            +
                return haml_build_single_class(escape_attrs, rb_ary_entry(array, 0));
         | 
| 159 | 
            +
              } else {
         | 
| 160 | 
            +
                return haml_build_multi_class(escape_attrs, array);
         | 
| 161 | 
            +
              }
         | 
| 162 | 
            +
            }
         | 
| 163 | 
            +
             | 
| 164 | 
            +
            struct merge_data_attrs_var {
         | 
| 165 | 
            +
              VALUE merged;
         | 
| 166 | 
            +
              VALUE key_str;
         | 
| 167 | 
            +
            };
         | 
| 168 | 
            +
             | 
| 169 | 
            +
            static int
         | 
| 170 | 
            +
            merge_data_attrs_i(VALUE key, VALUE value, VALUE ptr)
         | 
| 171 | 
            +
            {
         | 
| 172 | 
            +
              struct merge_data_attrs_var *arg = (struct merge_data_attrs_var *)ptr;
         | 
| 173 | 
            +
              VALUE merged = arg->merged;
         | 
| 174 | 
            +
              VALUE key_str = arg->key_str;
         | 
| 175 | 
            +
             | 
| 176 | 
            +
              if (NIL_P(key)) {
         | 
| 177 | 
            +
                rb_hash_aset(merged, key_str, value);
         | 
| 178 | 
            +
              } else {
         | 
| 179 | 
            +
                key = rb_str_concat(rb_str_concat(rb_str_dup(key_str), rb_str_new_cstr("-")), to_s(key));
         | 
| 180 | 
            +
                rb_hash_aset(merged, key, value);
         | 
| 181 | 
            +
              }
         | 
| 182 | 
            +
              return ST_CONTINUE;
         | 
| 183 | 
            +
            }
         | 
| 184 | 
            +
             | 
| 185 | 
            +
            static VALUE
         | 
| 186 | 
            +
            merge_data_attrs(VALUE values, VALUE key_str)
         | 
| 187 | 
            +
            {
         | 
| 188 | 
            +
              long i;
         | 
| 189 | 
            +
              VALUE value, merged = rb_hash_new();
         | 
| 190 | 
            +
             | 
| 191 | 
            +
              for (i = 0; i < RARRAY_LEN(values); i++) {
         | 
| 192 | 
            +
                struct merge_data_attrs_var arg;
         | 
| 193 | 
            +
                arg.merged = merged;
         | 
| 194 | 
            +
                arg.key_str = key_str;
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                value = rb_ary_entry(values, i);
         | 
| 197 | 
            +
                switch (TYPE(value)) {
         | 
| 198 | 
            +
                  case T_HASH:
         | 
| 199 | 
            +
                    rb_hash_foreach(value, merge_data_attrs_i, (VALUE)&arg);
         | 
| 200 | 
            +
                    break;
         | 
| 201 | 
            +
                  default:
         | 
| 202 | 
            +
                    rb_hash_aset(merged, key_str, value);
         | 
| 203 | 
            +
                    break;
         | 
| 204 | 
            +
                }
         | 
| 205 | 
            +
              }
         | 
| 206 | 
            +
              return merged;
         | 
| 207 | 
            +
            }
         | 
| 208 | 
            +
             | 
| 209 | 
            +
            struct flatten_data_attrs_i2_arg {
         | 
| 210 | 
            +
              VALUE flattened;
         | 
| 211 | 
            +
              VALUE key;
         | 
| 212 | 
            +
            };
         | 
| 213 | 
            +
             | 
| 214 | 
            +
            static int
         | 
| 215 | 
            +
            flatten_data_attrs_i2(VALUE k, VALUE v, VALUE ptr)
         | 
| 216 | 
            +
            {
         | 
| 217 | 
            +
              VALUE key;
         | 
| 218 | 
            +
              struct flatten_data_attrs_i2_arg *arg = (struct flatten_data_attrs_i2_arg *)ptr;
         | 
| 219 | 
            +
             | 
| 220 | 
            +
              if (!RTEST(v)) return ST_CONTINUE;
         | 
| 221 | 
            +
             | 
| 222 | 
            +
              if (k == Qnil) {
         | 
| 223 | 
            +
                rb_hash_aset(arg->flattened, arg->key, v);
         | 
| 224 | 
            +
              } else {
         | 
| 225 | 
            +
                key = rb_str_dup(arg->key);
         | 
| 226 | 
            +
                rb_str_cat(key, "-", 1);
         | 
| 227 | 
            +
                rb_str_concat(key, to_s(k));
         | 
| 228 | 
            +
             | 
| 229 | 
            +
                rb_hash_aset(arg->flattened, key, v);
         | 
| 230 | 
            +
              }
         | 
| 231 | 
            +
              return ST_CONTINUE;
         | 
| 232 | 
            +
            }
         | 
| 233 | 
            +
             | 
| 234 | 
            +
            static VALUE flatten_data_attrs(VALUE attrs);
         | 
| 235 | 
            +
             | 
| 236 | 
            +
            static int
         | 
| 237 | 
            +
            flatten_data_attrs_i(VALUE key, VALUE value, VALUE flattened)
         | 
| 238 | 
            +
            {
         | 
| 239 | 
            +
              struct flatten_data_attrs_i2_arg arg;
         | 
| 240 | 
            +
              key = hyphenate(to_s(key));
         | 
| 241 | 
            +
             | 
| 242 | 
            +
              switch (TYPE(value)) {
         | 
| 243 | 
            +
                case T_HASH:
         | 
| 244 | 
            +
                  value = flatten_data_attrs(value);
         | 
| 245 | 
            +
                  arg.key       = key;
         | 
| 246 | 
            +
                  arg.flattened = flattened;
         | 
| 247 | 
            +
                  rb_hash_foreach(value, flatten_data_attrs_i2, (VALUE)(&arg));
         | 
| 248 | 
            +
                  break;
         | 
| 249 | 
            +
                default:
         | 
| 250 | 
            +
                  if (RTEST(value)) rb_hash_aset(flattened, key, value);
         | 
| 251 | 
            +
                  break;
         | 
| 252 | 
            +
              }
         | 
| 253 | 
            +
              return ST_CONTINUE;
         | 
| 254 | 
            +
            }
         | 
| 255 | 
            +
             | 
| 256 | 
            +
            static VALUE
         | 
| 257 | 
            +
            flatten_data_attrs(VALUE attrs)
         | 
| 258 | 
            +
            {
         | 
| 259 | 
            +
              VALUE flattened = rb_hash_new();
         | 
| 260 | 
            +
              rb_hash_foreach(attrs, flatten_data_attrs_i, flattened);
         | 
| 261 | 
            +
             | 
| 262 | 
            +
              return flattened;
         | 
| 263 | 
            +
            }
         | 
| 264 | 
            +
             | 
| 265 | 
            +
            static VALUE
         | 
| 266 | 
            +
            haml_build_data(VALUE escape_attrs, VALUE quote, VALUE values, VALUE key_str)
         | 
| 267 | 
            +
            {
         | 
| 268 | 
            +
              long i;
         | 
| 269 | 
            +
              VALUE attrs, buf, keys, key, value;
         | 
| 270 | 
            +
             | 
| 271 | 
            +
              attrs = merge_data_attrs(values, key_str);
         | 
| 272 | 
            +
              attrs = flatten_data_attrs(attrs);
         | 
| 273 | 
            +
              keys  = rb_ary_sort_bang(rb_funcall(attrs, id_keys, 0));
         | 
| 274 | 
            +
              buf   = rb_str_new("", 0);
         | 
| 275 | 
            +
             | 
| 276 | 
            +
              for (i = 0; i < RARRAY_LEN(keys); i++) {
         | 
| 277 | 
            +
                key   = rb_ary_entry(keys, i);
         | 
| 278 | 
            +
                value = rb_hash_aref(attrs, key);
         | 
| 279 | 
            +
             | 
| 280 | 
            +
                switch (value) {
         | 
| 281 | 
            +
                  case Qtrue:
         | 
| 282 | 
            +
                    rb_str_concat(buf, str_space);
         | 
| 283 | 
            +
                    rb_str_concat(buf, key);
         | 
| 284 | 
            +
                    break;
         | 
| 285 | 
            +
                  case Qnil:
         | 
| 286 | 
            +
                    break; // noop
         | 
| 287 | 
            +
                  case Qfalse:
         | 
| 288 | 
            +
                    break; // noop
         | 
| 289 | 
            +
                  default:
         | 
| 290 | 
            +
                    rb_str_concat(buf, str_space);
         | 
| 291 | 
            +
                    rb_str_concat(buf, key);
         | 
| 292 | 
            +
                    rb_str_concat(buf, str_equal);
         | 
| 293 | 
            +
                    rb_str_concat(buf, quote);
         | 
| 294 | 
            +
                    rb_str_concat(buf, escape_attribute(escape_attrs, to_s(value)));
         | 
| 295 | 
            +
                    rb_str_concat(buf, quote);
         | 
| 296 | 
            +
                    break;
         | 
| 297 | 
            +
                }
         | 
| 298 | 
            +
              }
         | 
| 299 | 
            +
             | 
| 300 | 
            +
              return buf;
         | 
| 301 | 
            +
            }
         | 
| 302 | 
            +
             | 
| 303 | 
            +
            static VALUE
         | 
| 304 | 
            +
            parse_object_ref(VALUE object_ref)
         | 
| 305 | 
            +
            {
         | 
| 306 | 
            +
              return rb_funcall(mObjectRef, id_parse, 1, object_ref);
         | 
| 307 | 
            +
            }
         | 
| 308 | 
            +
             | 
| 309 | 
            +
            static int
         | 
| 310 | 
            +
            merge_all_attrs_i(VALUE key, VALUE value, VALUE merged)
         | 
| 311 | 
            +
            {
         | 
| 312 | 
            +
              VALUE array;
         | 
| 313 | 
            +
             | 
| 314 | 
            +
              key = to_s(key);
         | 
| 315 | 
            +
              if (str_eq(key, "id", 2) || str_eq(key, "class", 5) || str_eq(key, "data", 4) || str_eq(key, "aria", 4)) {
         | 
| 316 | 
            +
                array = rb_hash_aref(merged, key);
         | 
| 317 | 
            +
                if (NIL_P(array)) {
         | 
| 318 | 
            +
                  array = rb_ary_new2(1);
         | 
| 319 | 
            +
                  rb_hash_aset(merged, key, array);
         | 
| 320 | 
            +
                }
         | 
| 321 | 
            +
                rb_ary_push(array, value);
         | 
| 322 | 
            +
              } else {
         | 
| 323 | 
            +
                rb_hash_aset(merged, key, value);
         | 
| 324 | 
            +
              }
         | 
| 325 | 
            +
              return ST_CONTINUE;
         | 
| 326 | 
            +
            }
         | 
| 327 | 
            +
             | 
| 328 | 
            +
            static VALUE
         | 
| 329 | 
            +
            merge_all_attrs(VALUE hashes)
         | 
| 330 | 
            +
            {
         | 
| 331 | 
            +
              long i;
         | 
| 332 | 
            +
              VALUE hash, merged = rb_hash_new();
         | 
| 333 | 
            +
             | 
| 334 | 
            +
              for (i = 0; i < RARRAY_LEN(hashes); i++) {
         | 
| 335 | 
            +
                hash = rb_ary_entry(hashes, i);
         | 
| 336 | 
            +
                if (!RB_TYPE_P(hash, T_HASH)) {
         | 
| 337 | 
            +
                  rb_raise(rb_eArgError, "Non-hash object is given to attributes!");
         | 
| 338 | 
            +
                }
         | 
| 339 | 
            +
                rb_hash_foreach(hash, merge_all_attrs_i, merged);
         | 
| 340 | 
            +
              }
         | 
| 341 | 
            +
              return merged;
         | 
| 342 | 
            +
            }
         | 
| 343 | 
            +
             | 
| 344 | 
            +
            int
         | 
| 345 | 
            +
            is_boolean_attribute(VALUE key, VALUE boolean_attributes)
         | 
| 346 | 
            +
            {
         | 
| 347 | 
            +
              if (str_eq(rb_str_substr(key, 0, 5), "data-", 5)) return 1;
         | 
| 348 | 
            +
              if (str_eq(rb_str_substr(key, 0, 5), "aria-", 5)) return 1;
         | 
| 349 | 
            +
              return RTEST(rb_ary_includes(boolean_attributes, key));
         | 
| 350 | 
            +
            }
         | 
| 351 | 
            +
             | 
| 352 | 
            +
            void
         | 
| 353 | 
            +
            haml_build_for_id(VALUE escape_attrs, VALUE quote, VALUE buf, VALUE values)
         | 
| 354 | 
            +
            {
         | 
| 355 | 
            +
              rb_str_cat(buf, " id=", 4);
         | 
| 356 | 
            +
              rb_str_concat(buf, quote);
         | 
| 357 | 
            +
              rb_str_concat(buf, haml_build_id(escape_attrs, values));
         | 
| 358 | 
            +
              rb_str_concat(buf, quote);
         | 
| 359 | 
            +
            }
         | 
| 360 | 
            +
             | 
| 361 | 
            +
            void
         | 
| 362 | 
            +
            haml_build_for_class(VALUE escape_attrs, VALUE quote, VALUE buf, VALUE values)
         | 
| 363 | 
            +
            {
         | 
| 364 | 
            +
              rb_str_cat(buf, " class=", 7);
         | 
| 365 | 
            +
              rb_str_concat(buf, quote);
         | 
| 366 | 
            +
              rb_str_concat(buf, haml_build_class(escape_attrs, values));
         | 
| 367 | 
            +
              rb_str_concat(buf, quote);
         | 
| 368 | 
            +
            }
         | 
| 369 | 
            +
             | 
| 370 | 
            +
            void
         | 
| 371 | 
            +
            haml_build_for_data(VALUE escape_attrs, VALUE quote, VALUE buf, VALUE values)
         | 
| 372 | 
            +
            {
         | 
| 373 | 
            +
              rb_str_concat(buf, haml_build_data(escape_attrs, quote, values, str_data));
         | 
| 374 | 
            +
            }
         | 
| 375 | 
            +
             | 
| 376 | 
            +
            void
         | 
| 377 | 
            +
            haml_build_for_aria(VALUE escape_attrs, VALUE quote, VALUE buf, VALUE values)
         | 
| 378 | 
            +
            {
         | 
| 379 | 
            +
              rb_str_concat(buf, haml_build_data(escape_attrs, quote, values, str_aria));
         | 
| 380 | 
            +
            }
         | 
| 381 | 
            +
             | 
| 382 | 
            +
            void
         | 
| 383 | 
            +
            haml_build_for_others(VALUE escape_attrs, VALUE quote, VALUE buf, VALUE key, VALUE value)
         | 
| 384 | 
            +
            {
         | 
| 385 | 
            +
              rb_str_cat(buf, " ", 1);
         | 
| 386 | 
            +
              rb_str_concat(buf, key);
         | 
| 387 | 
            +
              rb_str_cat(buf, "=", 1);
         | 
| 388 | 
            +
              rb_str_concat(buf, quote);
         | 
| 389 | 
            +
              rb_str_concat(buf, escape_attribute(escape_attrs, to_s(value)));
         | 
| 390 | 
            +
              rb_str_concat(buf, quote);
         | 
| 391 | 
            +
            }
         | 
| 392 | 
            +
             | 
| 393 | 
            +
            void
         | 
| 394 | 
            +
            haml_build_for_boolean(VALUE escape_attrs, VALUE quote, VALUE format, VALUE buf, VALUE key, VALUE value)
         | 
| 395 | 
            +
            {
         | 
| 396 | 
            +
              switch (value) {
         | 
| 397 | 
            +
                case Qtrue:
         | 
| 398 | 
            +
                  rb_str_cat(buf, " ", 1);
         | 
| 399 | 
            +
                  rb_str_concat(buf, key);
         | 
| 400 | 
            +
                  if ((TYPE(format) == T_SYMBOL || TYPE(format) == T_STRING) && rb_to_id(format) == id_xhtml) {
         | 
| 401 | 
            +
                    rb_str_cat(buf, "=", 1);
         | 
| 402 | 
            +
                    rb_str_concat(buf, quote);
         | 
| 403 | 
            +
                    rb_str_concat(buf, key);
         | 
| 404 | 
            +
                    rb_str_concat(buf, quote);
         | 
| 405 | 
            +
                  }
         | 
| 406 | 
            +
                  break;
         | 
| 407 | 
            +
                case Qfalse:
         | 
| 408 | 
            +
                  break; // noop
         | 
| 409 | 
            +
                case Qnil:
         | 
| 410 | 
            +
                  break; // noop
         | 
| 411 | 
            +
                default:
         | 
| 412 | 
            +
                  haml_build_for_others(escape_attrs, quote, buf, key, value);
         | 
| 413 | 
            +
                  break;
         | 
| 414 | 
            +
              }
         | 
| 415 | 
            +
            }
         | 
| 416 | 
            +
             | 
| 417 | 
            +
            static VALUE
         | 
| 418 | 
            +
            haml_build(VALUE escape_attrs, VALUE quote, VALUE format, VALUE boolean_attributes, VALUE object_ref, VALUE hashes)
         | 
| 419 | 
            +
            {
         | 
| 420 | 
            +
              long i;
         | 
| 421 | 
            +
              VALUE attrs, buf, key, keys, value;
         | 
| 422 | 
            +
             | 
| 423 | 
            +
              if (!NIL_P(object_ref)) rb_ary_push(hashes, parse_object_ref(object_ref));
         | 
| 424 | 
            +
              attrs = merge_all_attrs(hashes);
         | 
| 425 | 
            +
              buf   = rb_str_new("", 0);
         | 
| 426 | 
            +
              keys  = rb_ary_sort_bang(rb_funcall(attrs, id_keys, 0));
         | 
| 427 | 
            +
             | 
| 428 | 
            +
              for (i = 0; i < RARRAY_LEN(keys); i++) {
         | 
| 429 | 
            +
                key   = rb_ary_entry(keys, i);
         | 
| 430 | 
            +
                value = rb_hash_aref(attrs, key);
         | 
| 431 | 
            +
                if (str_eq(key, "id", 2)) {
         | 
| 432 | 
            +
                  haml_build_for_id(escape_attrs, quote, buf, value);
         | 
| 433 | 
            +
                } else if (str_eq(key, "class", 5)) {
         | 
| 434 | 
            +
                  haml_build_for_class(escape_attrs, quote, buf, value);
         | 
| 435 | 
            +
                } else if (str_eq(key, "data", 4)) {
         | 
| 436 | 
            +
                  haml_build_for_data(escape_attrs, quote, buf, value);
         | 
| 437 | 
            +
                } else if (str_eq(key, "aria", 4)) {
         | 
| 438 | 
            +
                  haml_build_for_aria(escape_attrs, quote, buf, value);
         | 
| 439 | 
            +
                } else if (is_boolean_attribute(key, boolean_attributes)) {
         | 
| 440 | 
            +
                  haml_build_for_boolean(escape_attrs, quote, format, buf, key, value);
         | 
| 441 | 
            +
                } else {
         | 
| 442 | 
            +
                  haml_build_for_others(escape_attrs, quote, buf, key, value);
         | 
| 443 | 
            +
                }
         | 
| 444 | 
            +
              }
         | 
| 445 | 
            +
             | 
| 446 | 
            +
              return buf;
         | 
| 447 | 
            +
            }
         | 
| 448 | 
            +
             | 
| 449 | 
            +
            static VALUE
         | 
| 450 | 
            +
            rb_haml_build_id(int argc, VALUE *argv, RB_UNUSED_VAR(VALUE self))
         | 
| 451 | 
            +
            {
         | 
| 452 | 
            +
              VALUE array;
         | 
| 453 | 
            +
             | 
| 454 | 
            +
              rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
         | 
| 455 | 
            +
              rb_scan_args(argc - 1, argv + 1, "*", &array);
         | 
| 456 | 
            +
             | 
| 457 | 
            +
              return haml_build_id(argv[0], array);
         | 
| 458 | 
            +
            }
         | 
| 459 | 
            +
             | 
| 460 | 
            +
            static VALUE
         | 
| 461 | 
            +
            rb_haml_build_class(int argc, VALUE *argv, RB_UNUSED_VAR(VALUE self))
         | 
| 462 | 
            +
            {
         | 
| 463 | 
            +
              VALUE array;
         | 
| 464 | 
            +
             | 
| 465 | 
            +
              rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
         | 
| 466 | 
            +
              rb_scan_args(argc - 1, argv + 1, "*", &array);
         | 
| 467 | 
            +
             | 
| 468 | 
            +
              return haml_build_class(argv[0], array);
         | 
| 469 | 
            +
            }
         | 
| 470 | 
            +
             | 
| 471 | 
            +
            static VALUE
         | 
| 472 | 
            +
            rb_haml_build_aria(int argc, VALUE *argv, RB_UNUSED_VAR(VALUE self))
         | 
| 473 | 
            +
            {
         | 
| 474 | 
            +
              VALUE array;
         | 
| 475 | 
            +
             | 
| 476 | 
            +
              rb_check_arity(argc, 2, UNLIMITED_ARGUMENTS);
         | 
| 477 | 
            +
              rb_scan_args(argc - 2, argv + 2, "*", &array);
         | 
| 478 | 
            +
             | 
| 479 | 
            +
              return haml_build_data(argv[0], argv[1], array, str_aria);
         | 
| 480 | 
            +
            }
         | 
| 481 | 
            +
             | 
| 482 | 
            +
            static VALUE
         | 
| 483 | 
            +
            rb_haml_build_data(int argc, VALUE *argv, RB_UNUSED_VAR(VALUE self))
         | 
| 484 | 
            +
            {
         | 
| 485 | 
            +
              VALUE array;
         | 
| 486 | 
            +
             | 
| 487 | 
            +
              rb_check_arity(argc, 2, UNLIMITED_ARGUMENTS);
         | 
| 488 | 
            +
              rb_scan_args(argc - 2, argv + 2, "*", &array);
         | 
| 489 | 
            +
             | 
| 490 | 
            +
              return haml_build_data(argv[0], argv[1], array, str_data);
         | 
| 491 | 
            +
            }
         | 
| 492 | 
            +
             | 
| 493 | 
            +
            static VALUE
         | 
| 494 | 
            +
            rb_haml_build(int argc, VALUE *argv, RB_UNUSED_VAR(VALUE self))
         | 
| 495 | 
            +
            {
         | 
| 496 | 
            +
              VALUE array;
         | 
| 497 | 
            +
             | 
| 498 | 
            +
              rb_check_arity(argc, 5, UNLIMITED_ARGUMENTS);
         | 
| 499 | 
            +
              rb_scan_args(argc - 5, argv + 5, "*", &array);
         | 
| 500 | 
            +
             | 
| 501 | 
            +
              return haml_build(argv[0], argv[1], argv[2], argv[3], argv[4], array);
         | 
| 502 | 
            +
            }
         | 
| 503 | 
            +
             | 
| 504 | 
            +
            void
         | 
| 505 | 
            +
            Init_haml(void)
         | 
| 506 | 
            +
            {
         | 
| 507 | 
            +
              VALUE mHaml, mUtil;
         | 
| 508 | 
            +
             | 
| 509 | 
            +
              mHaml             = rb_define_module("Haml");
         | 
| 510 | 
            +
              mObjectRef        = rb_define_module_under(mHaml, "ObjectRef");
         | 
| 511 | 
            +
              mUtil             = rb_define_module_under(mHaml, "Util");
         | 
| 512 | 
            +
              mAttributeBuilder = rb_define_module_under(mHaml, "AttributeBuilder");
         | 
| 513 | 
            +
             | 
| 514 | 
            +
              rb_define_singleton_method(mUtil, "escape_html", rb_escape_html, 1);
         | 
| 515 | 
            +
              rb_define_singleton_method(mAttributeBuilder, "build", rb_haml_build, -1);
         | 
| 516 | 
            +
              rb_define_singleton_method(mAttributeBuilder, "build_id", rb_haml_build_id, -1);
         | 
| 517 | 
            +
              rb_define_singleton_method(mAttributeBuilder, "build_class", rb_haml_build_class, -1);
         | 
| 518 | 
            +
              rb_define_singleton_method(mAttributeBuilder, "build_aria", rb_haml_build_aria, -1);
         | 
| 519 | 
            +
              rb_define_singleton_method(mAttributeBuilder, "build_data", rb_haml_build_data, -1);
         | 
| 520 | 
            +
             | 
| 521 | 
            +
              id_flatten   = rb_intern("flatten");
         | 
| 522 | 
            +
              id_keys      = rb_intern("keys");
         | 
| 523 | 
            +
              id_parse     = rb_intern("parse");
         | 
| 524 | 
            +
              id_prepend   = rb_intern("prepend");
         | 
| 525 | 
            +
              id_tr        = rb_intern("tr");
         | 
| 526 | 
            +
              id_uniq_bang = rb_intern("uniq!");
         | 
| 527 | 
            +
              id_xhtml     = rb_intern("xhtml");
         | 
| 528 | 
            +
             | 
| 529 | 
            +
              // Consider using rb_interned_str() once we stop supporting Ruby 2.7.
         | 
| 530 | 
            +
              rb_gc_register_mark_object(str_aria       = rb_obj_freeze(rb_str_new_cstr("aria")));
         | 
| 531 | 
            +
              rb_gc_register_mark_object(str_data       = rb_obj_freeze(rb_str_new_cstr("data")));
         | 
| 532 | 
            +
              rb_gc_register_mark_object(str_equal      = rb_obj_freeze(rb_str_new_cstr("=")));
         | 
| 533 | 
            +
              rb_gc_register_mark_object(str_hyphen     = rb_obj_freeze(rb_str_new_cstr("-")));
         | 
| 534 | 
            +
              rb_gc_register_mark_object(str_space      = rb_obj_freeze(rb_str_new_cstr(" ")));
         | 
| 535 | 
            +
              rb_gc_register_mark_object(str_underscore = rb_obj_freeze(rb_str_new_cstr("_")));
         | 
| 536 | 
            +
            }
         | 
| 537 | 
            +
            #endif
         | 
    
        data/ext/haml/hescape.c
    ADDED
    
    | @@ -0,0 +1,108 @@ | |
| 1 | 
            +
            #include <stdio.h>
         | 
| 2 | 
            +
            #include <string.h>
         | 
| 3 | 
            +
            #include <stdlib.h>
         | 
| 4 | 
            +
            #include "hescape.h"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            static const char *ESCAPED_STRING[] = {
         | 
| 7 | 
            +
              "",
         | 
| 8 | 
            +
              """,
         | 
| 9 | 
            +
              "&",
         | 
| 10 | 
            +
              "'",
         | 
| 11 | 
            +
              "<",
         | 
| 12 | 
            +
              ">",
         | 
| 13 | 
            +
            };
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            // This is strlen(ESCAPED_STRING[x]) optimized specially.
         | 
| 16 | 
            +
            // Mapping: 1 => 6, 2 => 5, 3 => 5, 4 => 4, 5 => 4
         | 
| 17 | 
            +
            #define ESC_LEN(x) ((13 - x) / 2)
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            /*
         | 
| 20 | 
            +
             * Given ASCII-compatible character, return index of ESCAPED_STRING.
         | 
| 21 | 
            +
             *
         | 
| 22 | 
            +
             * " (34) => 1 (")
         | 
| 23 | 
            +
             * & (38) => 2 (&)
         | 
| 24 | 
            +
             * ' (39) => 3 (')
         | 
| 25 | 
            +
             * < (60) => 4 (<)
         | 
| 26 | 
            +
             * > (62) => 5 (>)
         | 
| 27 | 
            +
             */
         | 
| 28 | 
            +
            static const char HTML_ESCAPE_TABLE[] = {
         | 
| 29 | 
            +
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         | 
| 30 | 
            +
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         | 
| 31 | 
            +
              0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0,
         | 
| 32 | 
            +
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 5, 0,
         | 
| 33 | 
            +
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         | 
| 34 | 
            +
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         | 
| 35 | 
            +
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         | 
| 36 | 
            +
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         | 
| 37 | 
            +
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         | 
| 38 | 
            +
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         | 
| 39 | 
            +
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         | 
| 40 | 
            +
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         | 
| 41 | 
            +
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         | 
| 42 | 
            +
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         | 
| 43 | 
            +
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         | 
| 44 | 
            +
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         | 
| 45 | 
            +
            };
         | 
| 46 | 
            +
             | 
| 47 | 
            +
            static char*
         | 
| 48 | 
            +
            ensure_allocated(char *buf, size_t size, size_t *asize)
         | 
| 49 | 
            +
            {
         | 
| 50 | 
            +
              size_t new_size;
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              if (size < *asize)
         | 
| 53 | 
            +
                return buf;
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              if (*asize == 0) {
         | 
| 56 | 
            +
                new_size = size;
         | 
| 57 | 
            +
              } else {
         | 
| 58 | 
            +
                new_size = *asize;
         | 
| 59 | 
            +
              }
         | 
| 60 | 
            +
             | 
| 61 | 
            +
              // Increase buffer size by 1.5x if realloced multiple times.
         | 
| 62 | 
            +
              while (new_size < size)
         | 
| 63 | 
            +
                new_size = (new_size << 1) - (new_size >> 1);
         | 
| 64 | 
            +
             | 
| 65 | 
            +
              // Round allocation up to multiple of 8.
         | 
| 66 | 
            +
              new_size = (new_size + 7) & ~7;
         | 
| 67 | 
            +
             | 
| 68 | 
            +
              *asize = new_size;
         | 
| 69 | 
            +
              return realloc(buf, new_size);
         | 
| 70 | 
            +
            }
         | 
| 71 | 
            +
             | 
| 72 | 
            +
            size_t
         | 
| 73 | 
            +
            hesc_escape_html(char **dest, const char *buf, size_t size)
         | 
| 74 | 
            +
            {
         | 
| 75 | 
            +
              size_t asize = 0, esc_i = 0, esize = 0, i = 0, rbuf_end = 0;
         | 
| 76 | 
            +
              const char *esc;
         | 
| 77 | 
            +
              char *rbuf = NULL;
         | 
| 78 | 
            +
             | 
| 79 | 
            +
              while (i < size) {
         | 
| 80 | 
            +
                // Loop here to skip non-escaped characters fast.
         | 
| 81 | 
            +
                while (i < size && (esc_i = HTML_ESCAPE_TABLE[(unsigned char)buf[i]]) == 0)
         | 
| 82 | 
            +
                  i++;
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                if (i < size && esc_i) {
         | 
| 85 | 
            +
                  esc = ESCAPED_STRING[esc_i];
         | 
| 86 | 
            +
                  rbuf = ensure_allocated(rbuf, sizeof(char) * (size + esize + ESC_LEN(esc_i) + 1), &asize);
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                  // Copy pending characters and escaped string.
         | 
| 89 | 
            +
                  memmove(rbuf + rbuf_end, buf + (rbuf_end - esize), i - (rbuf_end - esize));
         | 
| 90 | 
            +
                  memmove(rbuf + i + esize, esc, ESC_LEN(esc_i));
         | 
| 91 | 
            +
                  rbuf_end = i + esize + ESC_LEN(esc_i);
         | 
| 92 | 
            +
                  esize += ESC_LEN(esc_i) - 1;
         | 
| 93 | 
            +
                }
         | 
| 94 | 
            +
                i++;
         | 
| 95 | 
            +
              }
         | 
| 96 | 
            +
             | 
| 97 | 
            +
              if (rbuf_end == 0) {
         | 
| 98 | 
            +
                // Return given buf and size if there are no escaped characters.
         | 
| 99 | 
            +
                *dest = (char *)buf;
         | 
| 100 | 
            +
                return size;
         | 
| 101 | 
            +
              } else {
         | 
| 102 | 
            +
                // Copy pending characters including NULL character.
         | 
| 103 | 
            +
                memmove(rbuf + rbuf_end, buf + (rbuf_end - esize), (size + 1) - (rbuf_end - esize));
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                *dest = rbuf;
         | 
| 106 | 
            +
                return size + esize;
         | 
| 107 | 
            +
              }
         | 
| 108 | 
            +
            }
         | 
    
        data/ext/haml/hescape.h
    ADDED
    
    | @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            #ifndef HESCAPE_H
         | 
| 2 | 
            +
            #define HESCAPE_H
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            #include <sys/types.h>
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            /*
         | 
| 7 | 
            +
             * Replace characters according to the following rules.
         | 
| 8 | 
            +
             * Note that this function can handle only ASCII-compatible string.
         | 
| 9 | 
            +
             *
         | 
| 10 | 
            +
             * " => "
         | 
| 11 | 
            +
             * & => &
         | 
| 12 | 
            +
             * ' => '
         | 
| 13 | 
            +
             * < => <
         | 
| 14 | 
            +
             * > => >
         | 
| 15 | 
            +
             *
         | 
| 16 | 
            +
             * @return size of dest. If it's larger than len, dest is required to be freed.
         | 
| 17 | 
            +
             */
         | 
| 18 | 
            +
            extern size_t hesc_escape_html(char **dest, const char *src, size_t size);
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            #endif
         |