#!/usr/bin/perl

#     cyr -- Setup Cyrillic on Linux console
#     Copyright (C) 1999,2000,2001 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 $STD_FONTDIR="/usr/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 ($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
-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.
--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).
    --ttys=\'TTY1 TTY2 ... \'    Setup Cyrillic in these consoles.
                               Current default: $LTTY.

Supported KEYBOARDs are bg_bds, bg_phon, by, mk, ru, ru_ms, sr and ua.

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 (/^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 (! /^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$|^ru$|^ru_ms$|^by$|^mk$|^bg_bds$|^bg_phon$/) {
	&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 ($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") {
    system "consolechars -v -k ${CONSOLETRANSDIR}/cyrillic-graph.fallback.gz".
	" $verbose";
}

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

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

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;
    }
}

system "loadkeys ${KEYMAPDIR}/$LAYOUT-$ENCODING.kmap $verbose";
if ("$OPTIONS" ne "") {
    system "loadkeys ${KEYMAPDIR}/option-clear.kmap $verbose";
    for my $op (split ' ', $OPTIONS) {
	system "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 loadfont {
    if ($t eq "kbd") {
	system ("setfont -v $_[0] ${verbose}");
    } else {
	system ("consolechars -v -f $_[0] ${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 (/^arab$/) {
	    if($t eq "kbd") { &err("cyr: The style ${style} is not possible on your system.\n"); }
	    if($size eq "8") {$size = "08";}
	    $font = "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 = "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$/) {
	    system "setfont -m ${CONSOLETRANSDIR}/cp1251.screenmap $verbose";
	} elsif (/^ibm866$/) {
	} elsif (/^iso-8859-5$/) {
	    system "setfont -m ${CONSOLETRANSDIR}/iso-8859-5.screenmap $verbose";
	} elsif (/^koi8-r$/) {
	    system "setfont -m ${CONSOLETRANSDIR}/koi8-r.screenmap $verbose";
	} elsif (/^koi8-u$/) {
	    system "setfont -m ${CONSOLETRANSDIR}/koi8-u.screenmap $verbose";
	} elsif (/^mac-cyrillic$/) {
	    system "setfont -m ${CONSOLETRANSDIR}/mac-cyrillic.screenmap $verbose";
	} elsif (/^mik$/) {
	    system "setfont -m ${CONSOLETRANSDIR}/mik.screenmap $verbose";
	} elsif (/^unicode$/) {
	} else {
	    &err("cyr: Unknown encoding `$_'.\n");
	}
    }
}

sub console_tools_encoding {
    for ($_[0]) {
	if (/^cp1251$/) {
	    system "consolechars -v --acm ${CONSOLETRANSDIR}/cp1251.acm.gz $verbose";
	} elsif (/^ibm866$/) {
	    system "consolechars -v --acm ${CONSOLETRANSDIR}/cp866.acm.gz $verbose";
	} elsif (/^iso-8859-5$/) {
	    system "consolechars -v --acm ${CONSOLETRANSDIR}/iso8859-5.acm.gz $verbose";
	} elsif (/^koi8-r$/) {
	    system "consolechars -v --acm ${CONSOLETRANSDIR}/koi8-r.acm.gz $verbose";
	} elsif (/^koi8-u$/) {
	    system "consolechars -v --acm ${CONSOLETRANSDIR}/koi8-u.acm.gz $verbose";
	} elsif (/^mac-cyrillic$/) {
	    system "consolechars -v --acm ${CONSOLETRANSDIR}/MacCyrillic.acm.gz $verbose";
	} elsif (/^mik$/) {
	    system "consolechars -v --acm ${CONSOLETRANSDIR}/bulgarian-mic.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;
	}
    }
}

