/*************************************************************
This program translates ascii chars to Chinese GB chars, remove
trailing white spaces(GB or ascii), detect errors.

Usage: map [-option] <filename>
example: map cm9204d.gb

Default option: 

(1)Remove trailing white spaces (ascii spaces, tabs, and GB A1A1)
(2)Map ASCII white spaces to GB blanks (A1A1). Map every 2n ' '
   to n GB blanks, and 2n+1 ' ' to n+1 GB blanks. '\t' to 4 A1A1.
(3)Check if there are out-of-range GB chars, undue ascii control chars, 
and any other bug chars, fix them if possible.
<4>Map << and >> to GB ShuMingHao, translate [[ and ]] to solid square brackets
like those we used in HXWZ TOC.  6 dot . to Shenglue Hao. ` to Dun Hao
(5)Map COMMONLY used ascii punctuations to corresponding GB ones.
eg, . to GB JuHao, , to DouHao.   But . in English text(such as the .
in e-mail addresses) will not be translated.  
(6)Map ascii number (1-19) plus an ascii dot '.' to ONE corresponding
   GB char.(number followed by an dot. Often used as index number in 
   HXWZ TOC). 
(6)Remove some unwanted ascii spaces in the text. 
(7)Add <CR> before <LF> if <CR> is missing. 
(8)Add <LF> if <CR> is missing.
(9)Map some traditional chars to simplified ones.

Switch -p:
(8)Map other(less commonly used) ascii punctuations, such as <, >, [, #, etc, 
to GB ones.

Switch -l:
(9)Map ascii letters (alphabets) to GB ones.

Switch -d:
(10)Map ascii digits to GB ones.

The program is based on Ruopeng's mapping.c and gbdetect.c. 
If find any problem, let me or Ruopeng know.

 Minghui Yao <yaom@csss.la.asu.edu>
 Last Revised 12/9/92

**************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define  TRUE       1
#define  FALSE      0

char    *pgname;      /* name of this program  */
int     smart_flag = FALSE;   /*  ????  if on, use smart mapping mode */
int     all_flag = FALSE;     /* if on, map all ASCII char to GuoBiao */
int     digit_flag = FALSE;   /* if on, activate digits mapping  */
int     letter_flag = FALSE;   /* if on, activate mapping of English letters */
int     punct_flag = FALSE;   /* if on, activate punctuations mapping mode */
int     command_flag = FALSE;   /* if on, de-activate delimiter mapping */
int     name_flag = FALSE;  /* indicate whether a file_name is provided */
int    tab_count = 0, space_count = 0, punct_count = 0, digit_count = 0;
int    letter_count = 0, trailing_space = 0, trailing_space_count = 0;
int     error_count = 0;  /* no. of possible errors in smart_mapping mode */
long    char_count = 0;     /* for smart quote mapping */
int     line_count = 0;  /*  total no. of <LF> in output file   */
int     cr_count = 0;     /* no. of <cr> added */
int     cntrl_count = 0;       /*  no. of control char not '\n' or '\r' */
int     out_range_count = 0;   /*  no. of char between 0x80 and 0xA0 */
int     shift_count = 0;      /*  no. of char in range but at wrong position */
int     uneven_count = 0;     /*  no. of lines with odd number of high byte  */
int     bug_count = 0;         /*  total no. of bug characters  */    
int	trad_count = 0;        /* traditional chars count */
char in_name[81];     /* input file name (with extension) */
char out_name[81];    /* output file name (with extension .gb0) */
char name[81];        /* file name (without extension */
int  	 hi[6]={0xE1, 0xF7, 0xEC, 0xE1, 0xA1, 0xA1};
int      lo[6]={0xE1, 0xE1, 0xB6, 0xE7, 0xB8, 0xB9};
int      map_h[6]={0xBA, 0xC3, 0xD3, 0xD5, 0xA1, 0xA1};
int      map_l[6]={0xF3, 0xB4, 0xDA, 0xF7, 0xB0, 0xB1};
FILE *in_file; 
FILE *out_file;

void usage();
void open_file();
void general_mapping();
void escape();
void quote_mapping();
void message();

