Mini Shell

Direktori : /opt/sharedrads/extras/
Upload File :
Current File : //opt/sharedrads/extras/say

#!/usr/bin/perl
# encoding: utf-8
# author: Kyle Yetter <kyle@ohboyohboyohboy.org>
# created on: May 27, 2011


use strict;
use IO::Handle;
use Scalar::Util 'openhandle';
use Carp;
use File::Basename;
use Getopt::Long;

our $VERSION     = '1.0.0';
our $add_newline = 1;
our $do_help     = 0;
our $do_version  = 0;

our %option_spec = (
  'n|no-newline' => sub { $add_newline = 0; },
  'h|help' => sub {  help();  exit 0; },
  'v|version' => sub {  print "$VERSION\n";  exit 0; }
);

our %ESCAPE_MAP = (
  'n'   => "\n",  't'   => "\t",  'r'   => "\r",  'e'   => "\e",
  '\\'  => "\\",  'a'   => "\a",  "s"   => " ",   'b'   => "\b",
  '%'   => '%%'
);
our $BACKGROUND_CODE = "\e[4%im";
our $FOREGROUND_CODE = "\e[3%im";

our %COLOR_VALUES = (
  "white" => 7,     "black" => 0,  "cyan" => 6,      "blue" => 4,
  "green" => 2,     "red" => 1,  "magenta" => 5,   "yellow" => 3
);

our %DISPLAY_MAP = ();
while ( my ( $k, $v ) = each %COLOR_VALUES ) {
  $DISPLAY_MAP{ $v } = $k;
}

our %COLOR_NAMES = (
  "bla" => "black",    "gree" => "green",       "w" => "white",
  "blac" => "black",   "green" => "green",      "wh" => "white",
  "black" => "black",  "m" => "magenta",        "whi" => "white",
  "blu" => "blue",     "ma" => "magenta",       "whit" => "white",
  "blue" => "blue",    "mag" => "magenta",      "white" => "white",
  "c" => "cyan",       "mage" => "magenta",     "y" => "yellow",
  "cy" => "cyan",      "magen" => "magenta",    "ye" => "yellow",
  "cya" => "cyan",     "magent" => "magenta",   "yel" => "yellow",
  "cyan" => "cyan",    "magenta" => "magenta",  "yell" => "yellow",
  "g" => "green",      "r" => "red",            "yello" => "yellow",
  "gr" => "green",     "re" => "red",           "yellow" => "yellow",
  "gre" => "green",    "red" => "red"
);

our %STYLE_CODES = (
  '*' => "\e[1m",  '_' => "\e[4m", '~' => "\e[7m",  '!' => "\e[5m"
);

our $RX_TAGS = qr`
  ( [\*~!_]* )
| (   [a-z]+ (?: @ [a-z]+ )?
  | @ [a-z]+
  )
`xi;

our $RX_COLOR_ARG = qr`
  % \( ( $RX_TAGS ) \)
  ( [\-\+0\s\#]? \d* (?:\.\d+)? [hlI]? [bcdEefGgiopsuXx] )
`x;

our $RX_ESCAPE    = qr(^\\(.));
our $RX_CLOSE_TAG = qr(^</$RX_TAGS>);
our $RX_OPEN_TAG  = qr(^<$RX_TAGS>);
our $RX_TEXT      = qr(^(.+?)(?=(?:\\.|</?$RX_TAGS>|\Z)))s;

