#!/usr/bin/perl

#     cyr -- Setup Cyrillic on Linux console
#     Copyright (C) 1999,2000,2001,2002,2003,2006 Anton Kirilov Zinoviev

#     This program is free software; you can redistribute it and/or modify
#     it under the terms of the GNU General Public License as published by
#     the Free Software Foundation; either version 2 of the License, or
#     (at your option) any later version.

#     This program is distributed in the hope that it will be useful,
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#     GNU General Public License for more details.

#     If you have not received a copy of the GNU General Public License
#     along with this program, write to the Free Software Foundation, Inc.,
#     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#     Contact the author by e-mail: anton@lml.bas.bg, zinoviev@fmi.uni-sofia.bg

use strict;

my $INSTALLDIR=$0;
$INSTALLDIR =~ s+/[^/]*$++g;
$INSTALLDIR .= "/..";

# $INSTALLDIR="/usr";
# $INSTALLDIR="/usr/local";

my $STDF="/usr/share/consolefonts";
my $ALTSTDF="${INSTALLDIR}/share/consolefonts";
my $FONTDIR="${INSTALLDIR}/share/console-cyrillic";
my $CONSOLETRANSDIR="${INSTALLDIR}/share/console-cyrillic";
my $KEYMAPDIR="${INSTALLDIR}/share/console-cyrillic";

my $MYCONFFILE = "";
if (`whoami` ne "root") {
    $MYCONFFILE = "$ENV{HOME}/.cyr_defaults";
} else {
    $MYCONFFILE = "/etc/console-cyrillic";
}

my $CONFFILE = "";
if (-f "$ENV{HOME}/.cyr_defaults" && `whoami` ne "root") {
    $CONFFILE = "$ENV{HOME}/.cyr_defaults";
} elsif (-f "/etc/console-cyrillic") {
    $CONFFILE = "/etc/console-cyrillic";
}

my $t;
if ( -x "/usr/bin/consolechars" || -x "/bin/consolechars" ) {
    $t="console-tools";
} elsif ( -x "/usr/bin/setfont" || -x "/bin/setfont" ) {
    $t="kbd";
} else {
    &err("cyr: In the system is not installed any of the known\ncyr: console packages. \n");
}

my $verbose='>/dev/null 2>&1';
my $commands='no';

my ($STYLE, $SIZE, $ENCODING, $LAYOUT, $OPTIONS, $FONTFILE, $VTTYS) =
    ("", "", "", "", "", "", "");

