
/*
 Copyright (c)2001 Daniel Mealha Cabrita (dancab@iname.com)
 This code is under the terms of GNU General Public License
 */

#include <string.h>
#include <stdlib.h>

/* given direction: 0 - koi->win  1 - win->koi */
/* writes the converted string over the old one */
/* always returns given_string */
char *convert_between_koi8_win1251(char *given_string, int given_direction)
{
    /* koi8, win1251 */
    const char filter_table[]="\xa3\xb8\xb3\xa8\xc0\xfe\xc1\xe0\xc2\xe1\xc3\xf6\xc4\xe4\xc5\xe5\xc6\xf4\xc7\xe3\xc8\xf5\xc9\xe8\xca\xe9\xcb\xea\xcc\xeb\xcd\xec\xce\xed\xcf\xee\xd0\xef\xd1\xff\xd2\xf0\xd3\xf1\xd4\xf2\xd5\xf3\xd6\xe6\xd7\xe2\xd8\xfc\xd9\xfb\xda\xe7\xdb\xf8\xdc\xfd\xdd\xf9\xde\xf7\xdf\xfa\xe0\xde\xe1\xc0\xe2\xc1\xe3\xd6\xe4\xc4\xe5\xc5\xe6\xd4\xe7\xc3\xe8\xd5\xe9\xc8\xea\xc9\xeb\xca\xec\xcb\xed\xcc\xee\xcd\xef\xce\xf0\xcf\xf1\xdf\xf2\xd0\xf3\xd1\xf4\xd2\xf5\xd3\xf6\xc6\xf7\xc3\xf8\xdc\xf9\xdb\xfa\xc7\xfb\xd8\xfc\xdd\xfd\xd9\xfe\xd7\xff\xda\0\0";
    char *current_point;
    char current_char;
    char from_char;
    int my_loop, from_offset, to_offset;
    int char_found;

    if(given_direction){ /* win->koi */
        from_offset=1;
        to_offset=0;
    }else{
        from_offset=0;    /* koi->win */
        to_offset=1;
    }

    current_point=(char *)given_string;

    while(current_char=*current_point){
        my_loop=0;
        char_found=0;
        while((from_char=filter_table[my_loop+from_offset]) && !char_found){
            if(current_char==from_char){
                *current_point=filter_table[my_loop+to_offset];
                char_found=1;
            }
            my_loop+=2;
        }
        current_point++;
    }

    return(given_string);
}

