#!/usr/bin/perl use common::sense; use List::Util qw{ max min }; use List::MoreUtils qw{ all any none }; use FindBin qw{ $RealBin }; use File::Spec qw{ }; use File::Slurp qw{ read_file }; use IO::File qw{ }; state $rgb_pattern = qr{ \A \s* ( \d+ ) \s* ( \d+ ) \s* ( \d+ ) \s* ( \S (?: \S+ | \s\S+ )*+ ) \s* \z }xms; state $rgb_comment = qr{ \A \! }xms; state $rgb_either = qr{ \A (?: $rgb_pattern | $rgb_comment ) }xms; my %rgb; my $rgb_read; my $oscolor; my $name_table; my $color_table; my $oscolor_read; sub first_exist { foreach ( @_ ) { return $_ if -e $_; } return $_[ 0 ]; } sub default_oscolor { return first_exist( qw{ oscolor.c }, File::Spec->catfile( $RealBin, qw{ oscolor.c } ) ); } ## end sub default_oscolor sub default_rgb { return first_exist( qw{ rgb.txt }, File::Spec->catfile( File::Spec->updir, File::Spec->updir, qw{ app rgb rgb.txt } ), File::Spec->catfile( $RealBin, qw{ rgb.txt } ), File::Spec->catfile( $RealBin, File::Spec->updir, File::Spec->updir, qw{ app rgb rgb.txt } ) ); } ## end sub default_rgb sub usage { my ( $exit ) = @_; print { \*STDERR } qq{Usage: $0 [oscolor.c] [rgb.txt]\n}; $exit //= 0; exit $exit; } ## end sub usage sub normalize_name { my ( $name ) = @_; my $index = $name =~ s{(\d+)\z}{}xms ? $1 : $0; $name =~ s{(?<=[[:lower:]])(?=[[:upper:]])}{ }xmsg; $name =~ s{\s+}{ }xmsg; $name = lc $name; return $name, $index; } sub try_process_rgb { my ( $fname, $lines ) = @_; return unless all { $_ =~ $rgb_either } @$lines; my $lnum = 0; foreach my $line ( @$lines ) { ++$lnum; next unless $line =~ $rgb_pattern; my ( $red, $green, $blue, $name ) = ( $1, $2, $3, $4 ); $red += 0; $green += 0; $blue += 0; my $hex = sprintf( q{#%02x%02x%02x}, $red, $green, $blue ); my $record = $rgb{ $name } //= { name => $name, hex => $hex, red => $red, green => $green, blue => $blue, file => $fname, line => $lnum, }; if ( $record->{ hex } ne $hex ) { die qq{Mismatch $name: $record->{file}:$record->{line} $record->{hex} vs $fname:$lnum $hex\n}; } } ## end foreach my $line ( @$lines ) $rgb_read = 1; return 1; } ## end sub try_process_rgb sub try_process_oscolor { my ( $fname, $lines ) = @_; return if $oscolor_read; return if none { m{\bBuiltinColorNames\b}xms } @$lines; return if none { m{\bBuiltinColors\b}xms } @$lines; $oscolor = $lines; $oscolor_read = 1; return 1; } ## end sub try_process_oscolor sub handle_file { my ( $fname ) = @_; my $lines = read_file( $fname, array_ref => 1 ) or die qq{Unable to read: $fname: $!\n}; try_process_rgb( $fname, $lines ) or try_process_oscolor( $fname, $lines ) or die qq{Unrecognized file type: $fname\n}; } ## end sub handle_file sub handle_rgb_file { my ( $fname ) = @_; my $lines = read_file( $fname, array_ref => 1 ) or die qq{Unable to read: $fname: $!\n}; try_process_rgb( $fname, $lines ) or die qq{Not an rgb.txt file: $fname\n}; } ## end sub handle_rgb_file sub handle_oscolor_file { my ( $fname ) = @_; my $lines = read_file( $fname, array_ref => 1 ) or die qq{Unable to read: $fname: $!\n}; try_process_oscolor( $fname, $lines ) or die qq{Not an oscolor file: $fname\n}; } ## end sub handle_oscolor_file sub name_sort { return lc $a cmp lc $b; } sub build_color_table { my $offset = 0; my $name_space = q{ }; my $name_space_indent = q{ }; foreach my $name ( sort name_sort keys %rgb ) { my $rec = $rgb{ $name }; $rec->{ name_offset } = $offset; push @$name_table, $name_space . q{"} . $name . q{\0"} . qq{\n}; my $desc = q[{] . join q{, }, $rec->{ red }, $rec->{ green }, $rec->{ blue }, $offset . q[},]; push @$color_table, sprintf q{ %-28s/* %s */} . qq{\n}, $desc, $name; $offset += 1 + length $name; $name_space = $name_space_indent; } ## end foreach my $name ( sort keys...) } ## end sub build_color_table sub gen_out_file { my $max_line = scalar @$oscolor; my $i = 0; my $out_lines = []; while ( $i < $max_line ) { my $line = $oscolor->[ $i ]; push @$out_lines, $line; ++$i; if ( $line =~ m{\b BuiltinColorNames \s* \[ [\d\s]* \] \s* = \s* \{ }xms ) { push @$out_lines, @$name_table; while ( 1 ) { die q{Could not find end of BuiltinColorNames\n} if $i >= $max_line; last if $oscolor->[ $i ] =~ m( } \s* ; )xms; ++$i; } } elsif ( $line =~ m{\b BuiltinColors \s* \[ [\d\s]* \] \s* = \s* \{ }xms ) { push @$out_lines, @$color_table; while ( 1 ) { die q{Could not find end of BuiltinColors\n} if $i >= $max_line; last if $oscolor->[ $i ] =~ m( } \s* ; )xms; ++$i; } } } print join q{}, @$out_lines; } sub main { foreach my $file ( @_ ) { handle_file( $file ); } handle_rgb_file( default_rgb ) unless $rgb_read; handle_oscolor_file( default_oscolor ) unless $oscolor_read; die qq{No rgb.txt file\n} unless $rgb_read; die qq{No oscolor.c file\n} unless $oscolor_read; build_color_table(); gen_out_file(); return 1; } ## end sub main main( @ARGV );