haml 5.2.2 → 6.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/workflows/test.yml +13 -14
  4. data/.gitignore +16 -16
  5. data/.yardopts +0 -3
  6. data/CHANGELOG.md +116 -3
  7. data/Gemfile +18 -11
  8. data/MIT-LICENSE +1 -1
  9. data/README.md +17 -23
  10. data/REFERENCE.md +69 -145
  11. data/Rakefile +48 -81
  12. data/bin/bench +66 -0
  13. data/bin/console +11 -0
  14. data/bin/ruby +3 -0
  15. data/bin/setup +7 -0
  16. data/bin/stackprof +27 -0
  17. data/bin/test +24 -0
  18. data/exe/haml +6 -0
  19. data/ext/haml/extconf.rb +10 -0
  20. data/ext/haml/haml.c +537 -0
  21. data/ext/haml/hescape.c +108 -0
  22. data/ext/haml/hescape.h +20 -0
  23. data/haml.gemspec +39 -37
  24. data/lib/haml/ambles.rb +20 -0
  25. data/lib/haml/attribute_builder.rb +134 -179
  26. data/lib/haml/attribute_compiler.rb +85 -194
  27. data/lib/haml/attribute_parser.rb +92 -126
  28. data/lib/haml/cli.rb +154 -0
  29. data/lib/haml/compiler/children_compiler.rb +155 -0
  30. data/lib/haml/compiler/comment_compiler.rb +51 -0
  31. data/lib/haml/compiler/doctype_compiler.rb +46 -0
  32. data/lib/haml/compiler/script_compiler.rb +114 -0
  33. data/lib/haml/compiler/silent_script_compiler.rb +24 -0
  34. data/lib/haml/compiler/tag_compiler.rb +76 -0
  35. data/lib/haml/compiler.rb +63 -296
  36. data/lib/haml/dynamic_merger.rb +67 -0
  37. data/lib/haml/engine.rb +48 -227
  38. data/lib/haml/error.rb +5 -4
  39. data/lib/haml/escape.rb +13 -0
  40. data/lib/haml/escape_any.rb +21 -0
  41. data/lib/haml/filters/base.rb +12 -0
  42. data/lib/haml/filters/cdata.rb +20 -0
  43. data/lib/haml/filters/coffee.rb +17 -0
  44. data/lib/haml/filters/css.rb +33 -0
  45. data/lib/haml/filters/erb.rb +10 -0
  46. data/lib/haml/filters/escaped.rb +22 -0
  47. data/lib/haml/filters/javascript.rb +33 -0
  48. data/lib/haml/filters/less.rb +20 -0
  49. data/lib/haml/filters/markdown.rb +11 -0
  50. data/lib/haml/filters/plain.rb +29 -0
  51. data/lib/haml/filters/preserve.rb +22 -0
  52. data/lib/haml/filters/ruby.rb +10 -0
  53. data/lib/haml/filters/sass.rb +15 -0
  54. data/lib/haml/filters/scss.rb +15 -0
  55. data/lib/haml/filters/text_base.rb +25 -0
  56. data/lib/haml/filters/tilt_base.rb +59 -0
  57. data/lib/haml/filters.rb +54 -378
  58. data/lib/haml/force_escape.rb +29 -0
  59. data/lib/haml/helpers.rb +3 -697
  60. data/lib/haml/html.rb +22 -0
  61. data/lib/haml/identity.rb +13 -0
  62. data/lib/haml/object_ref.rb +35 -0
  63. data/lib/haml/parser.rb +157 -22
  64. data/lib/haml/rails_helpers.rb +53 -0
  65. data/lib/haml/rails_template.rb +57 -0
  66. data/lib/haml/railtie.rb +3 -46
  67. data/lib/haml/ruby_expression.rb +32 -0
  68. data/lib/haml/string_splitter.rb +140 -0
  69. data/lib/haml/template.rb +15 -34
  70. data/lib/haml/temple_line_counter.rb +2 -1
  71. data/lib/haml/util.rb +18 -15
  72. data/lib/haml/version.rb +1 -2
  73. data/lib/haml/whitespace.rb +8 -0
  74. data/lib/haml.rb +8 -20
  75. metadata +211 -55
  76. data/.gitmodules +0 -3
  77. data/TODO +0 -24
  78. data/benchmark.rb +0 -70
  79. data/bin/haml +0 -9
  80. data/lib/haml/.gitattributes +0 -1
  81. data/lib/haml/buffer.rb +0 -182
  82. data/lib/haml/escapable.rb +0 -77
  83. data/lib/haml/exec.rb +0 -347
  84. data/lib/haml/generator.rb +0 -42
  85. data/lib/haml/helpers/action_view_extensions.rb +0 -60
  86. data/lib/haml/helpers/action_view_mods.rb +0 -132
  87. data/lib/haml/helpers/action_view_xss_mods.rb +0 -60
  88. data/lib/haml/helpers/safe_erubi_template.rb +0 -20
  89. data/lib/haml/helpers/safe_erubis_template.rb +0 -33
  90. data/lib/haml/helpers/xss_mods.rb +0 -114
  91. data/lib/haml/options.rb +0 -273
  92. data/lib/haml/plugin.rb +0 -54
  93. data/lib/haml/sass_rails_filter.rb +0 -47
  94. data/lib/haml/template/options.rb +0 -27
  95. data/lib/haml/temple_engine.rb +0 -124
  96. data/yard/default/.gitignore +0 -1
  97. data/yard/default/fulldoc/html/css/common.sass +0 -15
  98. 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
@@ -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
+ "&quot;",
9
+ "&amp;",
10
+ "&#39;",
11
+ "&lt;",
12
+ "&gt;",
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 (&quot;)
23
+ * & (38) => 2 (&amp;)
24
+ * ' (39) => 3 (&#39;)
25
+ * < (60) => 4 (&lt;)
26
+ * > (62) => 5 (&gt;)
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
+ }
@@ -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
+ * " => &quot;
11
+ * & => &amp;
12
+ * ' => &#39;
13
+ * < => &lt;
14
+ * > => &gt;
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