# frozen_string_literal: true begin require 'erubis/tiny' rescue LoadError require 'erb' end require 'set' require 'stringio' require 'strscan' module Haml # A module containing various useful functions. module Util extend self begin # Ruby 3.2+ or ERB 4+ require 'erb/escape' define_singleton_method(:escape_html, ERB::Escape.instance_method(:html_escape)) rescue LoadError require 'cgi/escape' def self.escape_html(html) CGI.escapeHTML(html.to_s) end end # TODO: Remove unescape_interpolation's workaround and get rid of `respond_to?`. def self.escape_html_safe(html) html = html.to_s (html.respond_to?(:html_safe?) && html.html_safe?) ? html : escape_html(html) end # Silence all output to STDERR within a block. # # @yield A block in which no output will be printed to STDERR def silence_warnings the_real_stderr, $stderr = $stderr, StringIO.new yield ensure $stderr = the_real_stderr end ## Rails XSS Safety # Whether or not ActionView's XSS protection is available and enabled, # as is the default for Rails 3.0+, and optional for version 2.3.5+. # Overridden in haml/template.rb if this is the case. # # @return [Boolean] def rails_xss_safe? false end # Checks that the encoding of a string is valid # and cleans up potential encoding gotchas like the UTF-8 BOM. # If it's not, yields an error string describing the invalid character # and the line on which it occurs. # # @param str [String] The string of which to check the encoding # @yield [msg] A block in which an encoding error can be raised. # Only yields if there is an encoding error # @yieldparam msg [String] The error message to be raised # @return [String] `str`, potentially with encoding gotchas like BOMs removed def check_encoding(str) if str.valid_encoding? # Get rid of the Unicode BOM if possible # Shortcut for UTF-8 which might be the majority case if str.encoding == Encoding::UTF_8 return str.gsub(/\A\uFEFF/, '') elsif str.encoding.name =~ /^UTF-(16|32)(BE|LE)?$/ return str.gsub(Regexp.new("\\A\uFEFF".encode(str.encoding)), '') else return str end end encoding = str.encoding newlines = Regexp.new("\r\n|\r|\n".encode(encoding).force_encoding(Encoding::ASCII_8BIT)) str.force_encoding(Encoding::ASCII_8BIT).split(newlines).each_with_index do |line, i| begin line.encode(encoding) rescue Encoding::UndefinedConversionError => e yield <