/* transliterate koi8 to latin characters */
/* returned string must be free()'ed later */
const char *transliterate_from_koi8(const char *original_string)
{
    int original_size;
    char *new_buff;

    original_size=strlen(original_string);
    /* allocate a buffer 4x the original koi8 string size */
    if(new_buff=(char *)malloc(((original_size<<2)+1)*sizeof(char))){
        const char *read_from;
        char *write_to;

        read_from=original_string;
        write_to=new_buff;
        while(*read_from){
            switch(*read_from){
                /* 'yo' group */
            case '\xa3':
                *write_to++='j';
                *write_to++='o';
                break;
            case '\xb3':
                *write_to++='J';
                *write_to++='O';
                break;
                /* non-capital letters group */
            case '\xc0':
                *write_to++='j';
                *write_to++='u';
                break;
            case '\xc1':
                *write_to++='a';
                break;
            case '\xc2':
                *write_to++='b';
                break;
            case '\xc3':
                *write_to++='c';
                break;
            case '\xc4':
                *write_to++='d';
                break;
            case '\xc5':
                *write_to++='e';
                break;
            case '\xc6':
                *write_to++='f';
                break;
            case '\xc7':
                *write_to++='g';
                break;
            case '\xc8':
                *write_to++='k';
                *write_to++='h';
                break;
            case '\xc9':
                *write_to++='i';
                break;
            case '\xca':
                *write_to++='j';
                break;
            case '\xcb':
                *write_to++='k';
                break;
            case '\xcc':
                *write_to++='l';
                break;
            case '\xcd':
                *write_to++='m';
                break;
            case '\xce':
                *write_to++='n';
                break;
            case '\xcf':
                *write_to++='o';
                break;
            case '\xd0':
                *write_to++='p';
                break;
            case '\xd1':
                *write_to++='j';
                *write_to++='a';
                break;
            case '\xd2':
                *write_to++='r';
                break;
            case '\xd3':
                *write_to++='s';
                break;
            case '\xd4':
                *write_to++='t';
                break;
            case '\xd5':
                *write_to++='u';
                break;
            case '\xd6':
                *write_to++='z';
                *write_to++='h';
                break;
            case '\xd7':
                *write_to++='v';
                break;
            case '\xd8':
                *write_to++='\'';
                break;
            case '\xd9':
                *write_to++='y'; /* bI */
                break;
            case '\xda':
                *write_to++='z';
                break;
            case '\xdb':
                *write_to++='s';
                *write_to++='h';
                break;
            case '\xdc':
                *write_to++='`';
                *write_to++='e';
                break;
            case '\xdd':
                *write_to++='s';
                *write_to++='h';
                *write_to++='c';
                *write_to++='h';
                break;
            case '\xde':
                *write_to++='c';
                *write_to++='h';
                break;
            case '\xdf':
                *write_to++='-'; /* hard sign */
                break;
                /* capital letters group */
            case '\xe0':
                *write_to++='J';
                *write_to++='U';
                break;
            case '\xe1':
                *write_to++='A';
                break;
            case '\xe2':
                *write_to++='B';
                break;
            case '\xe3':
                *write_to++='C';
                break;
            case '\xe4':
                *write_to++='D';
                break;
            case '\xe5':
                *write_to++='E';
                break;
            case '\xe6':
                *write_to++='F';
                break;
            case '\xe7':
                *write_to++='G';
                break;
            case '\xe8':
                *write_to++='K';
                *write_to++='H';
                break;
            case '\xe9':
                *write_to++='I';
                break;
            case '\xea':
                *write_to++='J';
                break;
            case '\xeb':
                *write_to++='K';
                break;
            case '\xec':
                *write_to++='L';
                break;
            case '\xed':
                *write_to++='M';
                break;
            case '\xee':
                *write_to++='N';
                break;
            case '\xef':
                *write_to++='O';
                break;
            case '\xf0':
                *write_to++='P';
                break;
            case '\xf1':
                *write_to++='J';
                *write_to++='A';
                break;
            case '\xf2':
                *write_to++='R';
                break;
            case '\xf3':
                *write_to++='S';
                break;
            case '\xf4':
                *write_to++='T';
                break;
            case '\xf5':
                *write_to++='U';
                break;
            case '\xf6':
                *write_to++='Z';
                *write_to++='H';
                break;
            case '\xf7':
                *write_to++='V';
                break;
            case '\xf8':
                *write_to++='\'';
                break;
            case '\xf9':
                *write_to++='Y'; /* bI */
                break;
            case '\xfa':
                *write_to++='Z';
                break;
            case '\xfb':
                *write_to++='S';
                *write_to++='H';
                break;
            case '\xfc':
                *write_to++='`';
                *write_to++='E';
                break;
            case '\xfd':
                *write_to++='S';
                *write_to++='H';
                *write_to++='C';
                *write_to++='H';
                break;
            case '\xfe':
                *write_to++='C';
                *write_to++='H';
                break;
            case '\xff':
                *write_to++='-'; /* hard sign (capital) */
                break;
                /* the rest.. */
            default:
                *write_to++=*read_from;
                break;
            }
            read_from++;
        }
        *write_to='\0';
    }
    return(new_buff);
}

