File Coverage

blib/lib/Term/ANSIColor.pm
Criterion Covered Total %
statement 194 194 100.0
branch 128 128 100.0
condition 93 93 100.0
subroutine 30 30 100.0
pod 6 24 25.0
total 451 469 96.1


line stmt bran cond sub pod time code
1             # Color screen output using ANSI escape sequences.
2             #
3             # Copyright 1996, 1997, 1998, 2000, 2001, 2002, 2005, 2006, 2008, 2009, 2010,
4             # 2011, 2012, 2013, 2014, 2015, 2016 Russ Allbery
5             # Copyright 1996 Zenin
6             # Copyright 2012 Kurt Starsinic
7             #
8             # This program is free software; you may redistribute it and/or modify it
9             # under the same terms as Perl itself.
10             #
11             # PUSH/POP support submitted 2007 by openmethods.com voice solutions
12             #
13             # Ah, September, when the sysadmins turn colors and fall off the trees....
14             # -- Dave Van Domelen
15              
16             ##############################################################################
17             # Modules and declarations
18             ##############################################################################
19              
20             package Term::ANSIColor;
21              
22 6     6   95616 use 5.006;
  6         18  
23 6     6   24 use strict;
  6         7  
  6         120  
24 6     6   29 use warnings;
  6         18  
  6         189  
25              
26             # Also uses Carp but loads it on demand to reduce memory usage.
27              
28 6     6   22 use Exporter ();
  6         6  
  6         1824  
