package com.netscape.pkisilent.argparser;

// --- BEGIN COPYRIGHT BLOCK ---
// 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; version 2 of the License.
//
// 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 this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// (C) 2007 Red Hat, Inc.
// All rights reserved.
// --- END COPYRIGHT BLOCK ---

/**
 * Copyright John E. Lloyd, 2004. All rights reserved. Permission to use,
 * copy, modify and redistribute is granted, provided that this copyright
 * notice is retained and the author is given credit whenever appropriate.
 *
 * This software is distributed "as is", without any warranty, including
 * any implied warranty of merchantability or fitness for a particular
 * use. The author assumes no responsibility for, and shall not be liable
 * for, any special, indirect, or consequential damages, or any damages
 * whatsoever, arising out of or in connection with the use of this
 * software.
 */

class StringScanner {
    private char[] buf;
    private int idx;
    private int len;
    private String stringDelimiters = "";

    public StringScanner(String s) {
        buf = new char[s.length() + 1];
        s.getChars(0, s.length(), buf, 0);
        len = s.length();
        buf[len] = 0;
        idx = 0;
    }

    public int getIndex() {
        return idx;
    }

    public void setIndex(int i) {
        if (i < 0) {
            idx = 0;
        } else if (i > len) {
            idx = len;
        } else {
            idx = i;
        }
    }

    public void setStringDelimiters(String s) {
        stringDelimiters = s;
    }

    public String getStringDelimiters() {
        return stringDelimiters;
    }

    public char scanChar()
            throws StringScanException {
        int idxSave = idx;
        skipWhiteSpace();
        try {
            if (buf[idx] == '\'') {
                return scanQuotedChar();
            } else {
                return scanUnquotedChar();
            }
        } catch (StringScanException e) {
            idx = idxSave;
            throw e;
        }
    }

    public char scanQuotedChar()
            throws StringScanException {
        StringScanException exception = null;
        char retval = 0;
        int idxSave = idx;

        skipWhiteSpace();
        if (idx == len) {
            exception = new StringScanException(idx, "end of input");
        } else if (buf[idx++] == '\'') {
            try {
                retval = scanUnquotedChar();
            } catch (StringScanException e) {
                exception = e;
            }
            if (exception == null) {
                if (idx == len) {
                    exception = new StringScanException
                            (idx, "end of input");
                } else if (buf[idx++] != '\'') {
                    exception = new StringScanException
                            (idx - 1, "unclosed quoted character");
                }
            }
        } else {
            exception = new StringScanException
                    (idx - 1, "uninitialized quoted character");
        }
        if (exception != null) {
            idx = idxSave;
            throw exception;
        }
        return retval;
    }

    public char scanUnquotedChar()
            throws StringScanException {
        StringScanException exception = null;
        char c, retval = 0;
        int idxSave = idx;

        if (idx == len) {
            exception = new StringScanException(idx, "end of input");
        } else if ((c = buf[idx++]) == '\\') {
            if (idx == len) {
                exception = new StringScanException(idx, "end of input");
            } else {
                c = buf[idx++];
                if (c == '"') {
                    retval = '"';
                } else if (c == '\'') {
                    retval = '\'';
                } else if (c == '\\') {
                    retval = '\\';
                } else if (c == 'n') {
                    retval = '\n';
                } else if (c == 't') {
                    retval = '\t';
                } else if (c == 'b') {
                    retval = '\b';
                } else if (c == 'r') {
                    retval = '\r';
                } else if (c == 'f') {
                    retval = '\f';
                } else if ('0' <= c && c < '8') {
                    int v = c - '0';
                    for (int j = 0; j < 2; j++) {
                        if (idx == len) {
                            break;
                        }
                        c = buf[idx];
                        if ('0' <= c && c < '8' && (v * 8 + (c - '0')) <= 255) {
                            v = v * 8 + (c - '0');
                            idx++;
                        } else {
                            break;
                        }
                    }
                    retval = (char) v;
                } else {
                    exception = new StringScanException
                            (idx - 1, "illegal escape character '" + c + "'");
                }
            }
        } else {
            retval = c;
        }
        if (exception != null) {
            idx = idxSave;
            throw exception;
        }
        return retval;
    }