const char *detransliterate_to_koi8(const char *original_string)
{
    int original_size;
    char *new_buff;

    original_size=strlen(original_string);
    /* alocate a buffer of the same size of the original string */
    if(new_buff=(char *)malloc((original_size+1)*sizeof(char))){
        const char *read_from;
        char *write_to;
        char second_char;

        read_from=original_string;
        write_to=new_buff;
        while(*read_from){
            second_char=*(read_from+1);

            switch(*read_from){
            case 'a':
                *write_to='\xc1';
                break;
            case 'A':
                *write_to='\xe1';
                break;
            case 'b':
                *write_to='\xc2';
                break;
            case 'B':
                *write_to='\xe2';
                break;
            case 'c':
                if(second_char=='h'){
                    *write_to='\xde'; /* ch */
                    read_from++;
                }else{
                    *write_to='\xc3'; /* ts */
                }
                break;
            case 'C':
                if(second_char=='H'){
                    *write_to='\xfe'; /* CH */
                    read_from++;
                }else{
                    *write_to='\xe3'; /* TS */
                }
                break;
            case 'd':
                *write_to='\xc4';
                break;
            case 'D':
                *write_to='\xe4';
                break;
            case 'e':
                *write_to='\xc5';
                break;
            case 'E':
                *write_to='\xe5';
                break;
            case '`':
                if(second_char=='e'){
                    *write_to='\xdc'; /* `e */
                    read_from++;
                }else if(second_char=='E'){
                    *write_to='\xfc'; /* `E */
                    read_from++;
                }else{
                    *write_to=*read_from; /* anything else */
                }
                break;
            case 'f':
                *write_to='\xc6';
                break;
            case 'F':
                *write_to='\xe6';
                break;
            case 'g':
                *write_to='\xc7';
                break;
            case 'G':
                *write_to='\xe7';
                break;
            case 'i':
                *write_to='\xc9';
                break;
            case 'I':
                *write_to='\xe9';
                break;
            case 'j':
                switch(second_char){
                case 'a':
                    *write_to='\xd1';
                    read_from++;
                    break;
                case 'u':
                    *write_to='\xc0';
                    read_from++;
                    break;
                case 'o':
                    *write_to='\xa3';
                    read_from++;
                    break;
                default:
                    *write_to='\xca';
                    break;
                }
                break;
            case 'J':
                switch(second_char){
                case 'A':
                    *write_to='\xf1';
                    read_from++;
                    break;
                case 'U':
                    *write_to='\xe0';
                    read_from++;
                    break;
                case 'O':
                    *write_to='\xb3';
                    read_from++;
                    break;
                default:
                    *write_to='\xea';
                    break;
                }
                break;
            case 'k':
                if(second_char=='h'){
                    *write_to='\xc8';
                    read_from++;
                }else{
                    *write_to='\xcb';
                }
                break;
            case 'K':
                if(second_char=='H'){
                    *write_to='\xe8';
                    read_from++;
                }else{
                    *write_to='\xeb';
                }
                break;
            case 'l':
                *write_to='\xcc';
                break;
            case 'L':
                *write_to='\xec';
                break;
            case 'm':
                *write_to='\xcd';
                break;
            case 'M':
                *write_to='\xed';
                break;
            case 'n':
                *write_to='\xce';
                break;
            case 'N':
                *write_to='\xee';
                break;
            case 'o':
                *write_to='\xcf';
                break;
            case 'O':
                *write_to='\xef';
                break;
            case 'p':
                *write_to='\xd0';
                break;
            case 'P':
                *write_to='\xf0';
                break;
            case 'r':
                *write_to='\xd2';
                break;
            case 'R':
                *write_to='\xf2';
                break;
            case 's':
                if(second_char=='h'){
                    if(*(read_from+2)=='c'){
                        if(*(read_from+3)=='h'){
                            *write_to='\xdd';
                            read_from+=3;
                        }else{
                            *write_to='\xdb';
                            read_from++;
                        }
                    }else{
                        *write_to='\xdb';
                        read_from++;
                    }
                }else{
                    *write_to='\xd3';
                }
                break;
            case 'S':
                if(second_char=='H'){
                    if((*(read_from+2)=='C')&&(*(read_from+3)=='H')){
                        *write_to='\xfd';
                        read_from+=3;
                    }else{
                        *write_to='\xfb';
                        read_from++;
                    }
                }else{
                    *write_to='\xf3';
                }
                break;
            case 't':
                *write_to='\xd4';
                break;
            case 'T':
                *write_to='\xf4';
                break;
            case 'u':
                *write_to='\xd5';
                break;
            case 'U':
                *write_to='\xf5';
                break;
            case 'v':
                *write_to='\xd7';
                break;
            case 'V':
                *write_to='\xf7';
                break;
            case 'y':
                *write_to='\xd9';
                break;
            case 'Y':
                *write_to='\xf9';
                break;
            case '\'':
                *write_to='\xd8';
                break;
            case 'z':
                if(second_char=='h'){
                    *write_to='\xd6';
                    read_from++;
                }else{
                    *write_to='\xda';
                }
                break;
            case 'Z':
                if(second_char=='H'){
                    *write_to='\xf6';
                    read_from++;
                }else{
                    *write_to='\xfa';
                }
                break;
            case '-':
                *write_to='\xdf'; /* non-capital only (tvardy znak) */
                break;
            case '.':             /* empty, used to separate dubious combinations (ja, jo, ju, shch) */
                write_to--;
                break;
            default:
                *write_to=*read_from;
                break;
            }
            read_from++;
            write_to++;
        }
        *write_to='\0';
    }
    return(new_buff);
}

