/*
 * %Z%%W% %I%
 * ===========================================================================
 * Licensed Materials - Property of IBM
 * "Restricted Materials of IBM"
 *
 * IBM SDK, Java(tm) 2 Technology Edition, v1.4
 * (C) Copyright IBM Corp. 1999, 2002. All Rights Reserved
 * ===========================================================================
 */
/*
 * convdump:  Dump out a Java converter
 *
 * Function:
 *     Use the external interfaces to Java conversion
 *
 *     This application takes a Java converter and dumps the mappings.
 *     This works for single byte codepage, and for PC and Host DBCS
 *     codepages.
 *
 *     Output is sent to stdout.  A brief summary is sent to stderr.
 *
 *     For PC DBCS codepages we assume that any byte which gives an
 *     exception is a DBCS starter.  For EBCDIC DBCS codepages we assume
 *     that any byte in the range 0x41 to 0xfe is a possible DBCS starter.
 *
 * Syntax:
 *     java convdump converter type ranges ucmname
 *
 *     converter: Java converter or charset.  If the name starts with
 *                an exclamation mark (!) it is an nio charset.
 *     type:      sa    Single Byte ASCII
 *                se    Single Byte EBCDIC
 *                da    Double Byte ASCII
 *                du    Double Byte EUC (Unix)
 *                eu    EUC (Unix)
 *                ej    EUC Japanese
 *                et    EUC Taiwan
 *                gb    GB18030
 *                gb    GB18030
 *                u8    UTF8
 *     ranges:    4 hex digit start/end range of DBCS starter
 *     ucmname:   Name of ucm file to validate against
 *
 * Notes:
 *     This code can be run in releases before 1.4.0, but it must be compiled
 *     using JDK 1.4 or above.  The nio converter logic does not work before
 *     JDK 1.4.
 */

import java.lang.*;
import java.io.*;
import java.util.*;
import java.nio.*;
import java.nio.charset.*;

public class convdump {

    public static final int AUTO_TYPE   = 0;
    public static final int SBCS_ASCII  = 1;
    public static final int SBCS_EBCDIC = 2;
    public static final int DBCS_ASCII  = 3;
    public static final int DBCS_EBCDIC = 4;
    public static final int DBCS_EUC    = 5;
    public static final int UTF_8       = 6;
    public static final int CESU_8      = 7;
    public static final int EUC_CNKR    = 8;
    public static final int EUC_JP      = 9;
    public static final int EUC_TW      = 10;

    static char [] ToUni;
    static char [] FromUni;
    static File    f;
    static InputStreamReader isr;
    static char    SubChar;
    static char    retCh;
    static String  encoding;
    static int     touniCount = 0;
    static int     fromCount  = 0;
    static int     exceptionCount = 0;
    static boolean useNio = false;
    static Charset charset = null;
    static CharsetDecoder decode = null;
    static CharsetEncoder encode = null;
    static CoderResult  cr;
    final static private String javaVersion = System.getProperty("java.version");


    /*
     * Get bytes from a string using either the String or nio interfaces
     */
    static byte [] getBytes(String s) throws UnsupportedEncodingException {
        if (useNio) {
            encode.reset();
            CharBuffer cb = CharBuffer.wrap(s.toCharArray());
            ByteBuffer bb = ByteBuffer.wrap(new byte[s.length() *4]);
            cr = encode.encode(cb, bb, true);
            cr = encode.flush(bb);
            byte[] res = new byte[bb.position()];
            System.arraycopy(bb.array(), 0, res, 0, res.length);
            return res;
        } else {
            return s.getBytes(encoding);
        }
    }

    /*
     * Create a new String using either the String or nio interfaces
     */
    static String  newString(byte [] b) throws UnsupportedEncodingException {
        if (useNio) {
            decode.reset();
            ByteBuffer bb = ByteBuffer.wrap(b);
            CharBuffer cb = CharBuffer.wrap(new char[b.length*2]);
            cr = decode.decode(bb, cb, true);
            cr = decode.flush(cb);
            return new String(cb.array(), 0, cb.position());
        } else {
            return new String(b, encoding);
        }
    }

