2004-04-12 21:40:49 +00:00
|
|
|
/* ScummVM - Scumm Interpreter
|
|
|
|
* Copyright (C) 2004 The ScummVM project
|
|
|
|
*
|
|
|
|
* The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
* of the License, 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; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*
|
|
|
|
* $Header$
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2004-05-01 06:57:54 +00:00
|
|
|
// Expression parsing module, and string handling functions
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-01 06:57:54 +00:00
|
|
|
// EXPR_ParseArgs() lifted wholesale from SDL win32 initialization code by Sam Lantinga
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-01 01:56:22 +00:00
|
|
|
#include "saga.h"
|
2004-04-12 21:40:49 +00:00
|
|
|
#include "cvar_mod.h"
|
|
|
|
#include "expr.h"
|
|
|
|
|
|
|
|
namespace Saga {
|
|
|
|
|
2004-04-25 14:42:14 +00:00
|
|
|
static const char *EXPR_ErrMsg[] = {
|
2004-04-12 21:40:49 +00:00
|
|
|
"Invalid error state.",
|
|
|
|
"No Error",
|
|
|
|
"Memory allocation failed",
|
|
|
|
"Illegal variable name",
|
|
|
|
"Expected \'=\' or \'(\' in expression",
|
|
|
|
"Expected \'(\' in function call",
|
|
|
|
"Illegal \'(\', identifier is not function",
|
|
|
|
"Expected a value to assign",
|
|
|
|
"Unterminated string literal",
|
|
|
|
"Unmatched parenthesis in function call",
|
|
|
|
"Error reading value string",
|
|
|
|
"Expected a number or boolean",
|
|
|
|
"Unknown variable or function"
|
|
|
|
};
|
|
|
|
|
|
|
|
enum EXPR_Errors {
|
|
|
|
EXERR_ASSERT,
|
|
|
|
EXERR_NONE,
|
|
|
|
EXERR_MEM,
|
|
|
|
EXERR_ILLEGAL,
|
|
|
|
EXERR_EXPR,
|
|
|
|
EXERR_FUNC,
|
|
|
|
EXERR_NOTFUNC,
|
|
|
|
EXERR_RVALUE,
|
|
|
|
EXERR_LITERAL,
|
|
|
|
EXERR_PAREN,
|
|
|
|
EXERR_STRING,
|
|
|
|
EXERR_NUMBER,
|
|
|
|
EXERR_NOTFOUND
|
|
|
|
};
|
|
|
|
|
|
|
|
static enum EXPR_Errors EXPR_ErrorState;
|
|
|
|
|
2004-05-01 06:57:54 +00:00
|
|
|
// Returns the appropriate expression parser error string given an error code.
|
|
|
|
int EXPR_GetError(const char **err_str) {
|
2004-04-12 21:40:49 +00:00
|
|
|
*err_str = EXPR_ErrMsg[EXPR_ErrorState];
|
|
|
|
return EXPR_ErrorState;
|
|
|
|
}
|
|
|
|
|
2004-05-01 06:57:54 +00:00
|
|
|
// Parses an interactive expression.
|
|
|
|
// Sets 'expr_cvar' to the cvar/cfunction identifier input by the user, and
|
|
|
|
// 'rvalue' to the corresponding rvalue ( in an expression ) or argument string
|
|
|
|
// ( in a function call ).
|
|
|
|
//
|
|
|
|
// Memory pointed to by rvalue after return must be explicitly freed by the
|
|
|
|
// caller.
|
|
|
|
int EXPR_Parse(const char **exp_pp, int *len, R_CVAR_P *expr_cvar, char **rvalue) {
|
2004-04-12 21:40:49 +00:00
|
|
|
int i;
|
|
|
|
int in_char;
|
2004-04-27 10:01:15 +00:00
|
|
|
int equ_offset = 0;
|
2004-04-12 21:40:49 +00:00
|
|
|
int rvalue_offset;
|
|
|
|
|
|
|
|
char *lvalue_str;
|
|
|
|
int lvalue_len;
|
|
|
|
char *rvalue_str;
|
|
|
|
int rvalue_len;
|
|
|
|
|
|
|
|
const char *scan_p;
|
|
|
|
int scan_len;
|
|
|
|
const char *expr_p;
|
|
|
|
int expr_len;
|
|
|
|
int test_char = '\0';
|
|
|
|
int have_func = 0;
|
|
|
|
|
|
|
|
R_CVAR_P lvalue_cvar;
|
|
|
|
|
|
|
|
expr_p = *exp_pp;
|
|
|
|
expr_len = strlen(*exp_pp);
|
|
|
|
scan_p = *exp_pp;
|
|
|
|
scan_len = expr_len;
|
|
|
|
|
2004-05-01 06:57:54 +00:00
|
|
|
//*lvalue = NULL;
|
2004-04-12 21:40:49 +00:00
|
|
|
*rvalue = NULL;
|
|
|
|
|
|
|
|
EXPR_ErrorState = EXERR_ASSERT;
|
|
|
|
|
|
|
|
for (i = 0; i <= scan_len; i++, scan_p++) {
|
|
|
|
in_char = *scan_p;
|
|
|
|
if ((i == 0) && isdigit(in_char)) {
|
2004-05-01 06:57:54 +00:00
|
|
|
// First character of a valid identifier cannot be a digit
|
2004-04-12 21:40:49 +00:00
|
|
|
EXPR_ErrorState = EXERR_ILLEGAL;
|
|
|
|
return R_FAILURE;
|
|
|
|
}
|
|
|
|
|
2004-05-01 06:57:54 +00:00
|
|
|
// If we reach a character that isn't valid in an identifier...
|
2004-04-12 21:40:49 +00:00
|
|
|
if ((!isalnum(in_char)) && ((in_char != '_'))) {
|
|
|
|
|
2004-05-01 06:57:54 +00:00
|
|
|
// then eat remaining whitespace, if any
|
2004-04-12 21:40:49 +00:00
|
|
|
equ_offset = strspn(scan_p, R_EXPR_WHITESPACE);
|
|
|
|
test_char = scan_p[equ_offset];
|
2004-05-01 06:57:54 +00:00
|
|
|
// and test for the only valid characters after an identifier
|
|
|
|
if ((test_char != '=') && (test_char != '\0') && (test_char != '(')) {
|
|
|
|
if ((equ_offset == 0) && ((scan_p - expr_p) != expr_len)) {
|
2004-04-12 21:40:49 +00:00
|
|
|
EXPR_ErrorState = EXERR_ILLEGAL;
|
|
|
|
} else {
|
|
|
|
EXPR_ErrorState = EXERR_EXPR;
|
|
|
|
}
|
|
|
|
return R_FAILURE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lvalue_len = (scan_p - expr_p);
|
|
|
|
lvalue_str = (char *)malloc(lvalue_len + 1);
|
|
|
|
|
|
|
|
if (lvalue_str == NULL) {
|
|
|
|
EXPR_ErrorState = EXERR_MEM;
|
|
|
|
return R_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
strncpy(lvalue_str, expr_p, lvalue_len);
|
|
|
|
lvalue_str[lvalue_len] = 0;
|
|
|
|
|
2004-05-01 06:57:54 +00:00
|
|
|
// We now have the lvalue, so attempt to find it
|
2004-04-12 21:40:49 +00:00
|
|
|
lvalue_cvar = CVAR_Find(lvalue_str);
|
|
|
|
if (lvalue_cvar == NULL) {
|
|
|
|
EXPR_ErrorState = EXERR_NOTFOUND;
|
|
|
|
return R_FAILURE;
|
|
|
|
}
|
|
|
|
if (lvalue_str) {
|
|
|
|
free(lvalue_str);
|
|
|
|
lvalue_str = NULL;
|
|
|
|
}
|
|
|
|
|
2004-05-01 06:57:54 +00:00
|
|
|
// Skip parsed character, if any
|
2004-04-12 21:40:49 +00:00
|
|
|
scan_p += equ_offset + 1;
|
|
|
|
scan_len = (scan_p - expr_p);
|
|
|
|
|
2004-05-01 06:57:54 +00:00
|
|
|
// Check if the 'cvar' is really a function
|
2004-04-12 21:40:49 +00:00
|
|
|
have_func = CVAR_IsFunc(lvalue_cvar);
|
|
|
|
|
|
|
|
if (test_char == '(') {
|
|
|
|
if (have_func) {
|
2004-05-01 06:57:54 +00:00
|
|
|
rvalue_str = EXPR_ReadString(&scan_p, &rvalue_len, ')');
|
2004-04-12 21:40:49 +00:00
|
|
|
if (rvalue_str != NULL) {
|
2004-05-01 06:57:54 +00:00
|
|
|
// Successfully read string
|
|
|
|
//CON_Print("Read function parameters \"%s\".", rvalue_str);
|
2004-04-12 21:40:49 +00:00
|
|
|
*expr_cvar = lvalue_cvar;
|
|
|
|
*rvalue = rvalue_str;
|
|
|
|
|
|
|
|
scan_len = (scan_p - expr_p);
|
|
|
|
|
|
|
|
*exp_pp = scan_p;
|
|
|
|
*len -= scan_len;
|
|
|
|
|
|
|
|
EXPR_ErrorState = EXERR_NONE;
|
|
|
|
return R_SUCCESS;
|
|
|
|
} else {
|
|
|
|
EXPR_ErrorState = EXERR_PAREN;
|
|
|
|
return R_FAILURE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
EXPR_ErrorState = EXERR_NOTFUNC;
|
|
|
|
return R_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-01 06:57:54 +00:00
|
|
|
// Eat more whitespace
|
2004-04-12 21:40:49 +00:00
|
|
|
rvalue_offset = strspn(scan_p, R_EXPR_WHITESPACE);
|
|
|
|
|
|
|
|
if (rvalue_offset + i == expr_len) {
|
2004-05-01 06:57:54 +00:00
|
|
|
// Only found single lvalue
|
2004-04-12 21:40:49 +00:00
|
|
|
*expr_cvar = lvalue_cvar;
|
|
|
|
*exp_pp = scan_p;
|
|
|
|
*len -= scan_len;
|
|
|
|
return R_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
scan_p += rvalue_offset;
|
|
|
|
scan_len = (scan_p - expr_p) + 1;
|
|
|
|
|
|
|
|
in_char = *scan_p;
|
|
|
|
in_char = toupper(in_char);
|
|
|
|
|
|
|
|
switch (in_char) {
|
|
|
|
case '\"':
|
|
|
|
scan_p++;
|
|
|
|
scan_len--;
|
|
|
|
rvalue_str = EXPR_ReadString(&scan_p, &rvalue_len, '\"');
|
|
|
|
if (rvalue_str != NULL) {
|
2004-05-01 06:57:54 +00:00
|
|
|
// Successfully read string
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
EXPR_ErrorState = EXERR_LITERAL;
|
|
|
|
return R_FAILURE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
#if 0
|
2004-05-01 06:57:54 +00:00
|
|
|
case 'Y': // Y[es]
|
|
|
|
case 'T': // T[rue]
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2004-05-01 06:57:54 +00:00
|
|
|
case 'N': // N[o]
|
|
|
|
case 'F': // F[alse]
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
|
|
|
|
if (isdigit(in_char) || (in_char == '-') || (in_char == '+')) {
|
|
|
|
rvalue_str = EXPR_ReadString(&scan_p, &rvalue_len, 0);
|
|
|
|
if (rvalue_str != NULL) {
|
2004-05-01 06:57:54 +00:00
|
|
|
// Successfully read string
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
EXPR_ErrorState = EXERR_STRING;
|
|
|
|
return R_FAILURE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
EXPR_ErrorState = EXERR_NUMBER;
|
|
|
|
return R_FAILURE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
*expr_cvar = lvalue_cvar;
|
|
|
|
*rvalue = rvalue_str;
|
|
|
|
|
|
|
|
scan_len = (scan_p - expr_p);
|
|
|
|
|
|
|
|
*exp_pp = scan_p;
|
|
|
|
*len -= scan_len;
|
|
|
|
|
|
|
|
EXPR_ErrorState = EXERR_NONE;
|
|
|
|
return R_SUCCESS;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2004-05-01 06:57:54 +00:00
|
|
|
// Reads in a string of characters from '*string_p' until 'term_char' is
|
|
|
|
// encountered. If 'term_char' == 0, the function reads characters until
|
|
|
|
// whitespace is encountered.
|
|
|
|
// Upon reading a string, the function modifies *string_p and len based on
|
|
|
|
// the number of characters read.
|
|
|
|
char *EXPR_ReadString(const char **string_p, int *len, int term_char) {
|
2004-04-12 21:40:49 +00:00
|
|
|
int string_len;
|
2004-04-27 10:01:15 +00:00
|
|
|
char *str_p = NULL;
|
2004-04-12 21:40:49 +00:00
|
|
|
char *term_p;
|
|
|
|
const char *scan_p;
|
|
|
|
int in_char;
|
|
|
|
|
|
|
|
if (term_char > 0) {
|
|
|
|
term_p = strchr(*string_p, term_char);
|
|
|
|
if (term_p == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
string_len = (int)(term_p - *string_p);
|
|
|
|
str_p = (char *)malloc(string_len + 1);
|
|
|
|
|
|
|
|
if (str_p == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
strncpy(str_p, *string_p, string_len);
|
|
|
|
str_p[string_len] = 0;
|
|
|
|
|
|
|
|
*string_p += (string_len + 1); /* Add 1 for terminating char */
|
|
|
|
*len -= (string_len + 1);
|
|
|
|
} else {
|
|
|
|
scan_p = *string_p;
|
|
|
|
string_len = 0;
|
|
|
|
|
|
|
|
while (scan_p) {
|
|
|
|
in_char = *scan_p++;
|
|
|
|
if (!isspace(in_char)) {
|
|
|
|
string_len++;
|
|
|
|
} else if (string_len) {
|
2004-05-01 06:57:54 +00:00
|
|
|
str_p = (char *)malloc(string_len + 1);
|
2004-04-12 21:40:49 +00:00
|
|
|
if (str_p == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
strncpy(str_p, *string_p, string_len);
|
|
|
|
str_p[string_len] = 0;
|
|
|
|
*string_p += string_len;
|
|
|
|
*len -= string_len;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return str_p;
|
|
|
|
}
|
|
|
|
|
2004-05-01 06:57:54 +00:00
|
|
|
// Parses the string 'cmd_str' into argc/argv format, returning argc.
|
|
|
|
// The resulting argv pointers point into the 'cmd_str' string, so any argv
|
|
|
|
// entries should not be used after cmd_str is deallocated.
|
|
|
|
//
|
|
|
|
// Memory pointed to by expr_argv must be explicitly freed by the caller.
|
|
|
|
int EXPR_GetArgs(char *cmd_str, char ***expr_argv) {
|
2004-04-12 21:40:49 +00:00
|
|
|
int expr_argc;
|
|
|
|
|
|
|
|
expr_argc = EXPR_ParseArgs(cmd_str, NULL);
|
|
|
|
*expr_argv = (char **)malloc((expr_argc + 1) * sizeof(**expr_argv));
|
|
|
|
|
|
|
|
if (expr_argv == NULL) {
|
|
|
|
return R_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPR_ParseArgs(cmd_str, *expr_argv);
|
|
|
|
|
|
|
|
return expr_argc;
|
|
|
|
}
|
|
|
|
|
2004-05-01 06:57:54 +00:00
|
|
|
int EXPR_ParseArgs(char *cmd_str, char **argv) {
|
2004-04-12 21:40:49 +00:00
|
|
|
char *bufp;
|
|
|
|
int argc;
|
|
|
|
|
|
|
|
argc = 0;
|
|
|
|
for (bufp = cmd_str; *bufp;) {
|
2004-05-01 06:57:54 +00:00
|
|
|
// Skip leading whitespace
|
2004-04-12 21:40:49 +00:00
|
|
|
while (isspace(*bufp)) {
|
|
|
|
++bufp;
|
|
|
|
}
|
2004-05-01 06:57:54 +00:00
|
|
|
// Skip over argument
|
2004-04-12 21:40:49 +00:00
|
|
|
if (*bufp == '"') {
|
|
|
|
++bufp;
|
|
|
|
if (*bufp) {
|
|
|
|
if (argv) {
|
|
|
|
argv[argc] = bufp;
|
|
|
|
}
|
|
|
|
++argc;
|
|
|
|
}
|
2004-05-01 06:57:54 +00:00
|
|
|
// Skip over word
|
2004-04-12 21:40:49 +00:00
|
|
|
while (*bufp && (*bufp != '"')) {
|
|
|
|
++bufp;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (*bufp) {
|
|
|
|
if (argv) {
|
|
|
|
argv[argc] = bufp;
|
|
|
|
}
|
|
|
|
++argc;
|
|
|
|
}
|
2004-05-01 06:57:54 +00:00
|
|
|
// Skip over word
|
2004-04-12 21:40:49 +00:00
|
|
|
while (*bufp && !isspace(*bufp)) {
|
|
|
|
++bufp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (*bufp) {
|
|
|
|
if (argv) {
|
|
|
|
*bufp = '\0';
|
|
|
|
}
|
|
|
|
++bufp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (argv) {
|
|
|
|
argv[argc] = NULL;
|
|
|
|
}
|
|
|
|
return (argc);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // End of namespace Saga
|