void usage()
{
fprintf(stderr, "|--------------------------------------------------------|\n");
fprintf(stderr, "| Usage:  mapping [-a] [-l] [-d] [-p] [-c] file_name     |\n");
fprintf(stderr, "|    Default: Map spaces, tabs, some common puncts       |\n");
fprintf(stderr, "|       Check out bad codes, fix them. Remove            |\n");
fprintf(stderr, "|       trailing white spaces(spaces, tabs, A1A1)        |\n"); 
fprintf(stderr, "|                                                        |\n");
fprintf(stderr, "|    Option -digit:  activate mapping of digits          |\n");
fprintf(stderr, "|    Option -letter: activate mapping of letters         |\n");
fprintf(stderr, "|    Option -punct:  mapping all punctuations            |\n");
fprintf(stderr, "|    Option -all:    equivalent to -d -l -p              |\n");
fprintf(stderr, "|    Option -command: de-activate mapping of \\, (, ).    |\n");
fprintf(stderr, "|    Output =  file_name.map                             |\n");
fprintf(stderr, "| Example 1:   map cm9204a.gb                            |\n");
fprintf(stderr, "|     Do the default mapping for the file cm9204a.gb     |\n");
fprintf(stderr, "| Example 2:   map -digit -punct  testfile               |\n");
fprintf(stderr, "|     Map ASCII characters except English letters        |\n");
fprintf(stderr, "|--------------------------------------------------------|\n");
exit(1);
}