    /*
     * Main converter dump program
     */
    public static void main (String[] args) {
        String  source;
        byte    target[], bsource[], invalid[], tsource[];
        byte    dbcslead[] = new byte[256];
        char    chrs[];
        int     i, j, k, m, n;
        int     minother;
        int     maxother;
        int     leadCount = 0;
        boolean isEbcdicDbcs = false;
        boolean doSurrogates = false;
        boolean isGB18030    = false;
        int     type = 0;
        String  typeStr;
        String  UcmName = null;
        boolean doCheck      = false;

        if (args.length < 1) {
            System.out.println("convdump - Dump a java converter");
            System.out.println("");
            System.out.println("syntax: java convdump encoding type range check.ucm");
            System.out.println("");
            System.out.println("    Do a dump of the complete mapping of an encoding.  This");
            System.out.println("    tests all codepoints, but only does one at a time.  The");
            System.out.println("    output is sent to stdout.                              ");
            System.out.println("");
            System.out.println("    If the encoding name starts with an exclamation point (!)");
            System.out.println("    then nio Charseet methods are used, otherwise the String");
            System.out.println("    methods are used.  nio does not work before JDK 1.4.0.");
            System.out.println("");
            System.out.println("    In most cases, the type of the encoding is automatically");
            System.out.println("    determined.  So type and range do not need to be specified.");
            return;
        }

        long runtime = System.currentTimeMillis();
        bsource = new byte[1];
        chrs    = new char[1];
        encoding = args[0];
        int rangecnt = 0;
        int [] range = {0, 0, 0, 0};
        int    val;

        /*
         * Use string or nio constructor
         */
        if (encoding.charAt(0)=='!') {
            if (javaVersion.compareTo("1.4.0") >= 0) {
                useNio = true;
            } else {
                System.out.println("nio not available in this release.");
                return;
            }
            encoding = encoding.substring(1);

            charset = Charset.forName(encoding);
            decode  = charset.newDecoder();
            decode.onUnmappableCharacter(CodingErrorAction.REPLACE);
            encode  = charset.newEncoder();
            encode.onUnmappableCharacter(CodingErrorAction.REPLACE);
        }
        System.out.println("Codepage: " + encoding);

        /*
         * Check other parameters
         */
        for (i=1; i<args.length; i++) {
            if (args[i].length()==2) {
                typeStr = args[i];
                if (typeStr.equals("sa"))
                    type = SBCS_ASCII;
                if (typeStr.equals("se"))
                    type = SBCS_EBCDIC;
                if (typeStr.equals("da"))
                    type = DBCS_ASCII;
                if (typeStr.equals("de"))
                    type = DBCS_EBCDIC;
                if (typeStr.equals("u8"))
                    type = UTF_8;
                if (typeStr.equals("c8"))
                    type = CESU_8;
                if (typeStr.equals("du"))
                    type = DBCS_EUC;
                if (typeStr.equals("ej"))
                    type = EUC_JP;
                if (typeStr.equals("eu"))
                    type = EUC_CNKR;
                if (typeStr.equals("et"))
                    type = EUC_TW;
                if (typeStr.equals("gb")) {
                    isGB18030 = true;
                    doSurrogates = true;
                }
                continue;
            }
            if (args[i].length()==4 && rangecnt<4) {
                try {
                    range[rangecnt] = Integer.parseInt(args[i], 16) & 0x00ffff;
                    rangecnt++;
                } catch (Exception e) {
                    System.out.println("Invalid range: " + args[i]);
                }
                continue;
            }
            if (args[i] == "=") {
                UcmName = encoding+".ucm";
            } else {
                UcmName = args[i];
            }
        }

        if (UcmName != null) {
            System.out.println("UCM file name = " + UcmName);
            doCheck = readUCM(UcmName);
        }

        if (type == AUTO_TYPE) {
            type = getType(encoding, range);
            if (range[0] != 0)
                rangecnt = 2 ;
        }
        try {
            invalid = getBytes("\u02ff");
            if (invalid.length == 4 && invalid[1]==0x30) {
                isGB18030 = true;
                doSurrogates = true;
                invalid = new byte[1];
                invalid[0] = 0x3f;
            }
            if (invalid.length == 5 && invalid[0]==0x1B) {
                doSurrogates = true;
                type = SBCS_ASCII;
                invalid = new byte[1];
                invalid[0] = 0x3f;
            }

            System.out.print("Substitute = ");
            for (j=0; j<invalid.length ; j++) {
                System.out.print(Hex2((int)invalid[j]) + " ");
            }
            System.out.println("");


            if (type < DBCS_ASCII || type >= EUC_CNKR)
            for (i=0; i<256; i++) {
                dbcslead[i] = 0;
                bsource[0] = (byte)i;
                try {
                    source = newString(bsource);
                    if (source.charAt(0) != 0xfffd) {
                        System.out.print("ByteToChar  " + Hex2(i) + " = ");
                        for (k=0; k<source.length() ; k++) {
                            System.out.print(Hex4((int)source.charAt(k)) + " ");
                        }
                        if (doCheck) {
                            if (source.charAt(0) != ToUni[i]) {
                                System.out.print("  Invalid:  should be " + Hex4(ToUni[i]));
                            }
                        }
                        System.out.println("");
                        touniCount++;
                    } else {
                        if (doCheck && ToUni[i]!=0) {
                            System.out.println("ByteToChar " + Hex2(i) +
                                " = 00  Invalid: should be " + Hex4(ToUni[i]));
                        }
                    }
                } catch (Throwable e) {
                    if (leadCount>0 || i<0x20 || (i>=0x80 && i<0x90)) {
                        if (type <= EUC_CNKR) {
                            if (i>0 && dbcslead[0]==0)
                                System.out.println("DBCS Lead Byte " + Hex2(i));
                            if (i==0x0e && leadCount==0)
                                isEbcdicDbcs = true;
                            leadCount++;
                            if (!isEbcdicDbcs)
                                dbcslead[i] = 1;
                        }
                    }
                }
            }

            /*
             * Set up for DBCS loop
             */
            if (isEbcdicDbcs) {
                for (i=65; i<255; i++)
                    dbcslead[i] = 1;
                minother = 65;
                maxother = 254;
                tsource = new byte[3];
                tsource[0] = 0x0e;
            } else {
                minother = 64;
                maxother = 254;
                if (leadCount==256) {
                    minother = 0;          /* Unicode */
                    maxother = 255;
                }
                if (type==DBCS_EBCDIC) {
                    minother = 65;
                    for (i=65; i<255; i++)
                        dbcslead[i] = 1;
                }
                tsource = new byte[2];
            }

            if (type==DBCS_ASCII) {
                if (rangecnt == 0) {
                    rangecnt = 1;
                    range[0] = 0x81fe;
                }
                for (i=0; i<rangecnt; i++) {
                    if ((range[i]&0xff) != 0) {
                        for (j=range[i]>>8; j<(range[i]&0xff); j++)
                            dbcslead[j] = 1;
                    }
                }
            }

            if (type==DBCS_EUC || type==EUC_CNKR || type==EUC_JP || type==EUC_TW ) {
                for (i=0xa1; i<=0xfe; i++)
                    dbcslead[i] = 1;
                minother = 0xa1;
            }
            if (type==EUC_JP) {
                dbcslead[0x8e] = 1;
            }

            if (type==UTF_8 || type==CESU_8) {
                doSurrogates = true;
                for (i=0; i<128; i++) {
                    bsource[0] = (byte) i;
                    doToChar(bsource);
                }

                bsource = new byte[2];
                for (i=0xc2; i<0xe0; i++) {
                    bsource[0] = (byte) i;
                    for (j=0x80; j<0xc0; j++) {
                        bsource[1] = (byte) j;
                        doToChar(bsource);
                    }
                }

                bsource = new byte[3];
                for (i=0xe0; i<0xf0; i++) {
                    bsource[0] = (byte)i;
                    for (j=(i==0xe0)?0xa0:0x80; j<0xc0; j++) {
                        bsource[1] = (byte)j;
                        for (k=0x80; k<0xc0; k++) {
                            bsource[2] = (byte)k;
                            doToChar(bsource);
                        }
                    }
                }
            }

            /*
             * Loop to reverse map DBCS
             */
            if (type != SBCS_ASCII && type != SBCS_EBCDIC && type != UTF_8 && type != CESU_8)
            for (i=0; i<256; i++) {
                if (dbcslead[i] > 0) {
                    tsource[isEbcdicDbcs ? 1 : 0] = (byte)i;
                    for (j=minother; j<=maxother; j++) {
                        try {
                            tsource[isEbcdicDbcs ? 2 : 1] = (byte)j;
                            val = (i<<8) + j;
                            source = newString(tsource);
                            if (source.charAt(0) != 0xfffd) {
                                System.out.print("ByteToChar  " + Hex2(i) + " " + Hex2(j) + " = ");
                                for (k=0; k<source.length(); k++) {
                                    System.out.print(Hex4((int)source.charAt(k)) + " ");
                                }
                                if (doCheck) {
                                    if (source.charAt(0) != ToUni[(i<<8)+j]) {
                                        System.out.print("  Invalid:  should be " + Hex4(ToUni[(i<<8)+j]));
                                    }
                                }
                                System.out.println("");
                                touniCount++;
                            } else {
                                if (doCheck && ToUni[(i<<8)+j]!=0) {
                                    System.out.println("ByteToChar " + Hex2(i) + " " +
                                        Hex2(j) + " = 00  Invalid: should be " + Hex4(ToUni[(i<<8)+j]));
                                }
                            }
                        } catch (Throwable e) {
                            if ((j!=0x7F || isEbcdicDbcs) && i!=0xFF && j!=0xFF) {
                                System.out.println("Exception at " + Hex2(i) + " " + Hex2(j));
                                exceptionCount++;
                            }
                        }
                    }
                }
            }

            /*
             * Dump the extended ranges for EUC of more than 2 bytes
             */
            if (type==EUC_JP || type==EUC_TW) {
                int lcount;
                int where;
                byte [] jsource;
                byte [] typebytes;
                if (type==EUC_JP) {
                    lcount = 1;
                    where = 1;
                    jsource = new byte[3];
                    typebytes = new byte[] {(byte)0x8f};
                } else {
                    lcount = 4;
                    where = 2;
                    jsource = new byte[4];
                    typebytes = new byte [] {(byte)0xa2, (byte)0xa3, (byte)0xac, (byte)0xad};
                    jsource [0] = (byte)0x8e;
                }
                for (i=0; i<lcount; i++) {
                    jsource[where-1] = typebytes[i];
                    for (j=0xa1; j<0xff; j++) {
                        jsource[where] = (byte)j;
                        for (k=0xa1; k<0xff; k++) {
                            jsource[where+1] = (byte)k;
                            doToChar(jsource);
                        }
                    }
                }
            }
            System.out.println("");

            /*
             * Dump GB18030 specific ranges.  Only dump a partial range
             */
            if (isGB18030) {
                byte [] xsource = new byte[4];
                for (i=0x81; i<=0xfe; i++) {    /* All that map to BMP */
                    xsource[0] = (byte)i;
                    for (j=0x30; j<=0x39; j++) {
                        if (i>0x85 && j>0x30)
                            continue;
                        xsource[1] = (byte)j;
                        for (k=0x81; k<=0xfe; k++) {
                            if (i>0x85 && k>0x81)
                                continue;
                            xsource[2] = (byte)k;
                            for (m=0x30; m<=0x39; m++) {
                               if (i>0x85 && m>0x30)
                                   continue;
                                xsource[3] = (byte)m;
                                try {
                                    source = newString(xsource);
                                    if (source.charAt(0) != 0xfffd) {
                                        System.out.print("ByteToChar  " + Hex2(i) + " " +
                                                 Hex2(j) + " " + Hex2(k) + " " +
                                                 Hex2(m) + " = ");
                                        for (n=0; n<source.length(); n++) {
                                            System.out.print(Hex4((int)source.charAt(n)) + " ");
                                        }
                                        if (i<0x90)
                                            touniCount++;
                                        System.out.println("");
                                    }
                                } catch (Throwable ee) {
                                    System.out.println("Exception at " + Hex2(i) + " " + Hex2(j) + " " + Hex2(k) + " " + Hex2(m));
                                    exceptionCount++;
                                }
                            }
                        }
                    }
                }
            }
            System.out.println("");


            /*
             * Map Unicode to codepage
             */
            for (i=0; i<=0xffff; i++) {
                /* Do not map surrogate area */
                if (type != CESU_8 && i>=0xd800 && i<=0xdfff)
                    continue;

                chrs[0] = (char)i;
                source = new String(chrs);
                target = getBytes(source);

                if (!equals(target, invalid)) {
                    val = 0;
                    System.out.print("CharToByte  " + Hex4(i) + " = ");
                    for (j=0; j<target.length ; j++) {
                        System.out.print(Hex2((int)target[j]) + " ");
                        if (!isEbcdicDbcs || target[j]<0x0e || target[j]>0x0f)
                            val = (val<<8) + ((int)target[j]&0xff);
                    }
                    if (doCheck && val != FromUni[i]) {
                        System.out.print("  Invalid: should be " + Hex4(FromUni[i]));
                    }
                    System.out.println("");
                    fromCount++;
                } else {
                    if (doCheck && FromUni[i]!=0) {
                        System.out.println("CharToByte  " + Hex4(i) + " = 0000  Invalid: should be " + Hex4(FromUni[i]));
                    }
                }
            }
            if (doSurrogates) {
                System.out.println("Surrogates");
                char [] ssource = new char[2];
                for (i=0; i<1500; i++) {
                    ssource[0] = (char)(0xd800 | i>>10);
                    ssource[1] = (char)(0xdc00 | (i&0x03ff));
                    source = new String(ssource);
                    try {
                        target = getBytes(source);
                     // if (!equals(target, invalid)) {
                            System.out.print("CharToByte  " + Hex4(ssource[0]) + " " + Hex4(ssource[1]) + " = ");
                            for (j=0; j<target.length ; j++) {
                                System.out.print(Hex2((int)target[j]) + " ");
                            }
                            System.out.println("");
                     // }
                    } catch (Throwable ex) {
                        ex.printStackTrace(System.err);
                    }
                }
            }
        } catch (Throwable e) {
            /* If anything goes wrong stack trace */
            e.printStackTrace(System.err);
        }

        System.err.println("Codepage: " + encoding);

        if (type>0) {
            switch (type) {
            case SBCS_ASCII:  System.err.println("Single byte ASCII codepage");  break;
            case SBCS_EBCDIC: System.err.println("Single byte EBCDIC codepage"); break;
            case DBCS_ASCII:  System.err.println("Double byte ASCII codepage");  break;
            case DBCS_EBCDIC: System.err.println("Double byte EBCDIC codepage"); break;
            case DBCS_EUC:    System.err.println("Double byte EUC codepage"); break;
            case EUC_CNKR:    System.err.println("EUC codepage"); break;
            case EUC_JP:      System.err.println("EUC Japanese codepage"); break;
            case EUC_TW:      System.err.println("EUC Taiwan codepage"); break;
            }
        } else {
            if (leadCount==0) {
                System.err.println("Single byte codepage");
            } else {
                if (isEbcdicDbcs) {
                    System.err.println("DBCS EBCDIC codepage");
                } else {
                    if (leadCount==256) {
                        System.err.println("Unicode codepage");
                    } else {
                        System.err.println("DBCS Lead bytes = " + leadCount);
                    }
                }
            }
        }
        if (exceptionCount > 0)
            System.err.println("Exceptions      = " + exceptionCount);
        System.err.println("CP to Unicode   = " + touniCount);
        System.err.println("Unicode to CP   = " + fromCount);
        runtime = System.currentTimeMillis()-runtime;
        System.err.println("Time = "+runtime+" milliseconds");
    }