our $BAD_CLOSE = q(bad format close tag: expected `%(g)s', but got `%(y)s');

sub color_value($) {
  my ( $name ) = @_;
  if ( $name ) {
    my $full_name = $COLOR_NAMES{ lc( $name ) } or
      damn( "`%(y)s' is not a valid color name", $name );
    return $COLOR_VALUES{ $full_name };
  }
}

sub color_scan($) {
  my ( $text ) = @_;
  my ( @foreground, @background, @styles );
  my $out = '';
  open( my $str, '>', \$out );

  $text =~ s($RX_COLOR_ARG)(<$1>%$4</$1>)g;
  $_ = $text;

  while ( $_ ) {
    my $print_escape = 0;

    if ( /$RX_ESCAPE/ ) {
      $_ = $';
      my $c = $ESCAPE_MAP{ $1 } || $1;
      print $str $c;
    } elsif ( /$RX_CLOSE_TAG/ ) {
      $_ = $';

      if ( my $style_char = $1 ) {
        for my $c ( split //, $style_char ) {
          my $v = $STYLE_CODES{ $c };
          @styles = grep { $_ ne $v } @styles;
        }
      } elsif ( my $colors = $2 ) {
        my ( $fg, $bg ) = split( /@/, $colors, 2 );

        if ( my $value = color_value( $fg ) ) {
          my $current = $foreground[ -1 ];
          if ( $current and $current ne $value ) {
            my $expected = $DISPLAY_MAP{ $current };
            my $got      = $DISPLAY_MAP{ $value };
            damn( $BAD_CLOSE, $expected, $got );
          }
          pop @foreground;
        }

        if ( my $value = color_value( $bg ) ) {
          my $current = $background[ -1 ];
          if ( $current and $current ne $value ) {
            my $expected = $DISPLAY_MAP{ $current };
            my $got      = $DISPLAY_MAP{ $value };
            damn( $BAD_CLOSE, $expected, $got );
          }
          pop @background;
        }
      }

      $print_escape = 1;

    } elsif ( /$RX_OPEN_TAG/ ) {
      $_ = $';

      if ( my $style_char = $1 ) {
        for my $c ( split //, $style_char ) {
          push @styles, $STYLE_CODES{ $c };
        }
      } elsif ( my $colors = $2 ) {
        my ( $fg, $bg ) = split( /@/, $colors, 2 );
        if ( my $value = color_value( $fg ) ) { push @foreground, $value; }
        if ( my $value = color_value( $bg ) ) { push @background, $value; }
      }

      $print_escape = 1;
    } elsif ( /$RX_TEXT/ ) {
      $_ = $';
      print $str $1;
    } else {
      damn( "this shouldn't happen: (%s@%i)", __FILE__, __LINE__ );
    }

    if ( $print_escape ) {
      print $str "\e[0m";
      if ( my $value = $foreground[ -1 ] ) { printf $str $FOREGROUND_CODE, $value; }
      if ( my $value = $background[ -1 ] ) { printf $str $BACKGROUND_CODE, $value; }
      print $str join( '', @styles );
    }
  }

  if ( @foreground + @background + @styles > 0 ) {
    print $str "\e[0m";
  }

  close( $str );
  return $out;
}

sub say {
  my $currfh = select();
  my $handle;
  {
      no strict 'refs';
      $handle = openhandle( $_[0] ) ? shift : \*$currfh;
      use strict 'refs';
  }

  my ( $format, @args ) = @_;
  $format ||= $_;
  $format ||= '';

  my $warning;
  local $SIG{__WARN__} = sub { $warning = join q{}, @_ };

  if ( $add_newline and $format !~ m($/\z)s ) { $format .= $/; }

  $format = color_scan( $format );

  my $res = printf {$handle} $format, @args;
  return $res if $res;

  $warning =~ s/[ ]at[ ].*//xms;
  croak $warning;
}

*IO::Handle::say = \&say if ! defined &IO::Handle::say;

sub damn {
  my ( $message, @args ) = @_;
  say( \*STDERR, "<red>ERROR:</red> $message", @args );
  exit( 1 );
}

sub help {
  my $name = basename( $0 );
  my @help_lines = <DATA>;
  my $help = join( '', @help_lines );
  $help =~ s(\b__name__\b)($name)g;
  print $help, "\n";
}





GetOptions( %option_spec );

say @ARGV;

__END__
__name__: print a printf-style formatted string with ansi color code markup
USAGE: __name__ [opts] format_string [args ...]

OPTIONS:
  -n, --no-newline     Do not append a trailing newline
  -v, --version        Print program version and exit
  -h, --help           Print program summary and exit

ANSI MARK UP EXAMPLES:
  styles: <_>underline</_> <~>invert</~> <!>blink</!> <*>bold</*>
  colors: <fg_color></fg_color> <@bg_color></@bg_color> <fg@bg></fg@bg>

Zerion Mini Shell 1.0