void general_mapping()
{
        int  b, even = TRUE, bug = FALSE, i=0, trad_flag;
        unsigned next_c, last_3c, last_2c, last_c, c;
        if (all_flag) {
                digit_flag = TRUE;
                letter_flag = TRUE;
                punct_flag = TRUE;
        }
        while ( ( b=fgetc(in_file) ) != EOF) {
		trad_flag=0;
                c = (unsigned) b;
                char_count ++;
                if ( isprint(c) || isspace(c) ) {
                     if (!even) {
                        bug = TRUE;  /* un-even block */
                        uneven_count ++;
                        fprintf(out_file, " Unfixed problem in this line");
                        }
                     even = TRUE;           /* reset at ASCII char */
                }
                if ( c >= 0xA1 && c <= 0xFE )
                        even = !even;       
                if ( !even &&               /* c is first byte in a pair */
                        ((c>=0xA8 && c<=0xAF) || (c>= 0xF8 && c<=0xFE))) {
                           bug = TRUE;      /* first byte out of range */
                           shift_count ++;  /* misplaced second byte? */
                           fprintf(out_file, " Unfixed Problem here");   
                }
/* map traditional chars to simp. */
		i=0;
		
		for(i=0; i<7 ; i++){
			if(even && c==lo[i] && last_c==hi[i] )
			{
                            	fseek(out_file, -1, 1);
				fprintf(out_file, "%c%c", map_h[i], map_l[i]);
				trad_count++; trad_flag=1;
			}
		}

                if ( even && !(isprint(c) || isspace(c)) &&
                                            c == 0xA1 && last_c == 0xA1 )
                       trailing_space += 2;
        	if (!(c == ' ' || c =='\t' || c =='\n' || c == '\r'
                                   || c == 0xA1 ))
                        trailing_space = 0;
                if (command_flag && (c=='\\' || c=='{' || c=='}' ) ) {
                        /* delimiter for commands */
                        fputc(c, out_file);
                }

                else if ( iscntrl(c) && !isspace(c) ) {
                        bug = TRUE;   /*  unwanted ASCII control char */
                        cntrl_count ++;   /* count, skip */
               }
                else if ( ( c >= 0x80 && c <= 0xA0 ) || c >= 0xFF ) {
                        bug = TRUE;      /* out of GuoBiao range */
                        out_range_count ++;   /* skip out-of-range char */
                }
               else if ( c == '$' && punct_flag ) {
                        punct_count ++;
                        fprintf(out_file, "%c%c", 0xA1, 0xE7);
                }
                else if ( c == '\"') {    /* a double quote */
                        punct_count ++;
                        quote_mapping();
               }
                else if ( c == ' ' && !ispunct(last_c)) {
                        space_count ++;
                        trailing_space ++;
	                if(trailing_space%2)
                    {                  /* map each 2 ascii space */
                                               /* to 1 GB space          */
                             fprintf(out_file, "%c%c", 0xA1, 0xA1);
                             }
                        else 
                             continue;
                }
                else if ( c == ' ')
                        continue;
                else if ( c == '\r'){
                         trailing_space += trailing_space%2; 
                         fseek(out_file, -trailing_space, 1);
                         trailing_space_count += trailing_space;
                         trailing_space = 0;
                         fputc(c, out_file);
                }
		else if ( c != '\n' && (last_c == '\r')){
			fprintf(out_file, "\r\n");
		}
                else if ( c == '\n' && (last_c != '\r')){
                         trailing_space += trailing_space%2;
                         fseek(out_file, -(trailing_space), 1);
                         trailing_space_count += trailing_space;
                         trailing_space = 0;
                         fputc('\r', out_file);          /* add cr */
                         fputc(c, out_file);
                         cr_count ++; 
                }         
               else if ( c == '<' && last_c == '<'){
                        if (punct_flag)
                            fseek(out_file, -2, 1);
                        else fseek(out_file, -1, 1);
                        fprintf(out_file, "%c%c", 0xA1,0xB6);
                        punct_count ++;         /* map ShuMing Hao */
               }
               else if ( c == '>' && last_c == '>'){
                        if (punct_flag)
                            fseek(out_file, -2, 1);
                        else fseek(out_file, -1, 1);
                        fprintf(out_file, "%c%c", 0xA1,0xB7);
                        punct_count ++;
               }
               else if ( c == '[' && last_c == '['){
                        if (punct_flag)
                            fseek(out_file, -2, 1);
                        else fseek(out_file, -1, 1);
                        fprintf(out_file, "%c%c", 0xA1,0xBE);
                        punct_count ++;
               }
               else if ( c == ']' && last_c == ']'){
                        if (punct_flag)
                            fseek(out_file, -2, 1);
                         else fseek(out_file, -1, 1);
                         fprintf(out_file, "%c%c", 0xA1,0xBF);
                         punct_count ++;
               }
                else if ( c == '.' && last_c == '.' 
                                        && last_2c == '.' && last_3c != '.')
                {
                        if(punct_flag)
                                fseek(out_file, -4, 1);
                        else
                                fseek(out_file, -3, 1);
                        fprintf(out_file, "%c%c", 0xA1,0xAD);
                        punct_count++;
                        b = -1;
                }               
                else if ( c == '`' )
                        fprintf(out_file,"%c%c", 0xA1, 0xA2);
               else if ( c == '.' && last_c >= '0' && last_c <= '9')
               {    
                    next_c = fgetc(in_file);
                    fseek(in_file, -1, 1);
                    if(last_2c == '1' && !(isdigit(last_3c) 
                              || isdigit(next_c) || isalpha(last_3c)))
                    {              
                        if (digit_flag)
                             fseek(out_file, -4, 1);
                        else fseek(out_file, -2, 1);
                        fprintf(out_file, "%c%c", 0xA2,last_c+0x8A);
                        digit_count ++;
                        punct_count ++;
                    } 
                    else if(!(isdigit(last_2c) || isalpha(last_3c)
                                             || isdigit(next_c)))
                    {              
                        if (digit_flag)
                             fseek(out_file, -2, 1);
                        else fseek(out_file, -1, 1);
                        fprintf(out_file, "%c%c", 0xA2, last_c+0x80);
                        digit_count ++;
                        punct_count ++;
                    }
                    else fputc(c, out_file);
               }                  
                else if ( c == '\t') {
                         tab_count ++;
                         trailing_space += 8;
                         fprintf(out_file, "%c%c%c%c%c%c%c%c", 
                         0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1);
                }
                else if (  isalpha(c) && letter_flag ){
                        letter_count ++;
                        fprintf(out_file, "%c%c", 0xA3, c+0x80);
               }
               else if (  isdigit(c) && digit_flag ){
                       digit_count ++;
                       fprintf(out_file, "%c%c", 0xA3, c+0x80);
               }
               else if ( c == '?' || c == ',' || c == '!'
                       || c == ':' || c == ';'){
                       punct_count ++;
                       fprintf(out_file, "%c%c", 0xA3, c+0x80);
               }  /* some commonly used puncts always mapped */
               else if ( c == '.' && !(isdigit(last_c) || 
                   isalpha(last_c) || (last_c == '.') ))
               {
                       punct_count ++;
                       fprintf(out_file, "%c%c", 0xA1, 0xA3);
                  /* if the char before '.' is not digit, letter, punct */
                  /* it is usually a Ju4Hao4, map to Chinese JuHao    */
               }         
               else if (  ispunct(c) && punct_flag ){
                       punct_count ++;
                       fprintf(out_file, "%c%c", 0xA3, c+0x80);
               }   /* less commonly used puncts are mapped if puct_flag set */ 
               else if (!trad_flag) {
                        fputc(c, out_file);                     
               }

       last_3c=last_2c;
       last_2c=last_c;
       last_c=b;
        }
}