if ($CONFFILE) {
    open CONFFILE, $CONFFILE;
    for (<CONFFILE>) {
	if (/^ *\#/) {next;}
	if (/^ *$/) {next;}
	my @words = split;# For compatability with the old (pre 0.7-2) format:
	if ($words[0] =~ /^-/) {
	    my $args = "@words @ARGV";
	    @ARGV = split ' ', $args;
	} elsif ($words[0] eq "options") {
	    shift @words;
	    $OPTIONS = "@words";
	} elsif ($words[0] eq "ttys") {
	    shift @words;
	    $VTTYS = "@words";
	} else {
	    if ($words[2]) {
		&err("cyr: Syntax error in the configuration file:\n".
		     "cyr:    ". $_ ."\n");
	    }
	    if ($words[0] eq "style") { $STYLE = $words[1];}
	    elsif ($words[0] eq "size") { $SIZE = $words[1];}
	    elsif ($words[0] eq "encoding") { $ENCODING = $words[1];}
	    elsif ($words[0] eq "layout") { $LAYOUT = $words[1];}
	    elsif ($words[0] eq "fontfile") { $FONTFILE = $words[1];}
	    else {
		&err("cyr: Syntax error in the configuration file:\n".
		     "cyr:    ". $_ ."\n");
	    }
	}
    }
    close CONFFILE;
}

my ($km_old, $save, $layread, $fontfileread)=("","","no","no");

while ( $ARGV[0] ) {
    my $op = &get_opt;
    for ($op) {
	if (/^-C$|^--copyright$/) {
	    &no_arg;
	    print STDOUT <<EOF;
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with bash interpreter; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
EOF
	    exit;
	} elsif (/^-k$|^--keymap$/) {
	    $km_old = &get_arg;
	} elsif (/^-m$|^--mode$/) {
	    my $arg = &get_arg;
	    if ($arg eq "80x25" || $arg eq "80x30") {
		$SIZE = "16";
	    } elsif ($arg eq "80x28" || $arg eq "80x34") {
		$SIZE = "14";
	    } elsif ($arg eq "80x43"||$arg eq "80x50"||$arg eq "80x60") {
		$SIZE = "8";
	    } else {
		&err("cyr: Unknown video mode $arg.\n");
	    }
	} elsif (/^--font$/) {
	    $FONTFILE = &get_arg;
	    $fontfileread = "yes";
	} elsif (/^-f$|^--size$/) {
	    $SIZE = &get_arg;
	} elsif (/^-s$|^--style$/) {
	    $STYLE = &get_arg;
	} elsif (/^-e$|^--encoding$/) {
	    $ENCODING = &get_arg;
	} elsif (/^--ttys$/) {
	    $VTTYS = &get_arg;
	} elsif (/^-h$|^--help$/) {
	    &no_arg;
	    my $LTTY=`tty`;
	    chomp $LTTY;
	    print STDOUT <<EOF;
cyr -- Setup Cyrillic on Linux console
Usage: cyr [OPTIONS]... [KEYBOARD [KBD_OPTIONS ... ]]

-C, --copyright           display copying conditions and warranty information
-h, --help                display this help and exit
    --save                saves given parameters as default for latter usage
-v, --verbose             verbose mode
    --commands            output the commands cyr executes
-s, --style=FONTSTYLE
-f, --size=FONTSIZE       Posible FONTSTYLEs and FONTSIZEs are:
        styles b, c, lenta, antiq and sans with size 16;
        styles a, alt, uni, iso and dos with sizes 8, 14 and 16;
        style  arab  with sizes 8, 14, 16 and 19;
        style  cage  with sizes 8, 10, 11, 12, 14, 15, 16, 18 and 19;
        style  thin  with sizes 14 and 16;
        style  sarge with size 16;
        style  pln   with size  8;
	styles a-asia, b-asia and antiq-asia with size 16;
        styles ter-CHARSET-FACE with sizes 14 and 16,
          where CHARSET is uni, asia or slav and FACE is norm, bold or framebuf.
--font=FONTFILE           Font file to use.  Overwrites --style and --size.
-e, --encoding=ENCODING   Possible ENCODINGs are:
        utf-8 (unicode), cp1251 (ms-cyr, 1251), ibm866 (dos-cyr-ru, cp866, 866),
        iso-8859-5 (cyrillic), koi8-r, koi8-u, mac-cyrillic (mac-cyr),
        mik (dos-cyr-bg), pt154 (asian-cyr), rk1048.
    --ttys=\'TTY1 TTY2 ... \'    Setup Cyrillic in these consoles.
                               Current default: $LTTY.

Supported KEYBOARDs are bg_bds, bg_phon, by, kaz_alt, kaz_gost, mk, mn,
ru, ru_ms, sr, ua and ua_ms.

Supported KBD_OPTIONS are toggle, ctrl_shift_toggle, caps_toggle,
ctrl_alt_toggle, alt_shift_toggle, right_ctrl_toggle, menu_toggle, lwin_toggle,
rwin_toggle, switch, menu_switch, win_switch, lwin_switch and rwin_switch.

Examples:  cyr mk alt_shift_toggle --style arab -f19 -e iso-8859-5 --save
           cyr ru caps_toggle --style=sans --size 16 -e koi8-r --save
	   cyr bg_bds toggle -s antiq -f16 --encoding=cp1251 --save
These are for the first usage.  Then it is enough just to type
           cyr
EOF
	    exit;
	} elsif (/^--save$/) {
	    &no_arg;
	    $save="yes";
	} elsif (/^-v$|^--verbose$/) {
	    &no_arg;
	    $verbose='';
	} elsif (/^--commands$/) {
	    &no_arg;
	    $commands='yes';
	} elsif (/^arg$/) {
	    my $arg = &get_arg;
	    if ( $LAYOUT eq "" || $layread eq "no") {
		$LAYOUT = $arg;
		$OPTIONS = "";
		$layread = "yes";
	    } else {
		if (-f "${CONSOLETRANSDIR}/option-$arg.kmap") {
		    $OPTIONS = "$OPTIONS $arg";
		} else {
		    &err("cyr: Unknown keyboard option: ${arg}.\n");
		}
	    }
	} else {
	    &err("cyr: Unknown option: $_\n");
	}
    }
}

if(! (`tty` =~ m!/dev/tty[0-9]+|/dev/vc/[0-9]+|/dev/console!)) {
    &err("cyr: This command may be executed only in Linux console.\n");
}

if ($fontfileread eq "no" && ($STYLE ne "" || $SIZE ne "")) {
    $FONTFILE = "";
}

if ($LAYOUT eq "") {
    $LAYOUT = $km_old;
}
if ($LAYOUT eq "") {
    &err("cyr: What keyboard to use?\n");
}
if ($FONTFILE eq "") {
    if ($STYLE eq "") {
	&err("cyr: What font style to use?\n");
    }
    if ($SIZE eq "") {
	&err("cyr: What font size to use?\n");
    }
}
if ($ENCODING eq "") {
    &err("cyr: What encoding to use?\n");
}

$ENCODING =~ tr/A-Z/a-z/;
for ($ENCODING) {
    if(/^cp1251$|^ms-cyr$|^1251$/) {$ENCODING = "cp1251";}
    elsif (/^utf-8$|^unicode$/) {$ENCODING = "unicode";}
    elsif (/^ibm866$|^dos-cyr-ru$|^cp866$|^866$/) {$ENCODING = "ibm866";}
    elsif (/^iso-8859-5$|^cyrillic$/) {$ENCODING = "iso-8859-5";}
    elsif (/^mac-cyr$|^mac-cyrillic$/) {$ENCODING = "mac-cyrillic";}
    elsif (/^mik$|^dos-cyr-bg$/) {$ENCODING = "mik";}
    elsif (/^pt154$|^asian-cyr$/) {$ENCODING = "pt154";}
    elsif (/^rk1048$/) {$ENCODING = "rk1048";}
    elsif (! /^koi8-[ru]$/) {
	&err("cyr: Unknown encoding `$_'.\n");
    }
}

$LAYOUT =~ tr/A-Z/a-z/;
for ($LAYOUT) {
    if(/^bds$/) {$LAYOUT = "bg_bds";}
    elsif (/^phon$/) {$LAYOUT = "bg_phon";}
    elsif (! /^sr$|^ua$|^ua_ms$|^ru$|^ru_ms$|^by$|^mk$|^mn$|^bg_bds$|^bg_phon$|^kaz_alt$|^kaz_gost$/) {
	&err("cyr: Unknown keyboard: $_\n");
    }
}

$STYLE =~ tr/A-Z/a-z/;
for ($STYLE) {
    if(/^1$/) {$STYLE = "a";}
    elsif(/^2$|^3$/) {$STYLE = "alt";}
    elsif(/^4$/) {$STYLE = "arab";}
}

if($VTTYS eq "") {
    $VTTYS = `tty`;
}

for my $console (glob $VTTYS) {
    if (-w $console) {
	open CONSOLE, ">$console";
	print CONSOLE "\x{1b}(K";
	if ($ENCODING eq "unicode") {
	    print CONSOLE "\x{1b}%G";
	} else {
	    print CONSOLE "\x{1b}%@";
	}
	close CONSOLE;
    }
}

if ($FONTFILE eq "") {
    &stylesize($STYLE, $SIZE);
} else {
    &loadfont($FONTFILE);
}

if($t eq "kbd") {
    &kbd_encoding($ENCODING);
} elsif ($t eq "console-tools") {
    &console_tools_encoding($ENCODING);
} else {&err("??????????????????\n")}

if($t eq "console-tools") {
    &execute( "consolechars -v -k ${CONSOLETRANSDIR}/cyrillic-graph.fallback.gz".
	" $verbose");
}

if ($ENCODING eq "unicode") {
    &execute ("kbd_mode -u");
} else {
    &execute ("kbd_mode -a");
}

&execute ("loadkeys ${KEYMAPDIR}/$LAYOUT-$ENCODING.kmap $verbose");
if ("$OPTIONS" ne "") {
    &execute ("loadkeys ${KEYMAPDIR}/option-clear.kmap $verbose");
    for my $op (split ' ', $OPTIONS) {
	&execute ("loadkeys ${KEYMAPDIR}/option-${op}.kmap $verbose");
    }
}

if ($save eq "yes") {
    if (`whoami` eq "root") {
	print STDERR "Warning: root may not use the option --save.\n";
    } else {
	if ($verbose eq "") {
	    print STDERR "Saving default options for the next time.\n";
	}
	open CONFFILE, ">$MYCONFFILE";
	if ($FONTFILE eq "") {
	    print CONFFILE "style $STYLE\n";
	    print CONFFILE "size $SIZE\n";
	} else {
	    print CONFFILE "fontfile $FONTFILE\n";
	}
	print CONFFILE "encoding $ENCODING\n";
	print CONFFILE "layout $LAYOUT\n";
	if ($OPTIONS) {print CONFFILE "options $OPTIONS\n";}
	close CONFFILE;
    }
}

sub execute {
    printf STDERR "%s\n", $_[0] if ($commands eq 'yes');
    system ($_[0]);
}

sub loadfont {
    if ($t eq "kbd") {
	for my $tty (glob $VTTYS) {
	    &execute ("setfont -v $_[0] -C ${tty} ${verbose}");
	}
    } else {
	for my $tty (glob $VTTYS) {
		&execute ("consolechars -v -f $_[0] --tty=${tty} ${verbose}");
	}
    }
}

sub stylesize {
    my ($style, $size) = @_;
    my $font;
    for ($style){
	if (/^a$/) {$font = "${FONTDIR}/Cyr_a8x${size}.psf";}
	elsif (/^pln$/) {$font = "${FONTDIR}/UniCyrX-pln-8x${size}.psf";}
	elsif (/^ibm$/) {$font = "${FONTDIR}/UniCyrX-ibm-8x${size}.psf";}
	elsif (/^b$/) {$font = "${FONTDIR}/alt-b-8x${size}.psf";}
	elsif (/^c$/) {$font = "${FONTDIR}/alt-c-8x${size}.psf";}
	elsif (/^uni$/) {$font = "${FONTDIR}/UniCyr_8x${size}.psf";}
	elsif (/^alt$/) {$font = "${FONTDIR}/alt_8x${size}.psf";}
	elsif (/^dos$/) {$font = "${FONTDIR}/866_8x${size}.psf";}
	elsif (/^antiq$/) {$font = "${FONTDIR}/alt-antiq-8x${size}.psf";}
	elsif (/^lenta$/) {$font = "${FONTDIR}/UniCyr-lenta-8x${size}.psf";}
	elsif (/^sans$/) {$font = "${FONTDIR}/UniCyr-sans-8x${size}.psf";}
	elsif (/^cage$/) {$font = "${FONTDIR}/CAG-8x${size}.psf";}
	elsif (/^thin$/) {$font = "${FONTDIR}/thin-${size}.psf";}
	elsif (/^sarge$/) {$font = "${FONTDIR}/sarge-${size}.psf";}
	elsif (/^a-asia$/) {$font = "${FONTDIR}/pt154_a8x${size}.psf";}
	elsif (/^b-asia$/) {$font = "${FONTDIR}/pt154_b8x${size}.psf";}
	elsif (/^antiq-asia$/) {$font = "${FONTDIR}/pt154_antiq8x${size}.psf";}
	elsif (/^ter-uni-norm$/) {
	    $font = "${ALTSTDF}/Uni3-Terminus${size}.psf.gz";
	} elsif (/^ter-uni-bold$/) {
	    $font = "${ALTSTDF}/Uni3-TerminusBoldVGA${size}.psf.gz";
	} elsif (/^ter-uni-framebuf$/) {
	    $font = "${ALTSTDF}/Uni3-TerminusBold${size}.psf.gz";
	} elsif (/^ter-slav-norm$/) {
	    $font = "${ALTSTDF}/CyrSlav-Terminus${size}.psf.gz";
	} elsif (/^ter-slav-bold$/) {
	    $font = "${ALTSTDF}/CyrSlav-TerminusBoldVGA${size}.psf.gz";
	} elsif (/^ter-slav-framebuf$/) {
	    $font = "${ALTSTDF}/CyrSlav-TerminusBold${size}.psf.gz";
	} elsif (/^ter-asia-norm$/) {
	    $font = "${ALTSTDF}/CyrAsia-Terminus${size}.psf.gz";
	} elsif (/^ter-asia-bold$/) {
	    $font = "${ALTSTDF}/CyrAsia-TerminusBoldVGA${size}.psf.gz";
	} elsif (/^ter-asia-framebuf$/) {
	    $font = "${ALTSTDF}/CyrAsia-TerminusBold${size}.psf.gz";
	} elsif (/^arab$/) {
#	    if($t eq "kbd") { &err("cyr: The style ${style} is not possible on your system.\n"); }
	    if($size eq "8") {$size = "08";}
	    $font = "${STDF}/LatArCyrHeb-${size}.psf.gz";}
	elsif (/^iso$/) {
#	    if($t eq "kbd") { &err("cyr: The style ${style} is not possible on your system.\n"); }
	    if($size eq "8") {$size = "08";}
	    $font = "${STDF}/iso05.f${size}.psf.gz";}
	else { &err("cyr: Font style `$_' is not known.\n"); }
    }
    if (-f $font) {
	&loadfont($font);
    } else {
	&err("cyr: Font size ${size} is not possible with style `${style}'.\n");
    }
}

sub kbd_encoding {
    for ($_[0]) {
	if (/^cp1251$/) {
	    &execute ("setfont -m ${CONSOLETRANSDIR}/cp1251_to_uni.trans $verbose");
	} elsif (/^ibm866$/) {
	    &execute ("setfont -m ${CONSOLETRANSDIR}/cp866_to_uni.trans $verbose");
	} elsif (/^iso-8859-5$/) {
	    &execute ("setfont -m ${CONSOLETRANSDIR}/8859-5_to_uni.trans $verbose");
	} elsif (/^koi8-r$/) {
	    &execute ("setfont -m ${CONSOLETRANSDIR}/koi8-r_to_uni.trans $verbose");
	} elsif (/^koi8-u$/) {
	    &execute ("setfont -m ${CONSOLETRANSDIR}/koi8-u_to_uni.trans $verbose");
	} elsif (/^mac-cyrillic$/) {
	    &execute ("setfont -m ${CONSOLETRANSDIR}/mac-cyrillic_to_uni.trans $verbose");
	} elsif (/^mik$/) {
	    &execute ("setfont -m ${CONSOLETRANSDIR}/mik_to_uni.trans $verbose");
	} elsif (/^pt154$/) {
	    &execute ("setfont -m ${CONSOLETRANSDIR}/pt154_to_uni.trans $verbose");
	} elsif (/^rk1048$/) {
	    &execute ("setfont -m ${CONSOLETRANSDIR}/rk1048_to_uni.trans $verbose");
	} elsif (/^unicode$/) {
	} else {
	    &err("cyr: Unknown encoding `$_'.\n");
	}
    }
}

sub console_tools_encoding {
    for ($_[0]) {
	if (/^cp1251$/) {
	    &execute ("consolechars -v --acm ${CONSOLETRANSDIR}/cp1251.acm.gz $verbose");
	} elsif (/^ibm866$/) {
	    &execute ("consolechars -v --acm ${CONSOLETRANSDIR}/cp866.acm.gz $verbose");
	} elsif (/^iso-8859-5$/) {
	    &execute ("consolechars -v --acm ${CONSOLETRANSDIR}/iso8859-5.acm.gz $verbose");
	} elsif (/^koi8-r$/) {
	    &execute ("consolechars -v --acm ${CONSOLETRANSDIR}/koi8-r.acm.gz $verbose");
	} elsif (/^koi8-u$/) {
	    &execute ("consolechars -v --acm ${CONSOLETRANSDIR}/koi8-u.acm.gz $verbose");
	} elsif (/^mac-cyrillic$/) {
	    &execute ("consolechars -v --acm ${CONSOLETRANSDIR}/MacCyrillic.acm.gz $verbose");
	} elsif (/^mik$/) {
	    &execute ("consolechars -v --acm ${CONSOLETRANSDIR}/bulgarian-mic.acm.gz $verbose");
	} elsif (/^pt154$/) {
	    &execute ("consolechars -v --acm ${CONSOLETRANSDIR}/pt154.acm.gz $verbose");
	} elsif (/^rk1048$/) {
	    &execute ("consolechars -v --acm ${CONSOLETRANSDIR}/rk1048.acm.gz $verbose");
	} elsif (/^unicode$/) {
	} else {
	    &err("cyr: Unknown encoding `$_'.\n");
	}
    }
}

sub err {
    printf STDERR $_[0];
    printf STDERR "cyr: Try `cyr --help for more information.\n";
    exit 2;
}

my $op;

sub get_opt {
    for (my $arg=$ARGV[0]) {
	if (/^-$/) {
	    $op = "arg";
	} elsif (/^--[^=]*/ || /^-./) {
	    $op = $&;
	} else {
	    $op = "arg";
	}
    }
    return $op;
}

sub get_arg {
    for ($ARGV[0]) {
	shift @ARGV;
	if (/^-$/) {
	    return "-";
	} elsif (/^--[^=]*=(.*)/ || /^-[^-](..*)/) {
	    if ($1) { return $1; }
	    &err("cyr: Option ". $op ." requires an argument.\n");
	} elsif (/^-/) {
	    my $arg = shift @ARGV;
	    if ($arg) {
		return $arg;
	    } else {
		&err("cyr: Option ". $op ." requires an argument.\n");
	    }
	} else {
	    return $_;
	}
    }
}

sub no_arg {
    for ($ARGV[0]) {
	if (/^--.*=/) {
	    &err("cyr: Option `${op}' doesn't allow an argument.\n");
	} elsif (/^-[^-](..*)/) {
	    $ARGV[0] = "-$1";
	} else {
	    shift @ARGV;
	}
    }
}