    private static void doToChar(byte [] b) {
        String s;
        int    k;
        try {
            s = newString(b);
            if (s.charAt(0) != 0xfffd) {
                System.out.print("ByteToChar  ");
                for (k=0; k<b.length; k++) {
                    System.out.print(Hex2(b[k]) + " ");
                }
                System.out.print("= ");
                for (k=0; k<s.length(); k++) {
                    System.out.print(Hex4((int)s.charAt(k)) + " ");
                }
                System.out.println("");
                touniCount++;
            }
        } catch (Throwable e) {
            System.out.println("Exception at ");
            for (k=0; k<b.length; k++) {
                System.out.print(Hex2(b[k]) + " ");
            }
            System.out.println("");
            exceptionCount++;
        }
    }

    /*
     * Read the UCM file
     */
    public static boolean readUCM(String name) {
        int    i, j, v;
        int    len;
        char [] line;
        String lineStr;
        boolean inCharmap = false;
        String hexStr;
        int    unival;
        int    charval;
        int    kind = 0;

        try {
            f = new File(name);
            if (!f.canRead()) {
                System.out.println("Unable to open ucm file: " + name);
                return false;
            }
            isr = new InputStreamReader( new FileInputStream(f), "ISO8859_1");
        } catch (Exception e) {
            System.out.println("Error opening input file: " + name);
            return false;
        }
        ToUni   = new char[0x10000];
        FromUni = new char[0x10000];
        line    = new char[1024];
        for (i=0; i<0x10000; i++) {
            ToUni[i] = 0;
            FromUni[i] = 0;
        }

        len = getline(line, isr);
        while (len>=0) {
            if (inCharmap) {
                if (line[0]=='<' && (line[1]=='u' || line[1]=='U')) {
                    try {
                        hexStr = new String(line, 2, 4);
                        unival = Integer.parseInt(hexStr, 16);
                        charval = getValue(line, 8, len);
                        ToUni[charval]  = (char) unival;
                        FromUni[unival] = (char) charval;
                    //  System.out.println("Map " + hex4(unival) + " to " + hex4(charval));
                    } catch (Exception e) {
                        System.err.println("Invalid line in charmap: " + new String(line, 0, len));
                    }
                } else {
                    if (len>0 && line[0] != '#') {
                        lineStr = new String(line, 0, len);
                        if (!lineStr.regionMatches(true, 0, "end charmap", 0, 11)) {
                            System.err.println("Invalid line in charmap: " + lineStr);
                        }
                    }
                }
            } else {
                lineStr = new String(line, 0, len);
                if (lineStr.regionMatches(true, 0, "<uconv_class>", 0, 13)) {
                    if (lineStr.indexOf("SBCS")>=0) kind=1;
                    if (lineStr.indexOf("sbcs")>=0) kind=1;
                    if (lineStr.indexOf("DBCS")>=0) kind=2;
                    if (lineStr.indexOf("dbcs")>=0) kind=2;
                    if (lineStr.indexOf("mbcs")>=0) kind=2;
                    if (lineStr.indexOf("MBCS")>=0) kind=2;
                    if (lineStr.indexOf("EBCDIC_STATEFUL")>=0) kind=3;
                    if (lineStr.indexOf("ebcdic_stateful")>=0) kind=3;
                    System.out.println("UCM Kind = " + kind);
                }
                if (lineStr.regionMatches(true, 0, "charmap", 0, 7)) {
                    inCharmap = true;
                }
                if (lineStr.regionMatches(true, 0, "<subchar>", 0, 8)) {
                    i = getValue(line, 8, len);
                    if (i>=0)
                        SubChar = (char) i;
                    System.out.println("UCM Subchar = " + Hex4(SubChar));
                }
            }
            len = getline(line, isr);
        }
        try {
            isr.close();
        } catch (Exception e) {
        }

        /* Fix up the CDRA control mappings */
        if (FromUni[0x7f] == 0x1C) {
            FromUni[0x1A] = 0x1A;
              ToUni[0x1A] = 0x1A;
            FromUni[0x1C] = 0x1C;
              ToUni[0x1C] = 0x1C;
            FromUni[0x7F] = 0x7F;
              ToUni[0x7F] = 0x7F;
        }
        /* Fix up for Open Edition line ends */
        if (ToUni[0x0015] == 0x85  && FromUni[0x000a] == 0x25) {
            ToUni[0x0015] = 0x000a;
            FromUni[0x000a] = 0x0015;
        }
        return true;
    }

