You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
282 lines
7.3 KiB
282 lines
7.3 KiB
/* ScriptParser Copyright (C) 1999 Jochen Hoenicke.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
package jode.obfuscator;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.IOException;
|
|
import java.io.Reader;
|
|
|
|
///#def COLLECTIONS java.util
|
|
import java.util.Collection;
|
|
import java.util.LinkedList;
|
|
///#enddef
|
|
|
|
public class ScriptParser {
|
|
static int NO_TOKEN = -2;
|
|
static int EOF_TOKEN = -1;
|
|
static int STRING_TOKEN = 0;
|
|
static int NEW_TOKEN = 1;
|
|
static int EQUALS_TOKEN = 2;
|
|
static int COMMA_TOKEN = 3;
|
|
static int OPENBRACE_TOKEN = 4;
|
|
static int CLOSEBRACE_TOKEN = 5;
|
|
static int IDENTIFIER_TOKEN = 6;
|
|
static int NUMBER_TOKEN = 7;
|
|
Scanner scanner;
|
|
|
|
class Scanner {
|
|
BufferedReader input;
|
|
String value;
|
|
String line;
|
|
int column;
|
|
int linenr;
|
|
int pushback = NO_TOKEN;
|
|
|
|
public Scanner(Reader i) {
|
|
input = new BufferedReader(i);
|
|
}
|
|
|
|
public void readString() throws ParseException {
|
|
StringBuffer val = new StringBuffer();
|
|
while (column < line.length()) {
|
|
char c = line.charAt(column++);
|
|
if (c == '"') {
|
|
value = val.toString();
|
|
return;
|
|
}
|
|
if (c == '\\') {
|
|
c = line.charAt(column++);
|
|
switch (c) {
|
|
case 'n':
|
|
val.append('\n');
|
|
break;
|
|
case 't':
|
|
val.append('\t');
|
|
break;
|
|
case 'r':
|
|
val.append('\r');
|
|
break;
|
|
case 'u':
|
|
if (column+4 <= line.length()) {
|
|
try {
|
|
char uni = (char) Integer.parseInt
|
|
(line.substring(column, column+4), 16);
|
|
column += 4;
|
|
val.append(uni);
|
|
} catch (NumberFormatException ex) {
|
|
throw new ParseException
|
|
(linenr,
|
|
"Invalid unicode escape character");
|
|
}
|
|
} else
|
|
throw new ParseException
|
|
(linenr,
|
|
"Invalid unicode escape character");
|
|
break;
|
|
default:
|
|
val.append(c);
|
|
}
|
|
} else
|
|
val.append(c);
|
|
}
|
|
throw new ParseException(linenr,
|
|
"String spans over multiple lines");
|
|
}
|
|
public void readIdentifier() {
|
|
int start = column-1;
|
|
while (column < line.length()
|
|
&& Character.isUnicodeIdentifierPart(line.charAt(column)))
|
|
column++;
|
|
value = line.substring(start, column);
|
|
}
|
|
|
|
public void readNumber() {
|
|
boolean hex = false;
|
|
int start = column-1;
|
|
/* special case for hex numbers */
|
|
if (line.charAt(start) == '0' && line.charAt(column) == 'x') {
|
|
column++;
|
|
hex = true;
|
|
}
|
|
while (column < line.length()) {
|
|
char c = line.charAt(column);
|
|
if (!Character.isDigit(c)) {
|
|
if (!hex)
|
|
break;
|
|
if ((c < 'A' || c > 'F') && (c < 'a' || c > 'f'))
|
|
break;
|
|
}
|
|
column++;
|
|
}
|
|
value = line.substring(start, column);
|
|
}
|
|
|
|
public void pushbackToken(int token) {
|
|
if (pushback != NO_TOKEN)
|
|
throw new IllegalStateException
|
|
("Can only handle one pushback");
|
|
pushback = token;
|
|
}
|
|
|
|
public int getToken() throws ParseException, IOException {
|
|
if (pushback != NO_TOKEN) {
|
|
int result = pushback;
|
|
pushback = NO_TOKEN;
|
|
return result;
|
|
}
|
|
value = null;
|
|
while (true) {
|
|
if (line == null) {
|
|
line = input.readLine();
|
|
if (line == null)
|
|
return EOF_TOKEN;
|
|
linenr++;
|
|
column = 0;
|
|
}
|
|
while (column < line.length()) {
|
|
char c = line.charAt(column++);
|
|
if (Character.isWhitespace(c))
|
|
continue;
|
|
if (c == '#')
|
|
// this is a comment, skip this line
|
|
break;
|
|
if (c == '=')
|
|
return EQUALS_TOKEN;
|
|
if (c == ',')
|
|
return COMMA_TOKEN;
|
|
if (c == '{')
|
|
return OPENBRACE_TOKEN;
|
|
if (c == '}')
|
|
return CLOSEBRACE_TOKEN;
|
|
if (c == '"') {
|
|
readString();
|
|
return STRING_TOKEN;
|
|
}
|
|
if (Character.isDigit(c) || c == '+' || c == '-') {
|
|
readNumber();
|
|
return NUMBER_TOKEN;
|
|
}
|
|
if (Character.isUnicodeIdentifierStart(c)) {
|
|
readIdentifier();
|
|
if (value.equals("new"))
|
|
return NEW_TOKEN;
|
|
return IDENTIFIER_TOKEN;
|
|
}
|
|
throw new ParseException
|
|
(linenr, "Illegal character `"+c+"'");
|
|
}
|
|
line = null;
|
|
}
|
|
}
|
|
|
|
public String getValue() {
|
|
return value;
|
|
}
|
|
|
|
public int getLineNr() {
|
|
return linenr;
|
|
}
|
|
}
|
|
|
|
public ScriptParser(Reader reader) {
|
|
this.scanner = new Scanner(reader);
|
|
}
|
|
|
|
public Object parseClass() throws ParseException, IOException {
|
|
int linenr = scanner.getLineNr();
|
|
int token = scanner.getToken();
|
|
if (token != IDENTIFIER_TOKEN)
|
|
throw new ParseException(linenr, "Class name expected");
|
|
Object instance;
|
|
try {
|
|
Class clazz = Class.forName("jode.obfuscator.modules."
|
|
+scanner.getValue());
|
|
instance = clazz.newInstance();
|
|
} catch (ClassNotFoundException ex) {
|
|
throw new ParseException(scanner.getLineNr(),
|
|
"Class `"+scanner.getValue()
|
|
+"' not found");
|
|
} catch (Exception ex) {
|
|
throw new ParseException(scanner.getLineNr(),
|
|
"Class `"+scanner.getValue()
|
|
+"' not valid: "+ex.getMessage());
|
|
}
|
|
|
|
token = scanner.getToken();
|
|
if (token == OPENBRACE_TOKEN) {
|
|
if (!(instance instanceof OptionHandler))
|
|
throw new ParseException
|
|
(scanner.getLineNr(),
|
|
"Class `"+instance.getClass().getName()
|
|
+"' doesn't handle options.");
|
|
parseOptions((OptionHandler) instance);
|
|
if (scanner.getToken() != CLOSEBRACE_TOKEN)
|
|
throw new ParseException(scanner.getLineNr(), "`}' expected");
|
|
} else
|
|
scanner.pushbackToken(token);
|
|
return instance;
|
|
}
|
|
|
|
public void parseOptions(OptionHandler optionHandler)
|
|
throws ParseException, IOException
|
|
{
|
|
int token = scanner.getToken();
|
|
while (true) {
|
|
if (token == EOF_TOKEN || token == CLOSEBRACE_TOKEN) {
|
|
scanner.pushbackToken(token);
|
|
return;
|
|
}
|
|
if (token != IDENTIFIER_TOKEN)
|
|
throw new ParseException(scanner.getLineNr(),
|
|
"identifier expected");
|
|
String ident = scanner.getValue();
|
|
if (scanner.getToken() != EQUALS_TOKEN)
|
|
throw new ParseException(scanner.getLineNr(),
|
|
"equal sign expected");
|
|
|
|
int linenr = scanner.getLineNr();
|
|
Collection values = new LinkedList();
|
|
do {
|
|
token = scanner.getToken();
|
|
if (token == NEW_TOKEN) {
|
|
values.add(parseClass());
|
|
} else if (token == STRING_TOKEN) {
|
|
values.add(scanner.getValue());
|
|
} else if (token == NUMBER_TOKEN) {
|
|
values.add(new Integer(scanner.getValue()));
|
|
}
|
|
token = scanner.getToken();
|
|
} while (token == COMMA_TOKEN);
|
|
try {
|
|
optionHandler.setOption(ident, values);
|
|
} catch (IllegalArgumentException ex) {
|
|
throw new ParseException(linenr,
|
|
optionHandler.getClass().getName()
|
|
+": "+ex.getMessage());
|
|
} catch (RuntimeException ex) {
|
|
throw new ParseException(linenr,
|
|
optionHandler.getClass().getName()
|
|
+": Illegal value: "
|
|
+ex.getClass().getName()
|
|
+": "+ex.getMessage());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|