/* parse.c */
#include "config.h"
/*
* This file is part of TeenyMUD II.
* Copyright(C) 1993, 1994, 1995 by Jason Downs.
* All rights reserved.
*
* TeenyMUD II 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.
*
* TeenyMUD II 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, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*
*/
/* AIX requires this to be the first thing in the file. */
#ifdef __GNUC__
#define alloca __builtin_alloca
#else /* not __GNUC__ */
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#else /* not HAVE_ALLOCA_H */
#ifdef _AIX
#pragma alloca
#endif /* not _AIX */
#endif /* not HAVE_ALLOCA_H */
#endif /* not __GNUC__ */
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif /* HAVE_STDLIB_H */
#include <stdio.h>
#include <sys/types.h>
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif /* HAVE_STRING_H */
#include <ctype.h>
#include "conf.h"
#include "teeny.h"
#include "prims.h"
#include "externs.h"
#include "hash/hash.h"
static char *exec_primitive _ANSI_ARGS_((int, int, int, char *, int, int,
char *[]));
static int can_exec _ANSI_ARGS_((int, int));
static long expr_val _ANSI_ARGS_((char *, char **));
char *exec(player, cause, source, string, flags, argc, argv)
int player, cause, source;
char *string;
int flags, argc;
char *argv[];
{
char buffer[MEDBUFFSIZ+2];
register char *ptr = buffer;
register char *tptr, *nptr, *eptr;
char *attr, anm[3], nbuf[MAXPNAMELEN + 3], *name;
char cbuf[64], pbuf[64], sbuf[64], *wstr;
int aflags, asource, gender, powner;
int increment = 1;
static char *subjective[4] = {"", "it", "she", "he"};
static char *objective[4] = {"", "it", "her", "him"};
static char *possessive[4] = {"", "its", "her", "his"};
static char *reflexive[4] = {"", "itself", "herself", "himself"};
static char *absolute[4] = {"", "its", "hers", "his"};
if (!*string)
return((char *)NULL);
/* make a copy of string to work on */
wstr = (char *)alloca(strlen(string)+1);
if(wstr == (char *)NULL)
panic("exec(): stack allocation failed\n");
strcpy(wstr, string);
if(get_int_elt(player, OWNER, &powner) == -1)
powner = -1;
/* set up pronoun strings, just in case */
gender = genderof(cause);
if(get_str_elt(cause, NAME, &name) == -1)
name = "???"; /* XXX */
strcpy(nbuf, name);
strcat(nbuf, "'s");
snprintf(cbuf, sizeof(cbuf), "#%d", cause);
snprintf(pbuf, sizeof(pbuf), "#%d", player);
snprintf(sbuf, sizeof(sbuf), "#%d", source);
while(*wstr && (ptr - buffer) < MEDBUFFSIZ) {
switch(*wstr) {
case '\\': /* single char escape */
if (wstr[1]) {
wstr++;
}
*ptr++ = *wstr++;
break;
case '{': /* full scale quoted */
wstr++;
if ((tptr = parse_to(wstr, '{', '}'))) {
if(flags & EXEC_PRIM) /* Put the brace back in. */
*ptr++ = '{';
while(*wstr && (ptr - buffer) < MEDBUFFSIZ) {
if (*wstr == '\\' && wstr[1])
wstr++;
*ptr++ = *wstr++;
}
if(flags & EXEC_PRIM) /* Put the brace back in. */
*ptr++ = '}';
wstr = tptr;
} else
*ptr++ = '{';
break;
case '[': /* oh oh, primitive */
wstr++;
if ((nptr = parse_to(wstr, '[', ']'))) {
if ((eptr = exec(player, cause, source, wstr, (flags|EXEC_PRIM),
argc, argv))) {
for(tptr = eptr; *tptr && (ptr - buffer) < MEDBUFFSIZ;
*ptr++ = *tptr++);
ty_free((VOID *)eptr);
} else {
*ptr++ = '[';
if ((ptr - buffer) < MEDBUFFSIZ)
*ptr++ = ']';
}
wstr = nptr;
} else
*ptr++ = '[';
break;
case '$': /* variable sub */
wstr++;
if(*wstr == '(') {
wstr++;
for(nptr = wstr; (*nptr != '\0') && (*nptr != ')'); nptr++);
if(*nptr == ')') {
*nptr++ = '\0';
eptr = (powner == -1) ? (char *)NULL : var_get(powner, wstr);
if(eptr != (char *)NULL) {
for(tptr = eptr; *tptr && (ptr - buffer) < MEDBUFFSIZ;
*ptr++ = *tptr++);
}
wstr = nptr;
} else {
if ((ptr - buffer) < MEDBUFFSIZ)
*ptr++ = '$';
if ((ptr - buffer) < MEDBUFFSIZ)
*ptr++ = '(';
}
} else
*ptr++ = '$';
break;
case '%': /* 'pronoun' sub */
if (wstr[1] && (wstr[1] != '%')) {
register int i = 0;
wstr++;
tptr = (char *)NULL;
if(!isdigit(*wstr)) {
anm[0] = '%';
anm[1] = *wstr;
anm[2] = '\0';
if ((attr_get_parent(cause, anm, &attr, &aflags, &asource) != -1)
&& (attr != (char *)NULL) &&
can_see_attr(player, cause, player, aflags)) { /* override */
tptr = attr;
}
}
if(tptr == (char *)NULL) {
switch(*wstr) {
case 'l':
tptr = "\r\n";
break;
case 't':
tptr = "\t";
break;
case 'n':
case 'N':
tptr = name;
break;
case 's':
case 'S':
tptr = (gender == 0) ? name : subjective[gender];
break;
case 'o':
case 'O':
tptr = (gender == 0) ? name : objective[gender];
break;
case 'p':
case 'P':
tptr = (gender == 0) ? nbuf : possessive[gender];
break;
case 'a':
case 'A':
tptr = (gender == 0) ? nbuf : absolute[gender];
break;
case 'r':
case 'R':
tptr = (gender == 0) ? name : reflexive[gender];
break;
case '#':
tptr = cbuf;
break;
case '!':
tptr = pbuf;
break;
case '@':
tptr = sbuf;
break;
case '\0':
tptr = "";
increment = 0;
break;
default:
if(isdigit(*wstr) && argc && (argv != (char **)NULL)) {
register int argnum = (int)strtol(wstr, &wstr, 10);
increment = 0;
if((argnum >= 0) && (argnum < argc) &&
(argv[argnum] != (char *)NULL)) {
tptr = argv[argnum];
} else
tptr = "";
} else
tptr = "";
break;
}
}
while (tptr[i] && (ptr - buffer) < MEDBUFFSIZ) {
if ((i == 0) && isupper(*wstr)) {
*ptr++ = to_upper(tptr[i]);
} else {
*ptr++ = tptr[i];
}
i++;
}
if(increment)
wstr++;
else
increment = 1;
} else {
if (wstr[1] == '%')
wstr++;
*ptr++ = *wstr++;
}
break;
default: /* just copy it */
*ptr++ = *wstr++;
}
}
*ptr = '\0';
if(flags & EXEC_PRIM) {
ptr = (char *)ty_strdup(exec_primitive(player, cause, source, buffer,
flags, argc, argv), "exec.return");
} else {
ptr = (char *)ty_strdup(buffer, "exec.return");
}
return(ptr);
}
void parse_copy(dest, src, len)
register char *dest, *src;
int len;
{
register char *ptr = dest;
while(*src && isspace(*src))
src++;
while(*src && (ptr - dest) < len) {
if ((*src == '\\') && src[1])
src++;
*ptr++ = *src++;
}
if((ptr > dest) && isspace(ptr[-1])) {
while((ptr >= dest) && isspace(ptr[-1]))
ptr--;
}
*ptr = '\0';
}
/* a simpler argv parser, for exec() arguments and such. */
void parse_sargv(str, limit, argc, argv)
char *str;
int limit, *argc;
char *argv[];
{
register char *ptr, *qtr;
*argc = 0;
if((str == (char *)NULL) || (str[0] == '\0'))
return;
while(limit) {
/* i don't want to overwrite str, so... */
for(ptr = str; *ptr && !isspace(*ptr); ptr++);
qtr = (char *)ty_malloc((ptr - str) + 1, "parse_sargv");
bcopy((VOID *)str, (VOID *)qtr, (ptr - str));
qtr[(ptr - str)] = '\0';
argv[*argc++] = qtr;
while(*ptr && isspace(*ptr))
ptr++;
if(*ptr == '\0')
break;
str = ptr;
limit--;
}
}
void free_argv(argc, argv)
int argc;
char *argv[];
{
register int i;
for(i = 0; i < argc; i++) {
ty_free((VOID *)argv[i]);
}
}
static char *exec_primitive(player, cause, source, string, flags, eargc, eargv)
int player, cause, source;
char *string;
int flags, eargc;
char *eargv[];
{
register int argc;
char *argv[MAXARGS];
static char buffer[MEDBUFFSIZ];
register char *wstr, *nptr;
Hash_Entry *entry;
Prim *fp;
if (!string || !*string)
return((char *)NULL);
wstr = string;
for(argc = 0; argc < MAXARGS; argc++) {
argv[argc] = (char *)NULL;
}
argc = 0;
while(*wstr && argc < MAXARGS) {
/* kill leading spaces */
while(*wstr && isspace(*wstr))
wstr++;
if(!*wstr) {
argc--;
break;
}
switch(*wstr) {
case '{':
wstr++;
if((nptr = parse_to(wstr, '{', '}'))) {
argv[argc] = (char *)ty_malloc(strlen(wstr)+1, "exec_primitive.argv");
parse_copy(argv[argc], wstr, strlen(wstr));
} else { /* syntax error */
argc--;
goto error;
}
wstr = nptr;
break;
case '"':
wstr++;
if((nptr = parse_to(wstr, '\0', '"')))
argv[argc] = exec(player, cause, source, wstr, (flags & ~EXEC_PRIM),
eargc, eargv);
else { /* syntax error */
argc--;
goto error;
}
wstr = nptr;
break;
default:
for(nptr = wstr; *nptr && !isspace(*nptr); nptr++);
if(*nptr)
*nptr++ = '\0';
argv[argc] = (char *)ty_malloc(strlen(wstr)+1, "exec_primitive.argv");
parse_copy(argv[argc], wstr, strlen(wstr));
wstr = nptr;
break;
}
argc++;
}
/* if we've reached here, we've parsed the arg list. yay. */
if((entry = Hash_FindEntry(&Prim_Table, argv[0])) == (Hash_Entry *)0) {
snprintf(buffer, sizeof(buffer), "#-1 NO SUCH PRIMITIVE \"%s\"", argv[0]);
free_argv(argc, argv);
return(buffer);
}
fp = (Prim *)Hash_GetValue(entry);
if (((fp->nargs+1) != argc) && !(fp->flags & PRIM_VARARGS)) {
snprintf(buffer, sizeof(buffer),
"#-1 PRIMITIVE \"%s\" EXPECTS %d ARGUMENTS", argv[0], fp->nargs);
free_argv(argc, argv);
return(buffer);
} else if((fp->nargs != -1) && (argc <= fp->nargs) &&
(fp->flags & PRIM_VARARGS)) {
snprintf(buffer, sizeof(buffer),
"#-1 PRIMITIVE \"%s\" EXPECTS AT LEAST %d ARGUMENTS",
argv[0], fp->nargs);
free_argv(argc, argv);
return(buffer);
}
if(!can_exec(player, fp->perms)) {
snprintf(buffer, sizeof(buffer), "#-1 %s: PERMISSION DENIED", argv[0]);
free_argv(argc, argv);
return(buffer);
}
buffer[0] = '\0';
(fp->func)(player, cause, source, argc, argv, buffer, sizeof(buffer));
return(buffer);
error:
if(argv[0]) { /* we have a primitive name, yay. */
snprintf(buffer, sizeof(buffer), "#-1 SYNTAX ERROR NEAR \"%s\"", argv[0]);
} else
strcpy(buffer, "#-1 SYNTAX ERROR");
free_argv(argc, argv);
return(buffer);
}
static int can_exec(player, perms)
int player, perms;
{
if((perms & PRIM_GOD) && !isGOD(player))
return(0);
if((perms & PRIM_WIZ) && !isWIZARD(player))
return(0);
return(1);
}
int genderof(player)
int player;
{
char *str;
int aflags, source;
if ((attr_get_parent(player, SEX, &str, &aflags, &source) == -1)
|| (str == (char *)NULL))
return (0);
switch (to_lower(str[0])) {
case 'n':
case 'i':
return (1);
case 'f':
return (2);
case 'm':
return (3);
default:
return (0);
}
}
/* expression evaluator */
static long expr_val(str, nptr)
register char *str;
char **nptr;
{
register long val = 0;
int error;
switch(str[0]) {
case '(':
str++;
if((*nptr = parse_to(str, '(', ')'))) {
val = expr_parse(str, &error);
if(error)
*nptr = (char *)NULL;
}
break;
case '-':
str++;
if(!*str)
*nptr = (char *)NULL;
else {
if(*str == '(') { /* awe, fuck */
str++;
if((*nptr = parse_to(str, '(', ')'))) {
if((val = expr_parse(str, &error)))
val = -val;
if(error)
*nptr = (char *)NULL;
}
} else {
val = strtol(str, nptr, 10);
if(*nptr == str)
*nptr = (char *)NULL;
else
val = -val;
}
}
break;
case '~':
str++;
if(!*str)
*nptr = (char *)NULL;
else {
if(*str == '(') { /* argh */
str++;
if((*nptr = parse_to(str, '(', ')'))) {
if((val = expr_parse(str, &error)))
val = ~val;
if(error)
*nptr = (char *)NULL;
}
} else {
val = strtol(str, nptr, 10);
if(*nptr == str)
*nptr = (char *)NULL;
else
val = ~val;
}
}
break;
case '!':
str++;
if(!*str)
*nptr = (char *)NULL;
else {
if(*str == '(') { /* die, scum */
str++;
if((*nptr = parse_to(str, '(', ')'))) {
if((val = expr_parse(str, &error)))
val = !val;
if(error)
*nptr = (char *)NULL;
}
} else {
val = strtol(str, nptr, 10);
if(*nptr == str)
*nptr = (char *)NULL;
else
val = !val;
}
}
break;
default:
if(isdigit(str[0])) {
val = strtol(str, nptr, 10);
if(*nptr == str)
*nptr = (char *)NULL;
} else {
*nptr = (char *)NULL;
}
break;
}
return(val);
}
long expr_parse(str, error)
char *str;
int *error;
{
register long left = 0, right = 0;
int first = 1;
char *expr, *nptr;
if (!str) {
*error = 1;
return(0);
} else
*error = 0;
while(*str && !*error) {
while(*str && isspace(*str))
str++;
if(!*str)
break;
if(first) {
left = expr_val(str, &nptr);
if(!nptr) {
*error = 1;
break;
}
for(str = nptr; *str && isspace(*str); str++);
if(!*str)
break;
first = 0;
}
expr = str;
while(*str && strchr("*/%+-<>=!&^|", *str))
str++;
while(*str && isspace(*str))
str++;
if(!*str || (!isdigit(*str) && !strchr("(-~!", *str))) {
*error = 1;
break;
}
right = expr_val(str, &nptr);
if(!nptr) {
*error = 1;
break;
}
str = nptr;
/* evaluate */
switch(expr[0]) {
case '*': /* multiply */
left *= right;
break;
case '/': /* divide */
left /= right;
break;
case '%': /* modulo */
left %= right;
break;
case '+': /* addition */
left += right;
break;
case '-': /* substraction */
left -= right;
break;
case '<':
switch(expr[1]) {
case '<': /* shift */
left = left << right;
break;
case '=': /* lesser than or equal */
left = (left <= right);
break;
default: /* lesser than */
left = (left < right);
break;
}
break;
case '>':
switch(expr[1]) {
case '>': /* shift */
left = left >> right;
break;
case '=': /* greater than or equal */
left = (left >= right);
break;
default: /* greater than */
left = (left > right);
break;
}
break;
case '=':
if(expr[1] != '=') {
*error = 1;
break;
}
left = (left == right);
break;
case '!':
if(expr[1] != '=') {
*error = 1;
break;
}
left = (left != right);
break;
case '^':
left ^= right;
break;
case '&':
switch(expr[1]) {
case '&':
left = (left && right);
break;
default:
left &= right;
break;
}
break;
case '|':
switch(expr[1]) {
case '|':
left = (left || right);
break;
default:
left |= right;
break;
}
break;
default:
*error = 1;
}
}
return(left);
}