package DDG::Goodie::HexToASCII; # ABSTRACT: Returns the ASCII representaion of a given hexadecimal value. (If printbale of course). use strict; use DDG::Goodie; # Used to restrict long generated outputs use constant MAX_INPUT_CHARS => 128; triggers startend => 'ascii', 'hex to ascii'; zci is_cached => 1; zci answer_type => 'ascii'; handle remainder => sub { my $value = $_; $value =~ s/^\s+//; $value =~ s/\s+$//; $value =~ s/ (?:to|as|in)$//i; if ($value =~ /^(?:[0\\]x)?([0-9a-f]+)$/i or $value =~ /^([0-9a-f]+)h?$/i) { my @digits = $1 =~ /(..)/g; my ($pure, $html, $count); # Now convert them one by one, until it seems like there might be too many. while (my $digit = shift @digits and $count++ <= MAX_INPUT_CHARS) { my $printable = printable_chr(hex $digit); return unless $printable; $pure .= $printable->{pure}; $html .= $printable->{html}; } # If they gave us more input than we're willing to handle, throw in # an appropriate ellipsis for their output type and call it done. if (scalar @digits) { $pure .= '...'; $html .= '…'; } return $html, structured_answer => { data => { title => $html, subtitle => "Hex to ASCII: $value", }, templates => { group => 'text', }, }; } return; }; # Some of these are actually printable whitespace, but # for consistency, we'll make them discoverable, too. my %invisibles = ( 0x00 => { abbr => 'NUL', desc => 'null' }, 0x01 => { abbr => 'SOH', desc => 'start of heading' }, 0x02 => { abbr => 'STX', desc => 'start of text' }, 0x03 => { abbr => 'ETX', desc => 'end of text' }, 0x04 => { abbr => 'EOT', desc => 'end of transmission' }, 0x05 => { abbr => 'ENQ', desc => 'enquiry' }, 0x06 => { abbr => 'ACK', desc => 'acknowledge' }, 0x07 => { abbr => 'BEL', desc => 'bell', }, 0x08 => { abbr => 'BS', desc => 'backspace', }, 0x09 => { abbr => 'TAB', desc => 'horizontal tab', }, 0x0A => { abbr => 'LF', desc => 'linefeed', }, 0x0B => { abbr => 'VT', desc => 'vertical tab', }, 0x0C => { abbr => 'FF', desc => 'form feed', }, 0x0D => { abbr => 'CR', desc => 'carriage return', }, 0x0E => { abbr => 'SO', desc => 'shift out', }, 0x0F => { abbr => 'SI', desc => 'shift in', }, 0x10 => { abbr => 'DLE', desc => 'data link escape', }, 0x11 => { abbr => 'DC1', desc => 'device control 1', }, 0x12 => { abbr => 'DC2', desc => 'device control 2', }, 0x13 => { abbr => 'DC3', desc => 'device control 3', }, 0x14 => { abbr => 'DC4', desc => 'device control 4', }, 0x15 => { abbr => 'NAK', desc => 'negative acknowledge', }, 0x16 => { abbr => 'SYN', desc => 'synchronous idle', }, 0x17 => { abbr => 'ETB', desc => 'end of transmission block', }, 0x18 => { abbr => 'CAN', desc => 'cancel', }, 0x19 => { abbr => 'EM', desc => 'end of medium', }, 0x1A => { abbr => 'SUB', desc => 'substitute', }, 0x1B => { abbr => 'ESC', desc => 'escape', }, 0x1C => { abbr => 'FS', desc => 'file separator', }, 0x1D => { abbr => 'GS', desc => 'group separator', }, 0x1E => { abbr => 'RS', desc => 'record separator', }, 0x1F => { abbr => 'US', desc => 'unit separator', }, 0x7F => { abbr => 'DEL', desc => 'delete', }, ); # The actual hex to ASCII conversion. # Returns a hashref with a 'pure' and 'html' representation. # The html representation includes tagged information on invisible # characters (defined as non-printable + non-space whitespace.) # The pure ASCII representation elides the invisible characters. sub printable_chr { my ($hex) = @_; # Looks like non-ASCII, don't give them anything. return if ($hex > 0x7F); my $representation; if (my $char_info = $invisibles{$hex}) { $representation->{html} = '[' . $char_info->{abbr} . ']'; $representation->{pure} = ''; # Don't want to add any printable whitespace and wonder what happened. } else { # This must be a printable character, so just let chr figure it out $representation->{html} = html_enc($representation->{pure} = chr $hex); } return $representation; } 1;