/* transliterate koi8 to latin characters */
/* returned string must be free()'ed later */
const char *transliterate_from_koi8_old(const char *original_string)
{
    int original_size;
    char *new_buff;

    original_size=strlen(original_string);
    /* allocate a buffer 4x the original koi8 string size */
    if(new_buff=(char *)malloc(((original_size<<2)+1)*sizeof(char))){
        const char *read_from;
        char *write_to;

        read_from=original_string;
        write_to=new_buff;
        while(*read_from){
            switch(*read_from){
                /* 'yo' group */
            case '\xa3':
                *write_to++='y';
                *write_to++='o';
                break;
            case '\xb3':
                *write_to++='Y';
                *write_to++='O';
                break;
                /* non-capital letters group */
            case '\xc0':
                *write_to++='y';
                *write_to++='u';
                break;
            case '\xc1':
                *write_to++='a';
                break;
            case '\xc2':
                *write_to++='b';
                break;
            case '\xc3':
                *write_to++='t';
                *write_to++='s';
                break;
            case '\xc4':
                *write_to++='d';
                break;
            case '\xc5':
                *write_to++='e';
                break;
            case '\xc6':
                *write_to++='f';
                break;
            case '\xc7':
                *write_to++='g';
                break;
            case '\xc8':
                *write_to++='k';
                *write_to++='h';
                break;
            case '\xc9':
                *write_to++='i';
                break;
            case '\xca':
                *write_to++='y';
                break;
            case '\xcb':
                *write_to++='k';
                break;
            case '\xcc':
                *write_to++='l';
                break;
            case '\xcd':
                *write_to++='m';
                break;
            case '\xce':
                *write_to++='n';
                break;
            case '\xcf':
                *write_to++='o';
                break;
            case '\xd0':
                *write_to++='p';
                break;
            case '\xd1':
                *write_to++='y';
                *write_to++='a';
                break;
            case '\xd2':
                *write_to++='r';
                break;
            case '\xd3':
                *write_to++='s';
                break;
            case '\xd4':
                *write_to++='t';
                break;
            case '\xd5':
                *write_to++='u';
                break;
            case '\xd6':
                *write_to++='z';
                *write_to++='h';
                break;
            case '\xd7':
                *write_to++='v';
                break;
            case '\xd8':
                *write_to++='\'';
                break;
            case '\xd9':
                *write_to++='\''; /* not sure about this one (bI) */
                *write_to++='i';
                break;
            case '\xda':
                *write_to++='z';
                break;
            case '\xdb':
                *write_to++='s';
                *write_to++='h';
                break;
            case '\xdc':
                *write_to++='e';
                break;
            case '\xdd':
                *write_to++='s';
                *write_to++='h';
                *write_to++='t';
                break;
            case '\xde':
                *write_to++='c';
                *write_to++='h';
                break;
            case '\xdf':
                *write_to++='-'; /* i just don't know this one */
                break;
                /* capital letters group */
            case '\xe0':
                *write_to++='Y';
                *write_to++='U';
                break;
            case '\xe1':
                *write_to++='A';
                break;
            case '\xe2':
                *write_to++='B';
                break;
            case '\xe3':
                *write_to++='T';
                *write_to++='S';
                break;
            case '\xe4':
                *write_to++='D';
                break;
            case '\xe5':
                *write_to++='E';
                break;
            case '\xe6':
                *write_to++='F';
                break;
            case '\xe7':
                *write_to++='G';
                break;
            case '\xe8':
                *write_to++='K';
                *write_to++='H';
                break;
            case '\xe9':
                *write_to++='I';
                break;
            case '\xea':
                *write_to++='Y';
                break;
            case '\xeb':
                *write_to++='K';
                break;
            case '\xec':
                *write_to++='L';
                break;
            case '\xed':
                *write_to++='M';
                break;
            case '\xee':
                *write_to++='N';
                break;
            case '\xef':
                *write_to++='O';
                break;
            case '\xf0':
                *write_to++='P';
                break;
            case '\xf1':
                *write_to++='Y';
                *write_to++='A';
                break;
            case '\xf2':
                *write_to++='R';
                break;
            case '\xf3':
                *write_to++='S';
                break;
            case '\xf4':
                *write_to++='T';
                break;
            case '\xf5':
                *write_to++='U';
                break;
            case '\xf6':
                *write_to++='Z';
                *write_to++='H';
                break;
            case '\xf7':
                *write_to++='V';
                break;
            case '\xf8':
                *write_to++='\'';
                break;
            case '\xf9':
                *write_to++='\''; /* not sure about this one (bI) */
                *write_to++='I';
                break;
            case '\xfa':
                *write_to++='Z';
                break;
            case '\xfb':
                *write_to++='S';
                *write_to++='H';
                break;
            case '\xfc':
                *write_to++='E';
                break;
            case '\xfd':
                *write_to++='S';
                *write_to++='H';
                *write_to++='T';
                break;
            case '\xfe':
                *write_to++='C';
                *write_to++='H';
                break;
            case '\xff':
                *write_to++='-'; /* i just don't know this one */
                break;
                /* the rest.. */
            default:
                *write_to++=*read_from;
                break;
            }
            read_from++;
        }
        *write_to='\0';
    }
    return(new_buff);
}