    public String scanQuotedString()
            throws StringScanException {
        StringScanException exception = null;
        StringBuffer sbuf = new StringBuffer(len);
        char c;
        int idxSave = idx;

        skipWhiteSpace();
        if (idx == len) {
            exception = new StringScanException(idx, "end of input");
        } else if ((c = buf[idx++]) == '"') {
            while (idx < len && (c = buf[idx]) != '"' && c != '\n') {
                if (c == '\\') {
                    try {
                        c = scanUnquotedChar();
                    } catch (StringScanException e) {
                        exception = e;
                        break;
                    }
                } else {
                    idx++;
                }
                sbuf.append(c);
            }
            if (exception == null && idx >= len) {
                exception = new StringScanException(len, "end of input");
            } else if (exception == null && c == '\n') {
                exception = new StringScanException
                        (idx, "unclosed quoted string");
            } else {
                idx++;
            }
        } else {
            exception = new StringScanException(idx - 1,
                    "quoted string must start with \"");
        }
        if (exception != null) {
            idx = idxSave;
            throw exception;
        }
        return sbuf.toString();
    }

    public String scanNonWhiteSpaceString()
            throws StringScanException {
        StringBuffer sbuf = new StringBuffer(len);
        int idxSave = idx;
        char c;

        skipWhiteSpace();
        if (idx == len) {
            StringScanException e = new StringScanException(
                       idx, "end of input");
            idx = idxSave;
            throw e;
        } else {
            c = buf[idx++];
            while (idx < len && !Character.isWhitespace(c)
                    && stringDelimiters.indexOf(c) == -1) {
                sbuf.append(c);
                c = buf[idx++];
            }
            if (Character.isWhitespace(c) ||
                    stringDelimiters.indexOf(c) != -1) {
                idx--;
            } else {
                sbuf.append(c);
            }
        }
        return sbuf.toString();
    }

    public String scanString()
            throws StringScanException {
        int idxSave = idx;
        skipWhiteSpace();
        try {
            if (buf[idx] == '"') {
                return scanQuotedString();
            } else {
                return scanNonWhiteSpaceString();
            }
        } catch (StringScanException e) {
            idx = idxSave;
            throw e;
        }
    }

    public String getString()
            throws StringScanException {
        StringBuffer sbuf = new StringBuffer(len);
        while (idx < len) {
            sbuf.append(buf[idx++]);
        }
        return sbuf.toString();
    }

    public long scanInt()
            throws StringScanException {
        int idxSave = idx;
        char c;
        int sign = 1;

        skipWhiteSpace();
        if ((c = buf[idx]) == '-' || c == '+') {
            sign = (c == '-' ? -1 : 1);
            idx++;
        }
        try {
            if (idx == len) {
                throw new StringScanException(len, "end of input");
            } else if ((c = buf[idx]) == '0') {
                if ((c = buf[idx + 1]) == 'x' || c == 'X') {
                    idx += 2;
                    return sign * scanInt(16, false);
                } else {
                    return sign * scanInt(8, false);
                }
            } else {
                return sign * scanInt(10, false);
            }
        } catch (StringScanException e) {
            idx = idxSave;
            throw e;
        }
    }

    public long scanInt(int radix)
            throws StringScanException {
        return scanInt(radix, /*skipWhite=*/true);
    }

    private String baseDesc(int radix) {
        switch (radix) {
        case 10: {
            return "decimal";
        }
        case 8: {
            return "octal";
        }
        case 16: {
            return "hex";
        }
        default: {
            return "base " + radix;
        }
        }
    }

    public long scanInt(int radix, boolean skipWhite)
            throws StringScanException {
        StringScanException exception = null;
        int charval, idxSave = idx;
        char c;
        long val = 0;
        boolean negate = false;

        if (skipWhite) {
            skipWhiteSpace();
        }
        if ((c = buf[idx]) == '-' || c == '+') {
            negate = (c == '-');
            idx++;
        }
        if (idx >= len) {
            exception = new StringScanException(len, "end of input");
        } else if ((charval = Character.digit(buf[idx++], radix)) == -1) {
            exception = new StringScanException
                    (idx - 1, "malformed " + baseDesc(radix) + " integer");
        } else {
            val = charval;
            while ((charval = Character.digit(buf[idx], radix)) != -1) {
                val = val * radix + charval;
                idx++;
            }
            if (Character.isLetter(c = buf[idx]) ||
                    Character.isDigit(c) || c == '_') {
                exception = new StringScanException
                        (idx, "malformed " + baseDesc(radix) + " integer");
            }
        }
        if (exception != null) {
            idx = idxSave;
            throw exception;
        }
        return negate ? -val : val;
    }