    /*
     * Get a line from a UCM file
     */
    static int  getline(char[] buf, InputStreamReader isr) {
        int  len = 0;
        char ch;

        try {
            if (retCh != 0)
                ch = retCh;
            else
                ch = (char) isr.read();
            retCh = 0;
            while (ch != '\r' && ch != '\n') {
                buf[len++] = ch;
                ch = (char) isr.read();
            }
            if (ch=='\r') {
                ch = (char) isr.read();
                if (ch != '\n')
                    retCh = ch;
            }
        } catch (Exception e) {
            return -1;
        }
        return len;
    }

    /*
     * Default the types for the converters we can not automatically determine.
     */
    static int  getType(String encoding, int [] range) {
        int  cpage = 0;
        String parseCp = "0";
        if (encoding.startsWith("EUC_TW"))
            return EUC_TW;
        if (encoding.startsWith("EUC_JP"))
            return EUC_JP;
        if (encoding.startsWith("EUC"))
            return EUC_CNKR;
        if (encoding.equals("UTF8"))
            return UTF_8;
        if (encoding.equals("CESU8"))
            return CESU_8;
        if (encoding.startsWith("Cp")) {
            parseCp = encoding.substring(2);
        } else if (encoding.startsWith("ibm-")) {
            parseCp = encoding.substring(4);
        } else if (encoding.startsWith("ibm")) {
            parseCp = encoding.substring(3);
        }
        int i = 0;
        while (i<parseCp.length()) {
            if (parseCp.charAt(i)>='0' && parseCp.charAt(i)<='9') {
                cpage = cpage*10 + (parseCp.charAt(i)-'0');
            } else break;
            i++;
        }
        switch (cpage) {
        case 0:    return AUTO_TYPE;
        case 290 : return SBCS_EBCDIC;
        case 300 : return DBCS_EBCDIC;
        case 301 : range[0]=0x819f; range[1]=0xe0fc; return DBCS_ASCII;
        case 833 : return SBCS_EBCDIC;
        case 834 : return DBCS_EBCDIC;
        case 835 : return DBCS_EBCDIC;
        case 836 : return SBCS_EBCDIC;
        case 837 : return DBCS_EBCDIC;
        case 897 : return SBCS_ASCII;
        case 927 : range[0]=0x81fc; return DBCS_ASCII;
        case 947 : range[0]=0x81fe; return DBCS_ASCII;
        case 951 : range[0]=0x8dfe; return DBCS_ASCII;
        case 954 : return EUC_JP;
        case 964 : return EUC_TW;
        case 970 : return EUC_CNKR;
        case 971 : return DBCS_EUC;
        case 1027: return SBCS_EBCDIC;
        case 1041: return SBCS_ASCII;
        case 1043: return SBCS_ASCII;
        case 1088: return SBCS_ASCII;
        case 1114: return SBCS_ASCII;
        case 1115: return SBCS_ASCII;
        case 1351: range[0]=0x819f; range[1]=0xe0fc; return DBCS_ASCII;
        case 1362: range[0]=0x81fe; return DBCS_ASCII;
        case 1380: range[0]=0x8cfe; return DBCS_ASCII;
        case 1382: return DBCS_EUC;
        case 1383: return EUC_CNKR;
        case 1385: range[0]=0x81ff; return DBCS_ASCII;
        case 33722: return EUC_JP;
        }
        return AUTO_TYPE;
    }