const char *detransliterate_to_koi8_old(const char *original_string)
{
    int original_size;
    char *new_buff;

    original_size=strlen(original_string);
    /* alocate a buffer of the same size of the original string */
    if(new_buff=(char *)malloc((original_size+1)*sizeof(char))){
        const char *read_from;
        char *write_to;
        char second_char;

        read_from=original_string;
        write_to=new_buff;
        while(*read_from){
            second_char=*(read_from+1);

            switch(*read_from){
            case 'a':
                *write_to='\xc1';
                break;
            case 'A':
                *write_to='\xe1';
                break;
            case 'b':
                *write_to='\xc2';
                break;
            case 'B':
                *write_to='\xe2';
                break;
            case 'c':
                if(second_char=='h'){
                    *write_to='\xde';
                    read_from++;
                }else{
                    *write_to=*read_from;
                }
                break;
            case 'C':
                if(second_char=='H'){
                    *write_to='\xfe';
                    read_from++;
                }else{
                    *write_to=*read_from;
                }
                break;
            case 'd':
                *write_to='\xc4';
                break;
            case 'D':
                *write_to='\xe4';
                break;
            case 'e':
                *write_to='\xc5';
                break;
            case 'E':
                *write_to='\xe5';
                break;
            case 'f':
                *write_to='\xc6';
                break;
            case 'F':
                *write_to='\xe6';
                break;
            case 'g':
                *write_to='\xc7';
                break;
            case 'G':
                *write_to='\xe7';
                break;
            case 'i':
                *write_to='\xc9';
                break;
            case 'I':
                *write_to='\xe9';
                break;
            case 'y':
                switch(second_char){
                case 'a':
                    *write_to='\xd1';
                    read_from++;
                    break;
                case 'u':
                    *write_to='\xc0';
                    read_from++;
                    break;
                case 'o':
                    *write_to='\xa3';
                    read_from++;
                    break;
                default:
                    *write_to='\xca';
                    break;
                }
                break;
            case 'Y':
                switch(second_char){
                case 'A':
                    *write_to='\xf1';
                    read_from++;
                    break;
                case 'U':
                    *write_to='\xe0';
                    read_from++;
                    break;
                case 'O':
                    *write_to='\xb3';
                    read_from++;
                    break;
                default:
                    *write_to='\xea';
                    break;
                }
                break;
            case 'k':
                if(second_char=='h'){
                    *write_to='\xc8';
                    read_from++;
                }else{
                    *write_to='\xcb';
                }
                break;
            case 'K':
                if(second_char=='H'){
                    *write_to='\xe8';
                    read_from++;
                }else{
                    *write_to='\xeb';
                }
                break;
            case 'l':
                *write_to='\xcc';
                break;
            case 'L':
                *write_to='\xec';
                break;
            case 'm':
                *write_to='\xcd';
                break;
            case 'M':
                *write_to='\xed';
                break;
            case 'n':
                *write_to='\xce';
                break;
            case 'N':
                *write_to='\xee';
                break;
            case 'o':
                *write_to='\xcf';
                break;
            case 'O':
                *write_to='\xef';
                break;
            case 'p':
                *write_to='\xd0';
                break;
            case 'P':
                *write_to='\xf0';
                break;
            case 'r':
                *write_to='\xd2';
                break;
            case 'R':
                *write_to='\xf2';
                break;
            case 's':
                if(second_char=='h'){
                    if(*(read_from+2)=='t'){
                        *write_to='\xdd';
                        read_from+=2;
                    }else{
                        *write_to='\xdb';
                        read_from++;
                    }
                }else{
                    *write_to='\xd3';
                }
                break;
            case 'S':
                if(second_char=='H'){
                    if(*(read_from+2)=='T'){
                        *write_to='\xfd';
                        read_from+=2;
                    }else{
                        *write_to='\xfb';
                        read_from++;
                    }
                }else{
                    *write_to='\xf3';
                }
                break;
            case 't':
                if(second_char=='s'){
                    *write_to='\xc3';
                    read_from++;
                }else{
                    *write_to='\xd4';
                }
                break;
            case 'T':
                if(second_char=='S'){
                    *write_to='\xe3';
                    read_from++;
                }else{
                    *write_to='\xf4';
                }
                break;
            case 'u':
                *write_to='\xd5';
                break;
            case 'U':
                *write_to='\xf5';
                break;
            case 'v':
                *write_to='\xd7';
                break;
            case 'V':
                *write_to='\xf7';
                break;
            case '\'':
                switch(second_char){
                case 'i':
                    *write_to='\xd9';
                    read_from++;
                    break;
                case 'I':
                    *write_to='\xf9';
                    read_from++;
                    break;
                default:
                    *write_to='\xd8';
                    break;
                }
                break;
            case 'z':
                if(second_char=='h'){
                    *write_to='\xd6';
                    read_from++;
                }else{
                    *write_to='\xda';
                }
                break;
            case 'Z':
                if(second_char=='H'){
                    *write_to='\xf6';
                    read_from++;
                }else{
                    *write_to='\xfa';
                }
                break;
            case '-':
                *write_to='\xdf'; /* non-capital only (tvardy znak) */
                break;
            default:
                *write_to=*read_from;
                break;
            }
            read_from++;
            write_to++;
        }
        *write_to='\0';
    }
    return(new_buff);
}

