/* 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); }