    /*
     * Get a hex value
     */
    static int  getValue(char [] buf, int offset, int max) {
        int   i;
        int   j = 0;
        char  [] values = new char[16];
        try {
            for (i=offset; i<max; i++) {
                if (buf[i]=='#') break;
                if ((buf[i]>='0' && buf[i]<='9') ||
                    (buf[i]>='a' && buf[i]<='f') ||
                    (buf[i]>='A' && buf[i]<='F')) {
                    values[j++] = buf[i];
                }
            }
            return Integer.parseInt(new String(values, 0, j), 16);
        } catch (Exception e) {
            return -1;
        }
    }


    /*
     * Define some utility functions for hex display
     */
    private final static char hexarray[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
    /*
     * hex4: Create a 4 character string representing a short int
     */
    public static String Hex4(int val) {
        char hexs[] = new char[4];
        hexs[0] = hexarray[(val>>12)&0x0f];
        hexs[1] = hexarray[(val>>8)&0x0f];
        hexs[2] = hexarray[(val>>4)&0x0f];
        hexs[3] = hexarray[val&0x0f];
        return new String(hexs);
    }

    /*
     * hex2: Create a 2 character string representing a short int
     */
    public static String Hex2(int val) {
        char hexs[] = new char[2];
        hexs[0] = hexarray[(val>>4)&0x0f];
        hexs[1] = hexarray[val&0x0f];
        return new String(hexs);
    }

    /*
     * Check for equality of a byte array
     */
    public static boolean equals(byte [] b1, byte [] b2) {
        if (b1.length != b2.length)
            return false;
        for (int i=0; i<b1.length; i++) {
            if (b1[i] != b2[i])
                return false;
        }
        return true;
    }
}