29              
30             # use Exporter plus @ISA instead of use base for 5.6 compatibility.
31             ## no critic (ClassHierarchies::ProhibitExplicitISA)
32              
33             # Declare variables that should be set in BEGIN for robustness.
34             ## no critic (Modules::ProhibitAutomaticExportation)
35             our (@EXPORT, @EXPORT_OK, %EXPORT_TAGS, @ISA, $VERSION);
36              
37             # We use autoloading, which sets this variable to the name of the called sub.
38             our $AUTOLOAD;
39              
40             # Set $VERSION and everything export-related in a BEGIN block for robustness
41             # against circular module loading (not that we load any modules, but
42             # consistency is good).
43             BEGIN {
44 6     6   13 $VERSION = '4.06';
45              
46             # All of the basic supported constants, used in %EXPORT_TAGS.
47 6         34 my @colorlist = qw(
48             CLEAR RESET BOLD DARK
49             FAINT ITALIC UNDERLINE UNDERSCORE
50             BLINK REVERSE CONCEALED
51              
52             BLACK RED GREEN YELLOW
53             BLUE MAGENTA CYAN WHITE
54             ON_BLACK ON_RED ON_GREEN ON_YELLOW
55             ON_BLUE ON_MAGENTA ON_CYAN ON_WHITE
56              
57             BRIGHT_BLACK BRIGHT_RED BRIGHT_GREEN BRIGHT_YELLOW
58             BRIGHT_BLUE BRIGHT_MAGENTA BRIGHT_CYAN BRIGHT_WHITE
59             ON_BRIGHT_BLACK ON_BRIGHT_RED ON_BRIGHT_GREEN ON_BRIGHT_YELLOW
60             ON_BRIGHT_BLUE ON_BRIGHT_MAGENTA ON_BRIGHT_CYAN ON_BRIGHT_WHITE
61             );
62              
63             # 256-color constants, used in %EXPORT_TAGS.
64             my @colorlist256 = (
65 1536         2238 (map { ("ANSI$_", "ON_ANSI$_") } 0 .. 255),
66 6         17 (map { ("GREY$_", "ON_GREY$_") } 0 .. 23),
  144         870  
67             );
68 6         88 for my $r (0 .. 5) {
69 36         41 for my $g (0 .. 5) {
70 216         177 push(@colorlist256, map { ("RGB$r$g$_", "ON_RGB$r$g$_") } 0 .. 5);
  1296         2392  
71             }
72             }
73              
74             # Exported symbol configuration.
75 6         55 @ISA = qw(Exporter);
76 6         11 @EXPORT = qw(color colored);
77 6         12 @EXPORT_OK = qw(uncolor colorstrip colorvalid coloralias);
78 6         46 %EXPORT_TAGS = (
79             constants => \@colorlist,
80             constants256 => \@colorlist256,
81             pushpop => [@colorlist, qw(PUSHCOLOR POPCOLOR LOCALCOLOR)],
82             );
83 6         13142 Exporter::export_ok_tags('pushpop', 'constants256');
84             }
85              
86             ##############################################################################
87             # Package variables
88             ##############################################################################
89              
90             # If this is set, any color changes will implicitly push the current color
91             # onto the stack and then pop it at the end of the constant sequence, just as
92             # if LOCALCOLOR were used.
93             our $AUTOLOCAL;
94              
95             # Caller sets this to force a reset at the end of each constant sequence.
96             our $AUTORESET;
97              
98             # Caller sets this to force colors to be reset at the end of each line.
99             our $EACHLINE;
100              
101             ##############################################################################
102             # Internal data structures
103             ##############################################################################
104              
105             # This module does quite a bit of initialization at the time it is first
106             # loaded, primarily to set up the package-global %ATTRIBUTES hash. The
107             # entries for 256-color names are easier to handle programmatically, and
108             # custom colors are also imported from the environment if any are set.
109              
110             # All basic supported attributes, including aliases.
111             #<<<
112             our %ATTRIBUTES = (
113             'clear' => 0,
114             'reset' => 0,
115             'bold' => 1,
116             'dark' => 2,
117             'faint' => 2,
118             'italic' => 3,
119             'underline' => 4,
120             'underscore' => 4,
121             'blink' => 5,
122             'reverse' => 7,
123             'concealed' => 8,
124              
125             'black' => 30, 'on_black' => 40,
126             'red' => 31, 'on_red' => 41,
127             'green' => 32, 'on_green' => 42,
128             'yellow' => 33, 'on_yellow' => 43,
129             'blue' => 34, 'on_blue' => 44,
130             'magenta' => 35, 'on_magenta' => 45,
131             'cyan' => 36, 'on_cyan' => 46,
132             'white' => 37, 'on_white' => 47,
133              
134             'bright_black' => 90, 'on_bright_black' => 100,
135             'bright_red' => 91, 'on_bright_red' => 101,
136             'bright_green' => 92, 'on_bright_green' => 102,
137             'bright_yellow' => 93, 'on_bright_yellow' => 103,
138             'bright_blue' => 94, 'on_bright_blue' => 104,
139             'bright_magenta' => 95, 'on_bright_magenta' => 105,
140             'bright_cyan' => 96, 'on_bright_cyan' => 106,
141             'bright_white' => 97, 'on_bright_white' => 107,
142             );
143             #>>>
144              
145             # Generating the 256-color codes involves a lot of codes and offsets that are
146             # not helped by turning them into constants.
147              
148             # The first 16 256-color codes are duplicates of the 16 ANSI colors. The rest
149             # are RBG and greyscale values.
150             for my $code (0 .. 15) {
151             $ATTRIBUTES{"ansi$code"} = "38;5;$code";
152             $ATTRIBUTES{"on_ansi$code"} = "48;5;$code";
153             }
154              
155             # 256-color RGB colors. Red, green, and blue can each be values 0 through 5,
156             # and the resulting 216 colors start with color 16.
157             for my $r (0 .. 5) {
158             for my $g (0 .. 5) {
159             for my $b (0 .. 5) {
160             my $code = 16 + (6 * 6 * $r) + (6 * $g) + $b;
161             $ATTRIBUTES{"rgb$r$g$b"} = "38;5;$code";
162             $ATTRIBUTES{"on_rgb$r$g$b"} = "48;5;$code";
163             }
164             }
165             }
166              
167             # The last 256-color codes are 24 shades of grey.
168             for my $n (0 .. 23) {
169             my $code = $n + 232;
170             $ATTRIBUTES{"grey$n"} = "38;5;$code";
171             $ATTRIBUTES{"on_grey$n"} = "48;5;$code";
172             }
173              
174             # Reverse lookup. Alphabetically first name for a sequence is preferred.
175             our %ATTRIBUTES_R;
176             for my $attr (reverse sort keys %ATTRIBUTES) {
177             $ATTRIBUTES_R{ $ATTRIBUTES{$attr} } = $attr;
178             }
179              
180             # Provide ansiN names for all 256 characters to provide a convenient flat
181             # namespace if one doesn't want to mess with the RGB and greyscale naming. Do
182             # this after creating %ATTRIBUTES_R since we want to use the canonical names
183             # when reversing a color.
184             for my $code (16 .. 255) {
185             $ATTRIBUTES{"ansi$code"} = "38;5;$code";
186             $ATTRIBUTES{"on_ansi$code"} = "48;5;$code";
187             }
188              
189             # Import any custom colors set in the environment.
190             our %ALIASES;
191             if (exists $ENV{ANSI_COLORS_ALIASES}) {
192             my $spec = $ENV{ANSI_COLORS_ALIASES};
193             $spec =~ s{\s+}{}xmsg;
194              
195             # Error reporting here is an interesting question. Use warn rather than
196             # carp because carp would report the line of the use or require, which
197             # doesn't help anyone understand what's going on, whereas seeing this code
198             # will be more helpful.
199             ## no critic (ErrorHandling::RequireCarping)
200             for my $definition (split m{,}xms, $spec) {
201             my ($new, $old) = split m{=}xms, $definition, 2;
202             if (!$new || !$old) {
203             warn qq{Bad color mapping "$definition"};
204             } else {
205             my $result = eval { coloralias($new, $old) };
206             if (!$result) {
207             my $error = $@;
208             $error =~ s{ [ ] at [ ] .* }{}xms;
209             warn qq{$error in "$definition"};
210             }
211             }
212             }
213             }
214              
215             # Stores the current color stack maintained by PUSHCOLOR and POPCOLOR. This
216             # is global and therefore not threadsafe.
217             our @COLORSTACK;
218              
219             ##############################################################################
220             # Helper functions
221             ##############################################################################
222              
223             # Stub to load the Carp module on demand.
224             sub croak {
225 29     29 0 34 my (@args) = @_;
226 29         131 require Carp;
227 29         2943 Carp::croak(@args);
228             }
229              
230             ##############################################################################
231             # Implementation (constant form)
232             ##############################################################################
233              
234             # Time to have fun! We now want to define the constant subs, which are named
235             # the same as the attributes above but in all caps. Each constant sub needs
236             # to act differently depending on whether $AUTORESET is set. Without
237             # autoreset:
238             #
239             # BLUE "text\n" ==> "\e[34mtext\n"
240             #
241             # If $AUTORESET is set, we should instead get:
242             #
243             # BLUE "text\n" ==> "\e[34mtext\n\e[0m"
244             #
245             # The sub also needs to handle the case where it has no arguments correctly.
246             # Maintaining all of this as separate subs would be a major nightmare, as well
247             # as duplicate the %ATTRIBUTES hash, so instead we define an AUTOLOAD sub to
248             # define the constant subs on demand. To do that, we check the name of the
249             # called sub against the list of attributes, and if it's an all-caps version
250             # of one of them, we define the sub on the fly and then run it.
251             #
252             # If the environment variable ANSI_COLORS_DISABLED is set to a true value,
253             # just return the arguments without adding any escape sequences. This is to
254             # make it easier to write scripts that also work on systems without any ANSI
255             # support, like Windows consoles.
256             #
257             # Avoid using character classes like [:upper:] and \w here, since they load
258             # Unicode character tables and consume a ton of memory. All of our constants
259             # only use ASCII characters.
260             #
261             ## no critic (ClassHierarchies::ProhibitAutoloading)
262             ## no critic (Subroutines::RequireArgUnpacking)
263             ## no critic (RegularExpressions::ProhibitEnumeratedClasses)
264             sub AUTOLOAD {
265 29     29   75304 my ($sub, $attr) = $AUTOLOAD =~ m{
266             \A ( [a-zA-Z0-9:]* :: ([A-Z0-9_]+) ) \z
267             }xms;
268              
269             # Check if we were called with something that doesn't look like an
270             # attribute.
271 29 100 100     177 if (!($attr && defined($ATTRIBUTES{ lc $attr }))) {
272 3         9 croak("undefined subroutine &$AUTOLOAD called");
273             }
274              
275             # If colors are disabled, just return the input. Do this without
276             # installing a sub for (marginal, unbenchmarked) speed.
277 26 100       55 if ($ENV{ANSI_COLORS_DISABLED}) {
278 1         6 return join(q{}, @_);
279             }
280              
281             # We've untainted the name of the sub.
282 25         41 $AUTOLOAD = $sub;
283              
284             # Figure out the ANSI string to set the desired attribute.
285 25         50 my $escape = "\e[" . $ATTRIBUTES{ lc $attr } . 'm';
286              
287             # Save the current value of $@. We can't just use local since we want to
288             # restore it before dispatching to the newly-created sub. (The caller may
289             # be colorizing output that includes $@.)
290 25         28 my $eval_err = $@;
291              
292             # Generate the constant sub, which should still recognize some of our
293             # package variables. Use string eval to avoid a dependency on
294             # Sub::Install, even though it makes it somewhat less readable.
295             ## no critic (BuiltinFunctions::ProhibitStringyEval)
296             ## no critic (ValuesAndExpressions::ProhibitImplicitNewlines)
297 25 100 100 32 0 3560 my $eval_result = eval qq{
  32 100 100 27 0 207  
  4 100 100 8 0 18  
  6 100 100 13 0 16  
  6 100 100 13 0 31  
  16 100 100 9 0 114  
  27 100 100 7 0 604  
  3 100 100 8 0 13  
  3 100 100 7 0 12  
  4 100 100 10 0 35  
  17 100 100 10 0 121  
  8 100 100 32 0 70  
  1 100 100 6 0 6  
  1 100 100 6 0 4  
  1 100 100     7  
  5 100 100     25  
  13 100 100     92  
  2 100 100     12  
  2 100 100     7  
  2 100 100     10  
  7 100 100     38  
  13 100 100     92  
  2 100 100     11  
  2 100 100     7  
  2 100 100     11  
  7 100 100     45  
  9 100 100     64  
  1 100 100     5  
  1 100       4  
  1 100       7  
  6 100       47  
  7 100       53  
  1 100       5  
  1 100       5  
  1 100       6  
  4 100       20  
  8 100       53  
  1 100       5  
  1 100       4  
  1 100       7  
  5 100       27  
  7 100       60  
  1         8  
  1         4  
  1         8  
  4         25  
  10         77  
  1         5  
  1         5  
  1         7  
  7         64  
  10         111  
  1         5  
  1         5  
  1         6  
  7         49  
  32         188  
  1         5  
  1         4  
  2         12  
  28         149  
  6         41  
  1         5  
  1         3  
  1         5  
  3         12  
  6         40  
  1         4  
  1         3  
  1         4  
  3         11  
298             sub $AUTOLOAD {
299             if (\$ENV{ANSI_COLORS_DISABLED}) {
300             return join(q{}, \@_);
301             } elsif (\$AUTOLOCAL && \@_) {
302             return PUSHCOLOR('$escape') . join(q{}, \@_) . POPCOLOR;
303             } elsif (\$AUTORESET && \@_) {
304             return '$escape' . join(q{}, \@_) . "\e[0m";
305             } else {
306             return '$escape' . join(q{}, \@_);
307             }
308             }
309             1;
310             };
311              
312             # Failure is an internal error, not a problem with the caller.
313             ## no critic (ErrorHandling::RequireCarping)
314 25 100       104 if (!$eval_result) {
315 1         9 die "failed to generate constant $attr: $@";
316             }
317              
318             # Restore $@.
319             ## no critic (Variables::RequireLocalizedPunctuationVars)
320 24         21 $@ = $eval_err;
321              
322             # Dispatch to the newly-created sub.
323             ## no critic (References::ProhibitDoubleSigils)
324 24         441 goto &$AUTOLOAD;
325             }
326             ## use critic
327              
328             # Append a new color to the top of the color stack and return the top of
329             # the stack.
330             #
331             # $text - Any text we're applying colors to, with color escapes prepended
332             #
333             # Returns: The text passed in
334             sub PUSHCOLOR {
335 32     32 0 47 my (@text) = @_;
336 32         55 my $text = join(q{}, @text);
337              
338             # Extract any number of color-setting escape sequences from the start of
339             # the string.
340 32         133 my ($color) = $text =~ m{ \A ( (?:\e\[ [\d;]+ m)+ ) }xms;
341              
342             # If we already have a stack, append these escapes to the set from the top
343             # of the stack. This way, each position in the stack stores the complete
344             # enabled colors for that stage, at the cost of some potential
345             # inefficiency.
346 32 100       60 if (@COLORSTACK) {
347 8         15 $color = $COLORSTACK[-1] . $color;
348             }
349              
350             # Push the color onto the stack.
351 32         42 push(@COLORSTACK, $color);
352 32         127 return $text;
353             }
354              
355             # Pop the color stack and return the new top of the stack (or reset, if
356             # the stack is empty).
357             #
358             # @text - Any text we're applying colors to
359             #
360             # Returns: The concatenation of @text prepended with the new stack color
361             sub POPCOLOR {
362 32     32 0 41 my (@text) = @_;
363 32         33 pop(@COLORSTACK);
364 32 100       55 if (@COLORSTACK) {
365 8         34 return $COLORSTACK[-1] . join(q{}, @text);
366             } else {
367 24         421 return RESET(@text);
368             }
369             }
370              
371             # Surround arguments with a push and a pop. The effect will be to reset the
372             # colors to whatever was on the color stack before this sequence of colors was
373             # applied.
374             #
375             # @text - Any text we're applying colors to
376             #
377             # Returns: The concatenation of the text and the proper color reset sequence.
378             sub LOCALCOLOR {
379 3     3 0 5 my (@text) = @_;
380 3         8 return PUSHCOLOR(join(q{}, @text)) . POPCOLOR();
381             }
382              
383             ##############################################################################
384             # Implementation (attribute string form)
385             ##############################################################################
386              
387             # Return the escape code for a given set of color attributes.
388             #
389             # @codes - A list of possibly space-separated color attributes
390             #
391             # Returns: The escape sequence setting those color attributes
392             # undef if no escape sequences were given
393             # Throws: Text exception for any invalid attribute
394             sub color {
395 44     44 1 1918 my (@codes) = @_;
396 44         69 @codes = map { split } @codes;
  51         138  
397              
398             # Return the empty string if colors are disabled.
399 44 100       157 if ($ENV{ANSI_COLORS_DISABLED}) {
400 1         6 return q{};
401             }
402              
403             # Build the attribute string from semicolon-separated numbers.
404 43         47 my $attribute = q{};
405 43         54 for my $code (@codes) {
406 56         60 $code = lc($code);
407 56 100       111 if (defined($ATTRIBUTES{$code})) {
    100          
408 42         70 $attribute .= $ATTRIBUTES{$code} . q{;};
409             } elsif (defined($ALIASES{$code})) {
410 3         6 $attribute .= $ALIASES{$code} . q{;};
411             } else {
412 11         29 croak("Invalid attribute name $code");
413             }
414             }
415              
416             # We added one too many semicolons for simplicity. Remove the last one.
417 32         42 chop($attribute);
418              
419             # Return undef if there were no attributes.
420 32 100       119 return ($attribute ne q{}) ? "\e[${attribute}m" : undef;
421             }
422              
423             # Return a list of named color attributes for a given set of escape codes.
424             # Escape sequences can be given with or without enclosing "\e[" and "m". The
425             # empty escape sequence '' or "\e[m" gives an empty list of attrs.
426             #
427             # There is one special case. 256-color codes start with 38 or 48, followed by
428             # a 5 and then the 256-color code.
429             #
430             # @escapes - A list of escape sequences or escape sequence numbers
431             #
432             # Returns: An array of attribute names corresponding to those sequences
433             # Throws: Text exceptions on invalid escape sequences or unknown colors
434             sub uncolor {
435 19     19 1 6119 my (@escapes) = @_;
436 19         18 my (@nums, @result);
437              
438             # Walk the list of escapes and build a list of attribute numbers.
439 19         28 for my $escape (@escapes) {
440 22         61 $escape =~ s{ \A \e\[ }{}xms;
441 22         44 $escape =~ s{ m \z } {}xms;
442 22         79 my ($attrs) = $escape =~ m{ \A ((?:\d+;)* \d*) \z }xms;
443 22 100       46 if (!defined($attrs)) {
444 1         4 croak("Bad escape sequence $escape");
445             }
446              
447             # Pull off 256-color codes (38;5;n or 48;5;n) as a unit.
448 21         68 push(@nums, $attrs =~ m{ ( 0*[34]8;0*5;\d+ | \d+ ) (?: ; | \z ) }xmsg);
449             }
450              
451             # Now, walk the list of numbers and convert them to attribute names.
452             # Strip leading zeroes from any of the numbers. (xterm, at least, allows
453             # leading zeroes to be added to any number in an escape sequence.)
454 18         21 for my $num (@nums) {
455 18         33 $num =~ s{ ( \A | ; ) 0+ (\d) }{$1$2}xmsg;
456 18         26 my $name = $ATTRIBUTES_R{$num};
457 18 100       31 if (!defined($name)) {
458 9         21 croak("No name for escape sequence $num");
459             }
460 9         12 push(@result, $name);
461             }
462              
463             # Return the attribute names.
464 9         45 return @result;
465             }
466              
467             # Given a string and a set of attributes, returns the string surrounded by
468             # escape codes to set those attributes and then clear them at the end of the
469             # string. The attributes can be given either as an array ref as the first
470             # argument or as a list as the second and subsequent arguments.
471             #
472             # If $EACHLINE is set, insert a reset before each occurrence of the string
473             # $EACHLINE and the starting attribute code after the string $EACHLINE, so
474             # that no attribute crosses line delimiters (this is often desirable if the
475             # output is to be piped to a pager or some other program).
476             #
477             # $first - An anonymous array of attributes or the text to color
478             # @rest - The text to color or the list of attributes
479             #
480             # Returns: The text, concatenated if necessary, surrounded by escapes to set
481             # the desired colors and reset them afterwards
482             # Throws: Text exception on invalid attributes
483             sub colored {
484 16     16 1 2479 my ($first, @rest) = @_;
485 16         18 my ($string, @codes);
486 16 100 100     71 if (ref($first) && ref($first) eq 'ARRAY') {
487 2         20 @codes = @{$first};
  2         8  
488 2         4 $string = join(q{}, @rest);
489             } else {
490 14         17 $string = $first;
491 14         22 @codes = @rest;
492             }
493              
494             # Return the string unmolested if colors are disabled.
495 16 100       40 if ($ENV{ANSI_COLORS_DISABLED}) {
496 1         4 return $string;
497             }
498              
499             # Find the attribute string for our colors.
500 15         25 my $attr = color(@codes);
501              
502             # If $EACHLINE is defined, split the string on line boundaries, suppress
503             # empty segments, and then colorize each of the line sections.
504 14 100       23 if (defined($EACHLINE)) {
505 18 100       41 my @text = map { ($_ ne $EACHLINE) ? $attr . $_ . "\e[0m" : $_ }
506 7         66 grep { length > 0 }
  22         29  
507             split(m{ (\Q$EACHLINE\E) }xms, $string);
508 7         40 return join(q{}, @text);
509             } else {
510 7         26 return $attr . $string . "\e[0m";
511             }
512             }
513              
514             # Define a new color alias, or return the value of an existing alias.
515             #
516             # $alias - The color alias to define
517             # $color - The standard color the alias will correspond to (optional)
518             #
519             # Returns: The standard color value of the alias
520             # undef if one argument was given and the alias was not recognized
521             # Throws: Text exceptions for invalid alias names, attempts to use a
522             # standard color name as an alias, or an unknown standard color name
523             sub coloralias {
524 10     10 1 3626 my ($alias, $color) = @_;
525 10 100       27 if (!defined($color)) {
526 3 100       6 if (!exists $ALIASES{$alias}) {
527 1         3 return;
528             } else {
529 2         9 return $ATTRIBUTES_R{ $ALIASES{$alias} };
530             }
531             }
532              
533             # Avoid \w here to not load Unicode character tables, which increases the
534             # memory footprint of this module considerably.
535             #
536             ## no critic (RegularExpressions::ProhibitEnumeratedClasses)
537 7 100       40 if ($alias !~ m{ \A [a-zA-Z0-9._-]+ \z }xms) {
    100          
    100          
538 2         7 croak(qq{Invalid alias name "$alias"});
539             } elsif ($ATTRIBUTES{$alias}) {
540 1         4 croak(qq{Cannot alias standard color "$alias"});
541             } elsif (!exists $ATTRIBUTES{$color}) {
542 2         8 croak(qq{Invalid attribute name "$color"});
543             }
544             ## use critic
545              
546             # Set the alias and return.
547 2         6 $ALIASES{$alias} = $ATTRIBUTES{$color};
548 2         8 return $color;
549             }
550              
551             # Given a string, strip the ANSI color codes out of that string and return the
552             # result. This removes only ANSI color codes, not movement codes and other
553             # escape sequences.
554             #
555             # @string - The list of strings to sanitize
556             #
557             # Returns: (array) The strings stripped of ANSI color escape sequences
558             # (scalar) The same, concatenated
559             sub colorstrip {
560 5     5 1 10 my (@string) = @_;
561 5         8 for my $string (@string) {
562 11         34 $string =~ s{ \e\[ [\d;]* m }{}xmsg;
563             }
564 5 100       30 return wantarray ? @string : join(q{}, @string);
565             }
566              
567             # Given a list of color attributes (arguments for color, for instance), return
568             # true if they're all valid or false if any of them are invalid.
569             #
570             # @codes - A list of color attributes, possibly space-separated
571             #
572             # Returns: True if all the attributes are valid, false otherwise.
573             sub colorvalid {
574 17     17 1 6723 my (@codes) = @_;
575 17         26 @codes = map { split(q{ }, lc) } @codes;
  18         55  
576 17         25 for my $code (@codes) {
577 22 100 100     94 if (!(defined($ATTRIBUTES{$code}) || defined($ALIASES{$code}))) {
578 9         29 return;
579             }
580             }
581 8         32 return 1;
582             }
583              
584             ##############################################################################
585             # Module return value and documentation
586             ##############################################################################
587              
588             # Ensure we evaluate to true.
589             1;
590             __END__