    public double scanDouble()
            throws StringScanException {
        StringScanException exception = null;
        int idxSave = idx;
        char c;
        // parse [-][0-9]*[.][0-9]*[eE][-][0-9]*
        boolean hasDigits = false;
        double value = 0;

        skipWhiteSpace();
        if (idx == len) {
            exception = new StringScanException("end of input");
        } else {
            if ((c = buf[idx]) == '-' || c == '+') {
                // signed
                idx++;
            }
            if (matchDigits()) {
                hasDigits = true;
            }
            if (buf[idx] == '.') {
                idx++;
            }
            if (!hasDigits && (buf[idx] < '0' || buf[idx] > '9')) {
                if (idx == len) {
                    exception = new StringScanException(idx, "end of input");
                } else {
                    exception = new StringScanException(
                            idx, "malformed floating number: no digits");
                }
            } else {
                matchDigits();

                if ((c = buf[idx]) == 'e' || c == 'E') {
                    idx++;
                    if ((c = buf[idx]) == '-' || c == '+') {
                        // signed
                        idx++;
                    }
                    if (buf[idx] < '0' || buf[idx] > '9') {
                        if (idx == len) {
                            exception = new StringScanException(
                                    idx, "end of input");
                        } else {
                            exception = new StringScanException(idx,
                                    "malformed floating number: no digits in exponent");
                        }
                    } else {
                        matchDigits();
                    }
                }
            }
        }
        if (exception == null) {
            //	      if (Character.isLetterOrDigit(c=buf[idx]) || c == '_')
            //	       { exception = new StringScanException (idx,
            //"malformed floating number");
            //	       }
            //	      else
            {
                try {
                    value = Double.parseDouble(new String(buf, idxSave,
                                   idx - idxSave));
                } catch (NumberFormatException e) {
                    exception = new StringScanException(
                            idx, "malformed floating number");
                }
            }
        }
        if (exception != null) {
            idx = idxSave;
            throw exception;
        }
        return value;
    }

    public boolean scanBoolean()
            throws StringScanException {
        StringScanException exception = null;
        int idxSave = idx;
        String testStr = "false";
        boolean testval = false;
        char c;

        skipWhiteSpace();
        if (buf[idx] == 't') {
            testStr = "true";
            testval = true;
        } else {
            testval = false;
        }
        int i = 0;
        for (i = 0; i < testStr.length(); i++) {
            if (testStr.charAt(i) != buf[idx]) {
                if (idx == len) {
                    exception = new StringScanException(idx, "end of input");
                }
                break;
            }
            idx++;
        }
        if (exception == null) {
            if (i < testStr.length() ||
                    Character.isLetterOrDigit(c = buf[idx]) || c == '_') {
                exception = new StringScanException(idx, "illegal boolean");
            }
        }
        if (exception != null) {
            idx = idxSave;
            throw exception;
        }
        return testval;
    }

    public boolean matchString(String s) {
        int k = idx;
        for (int i = 0; i < s.length(); i++) {
            if (k >= len || s.charAt(i) != buf[k++]) {
                return false;
            }
        }
        idx = k;
        return true;
    }

    public boolean matchDigits() {
        int k = idx;
        char c;

        while ((c = buf[k]) >= '0' && c <= '9') {
            k++;
        }
        if (k > idx) {
            idx = k;
            return true;
        } else {
            return false;
        }
    }

    public void skipWhiteSpace() {
        while (Character.isWhitespace(buf[idx])) {
            idx++;
        }
    }

    public boolean atEnd() {
        return idx == len;
    }

    public boolean atBeginning() {
        return idx == 0;
    }

    public void ungetc() {
        if (idx > 0) {
            idx--;
        }
    }

    public char getc() {
        char c = buf[idx];
        if (idx < len) {
            idx++;
        }
        return c;
    }

    public char peekc() {
        return buf[idx];
    }

    public String substring(int i0, int i1) {
        if (i0 < 0) {
            i0 = 0;
        } else if (i0 >= len) {
            i0 = len - 1;
        }
        if (i1 < 0) {
            i1 = 0;
        } else if (i1 > len) {
            i1 = len;
        }
        if (i1 <= i0) {
            return "";
        }
        return new String(buf, i0, i1 - i0);
    }

    public String substring(int i0) {
        if (i0 < 0) {
            i0 = 0;
        }
        if (i0 >= len) {
            return "";
        } else {
            return new String(buf, i0, len - i0);
        }
    }
}