void quote_mapping()
{
        static int      quote_even = TRUE;

        quote_even = !quote_even;
        if ( !quote_even ) {   /* left quote */
                char_count = 0;
                fprintf(out_file, "%c%c", 0xA1, 0xB0);
        }
        else if ( char_count <=2000 ) { /* right quote */
                fprintf(out_file, "%c%c", 0xA1, 0xB1);
        }
        else {  /* left quote unmatched, start a new pair */
                char_count = 0;
                fprintf(out_file, "%c%c", 0xA1, 0xB0);
                quote_even = FALSE;
                error_count ++;
                fprintf(stderr, "Un-matched left quote\n");
        }
}

void open_file()
{
        if (name_flag) {
                if ((in_file=fopen(in_name, "rb")) == NULL)
                {
                        fprintf(stderr, "Cannot open GB file\n");
                        usage();
                }
                if ((out_file=fopen(out_name,"wb")) == NULL) 
                {
                        fprintf(stderr, "Cannot open out_file\n");
                        exit(1);
                }
        }
}

/* report to user what is done */
void message()
{
        fprintf(stderr, "\t|----------------------------------------|\n");
        fprintf(stderr, "\t| Input_file  =  %-16.15s        |\n", in_name);
        fprintf(stderr, "\t| Output_file =  %-16.15s        |\n", out_name);
        fprintf(stderr, "\t| ASCII spaces translated      =  %4d   |\n",
            space_count);
        fprintf(stderr, "\t| Cr added                     =  %4d   |\n",
            cr_count);
        fprintf(stderr, "\t| Tabs mapped to 4 A1A1        =  %4d   |\n",
            tab_count);
        fprintf(stderr, "\t| ASCII puncts mapped          =  %4d   |\n",
            punct_count);
        fprintf(stderr, "\t| ASCII digits mapped          =  %4d   |\n",
            digit_count);
        fprintf(stderr, "\t| ASCII letters translated     =  %4d   |\n",
            letter_count);      
        fprintf(stderr, "\t| Possible mis-translations    =  %4d   |\n",
            error_count);
        fprintf(stderr, "\t| Trailing Spaces removed(byte)=  %4d   |\n",
            trailing_space_count);
        fprintf(stderr, "\t| Unwanted Ctrl Chars removed  =  %4d   |\n",
            cntrl_count);
        fprintf(stderr, "\t| Out-of-Range Chars removed   =  %4d   |\n",
            out_range_count);
        fprintf(stderr, "\t| Traditional  Chars  mapped   =  %4d   |\n",
            trad_count);
        fprintf(stderr, "\t|----------------------------------------|\n");
        if(uneven_count || shift_count)
            fprintf(stderr, "Warning!!! %d Unfixed bug Char in %s\n", 
            (shift_count+uneven_count), out_name); 
}

main(argc, argv)
int argc; char *argv[];
{
        int     i;
        char    *cp;

        pgname = argv[0];
        in_file = NULL;
        out_file = NULL;
        for (i=1; i<argc; i++) {
                cp = argv[i];
                if (*cp == '-') {  /* a flag */
                        if (*++cp == 'l') {   /* activate letter mapping */
                                letter_flag++;
                        } 
                        else if ( *cp == 'd' ) { /* activate digit mapping */
                                digit_flag ++;
                        }
                        else if ( *cp == 'p' ) { /* activate punct mapping */
                                punct_flag ++;
                        }
                        else if ( *cp == 'a' ) { /* activate all mapping */
                                all_flag ++;
                        }
                        else if ( *cp == 'c' ) {/* activate delimiter mapping */
                                command_flag ++;
                        }
                        else 
                        {
                              fprintf(stderr, "Unknown option %s\n", cp);
                              usage();
                        }
                }
                else {         /* a file name argument */
                        if ( name_flag == TRUE ) 
                                usage();
                        name_flag = TRUE;
                        strcpy(in_name, cp);
                        sscanf(in_name, "%[^.]", name);
                        strcpy(out_name, "");   /* initialization */
                        strcat(out_name, name);
                        strcat(out_name, ".map");   /* use extension .map */
                }
        }
        if ( !name_flag )         /* error: no name is specified */
        {
                fprintf(stderr, "No filename is given...\n");
                usage();
        }
        else {        /* smart mode is specified */
                open_file();
                general_mapping();
                message();
        }
}

