#include <stdio.h> #include <string.h> #include <ctype.h> #include "db.h" #include "externs.h" #include "config.h" #include "softcode.h" #include "scommands.h" #include "funcs.h" CODE *code_list = NULL; CODE *code_ptr = NULL; void show_line_and_end() { if(!code_ptr) return; notify(code_ptr->executor, tprintf("%d %s", code_ptr->pline->line, code_ptr->pline->code)); end_program(code_ptr->program, 0); return; } static int next_pid() { static int pid = 0; CODE *c = NULL; int i; if(pid >= 65535) pid = 0; for(i = 0;i < 2;++i) { while(pid++ < 65535) { for(c = code_list;c;c = c->next) { if(c->pid == pid) break; } if(!c) break; } if(!c) break; if(pid == 65535) pid = 0; } if(i == 2) /* No available pids */ return(-1); return(pid); } int is_running(PROG *program) { CODE *c; for(c = code_list;c;c = c->next) if(c->program == program) return(1); return(0); } void syntax_error() { if(!code_ptr || !code_ptr->pline) return; notify(code_ptr->executor, tprintf("|+R|Syntax error on line |+Y|%d|+R|.", code_ptr->pline->line)); notify(code_ptr->executor, tprintf("%d %s", code_ptr->pline->line, code_ptr->pline->code)); end_program(code_ptr->program, 0); } void end_program(PROG *program, int showmsg) { CODE *c, *cprev = NULL; SCVAR *var, *varnext; SCIF *scif, *scifnext; SCFOR *scfor, *scfornext; SCINPUT *input, *inputnext; SCGOSUB *gosub, *gosubnext; if(code_ptr && showmsg && !code_ptr->triggered) notify(code_ptr->executor, tprintf("|+G|Program |+Y|%s |+G|finished.", code_ptr->program->name)); for(c = code_list;c;c = c->next) { if(c->program == program) break; cprev = c; } if(!c) return; if(!cprev) code_list = code_list->next; else cprev->next = c->next; for(var = c->vars;var;var = varnext) { varnext = var->next; stack_free(var->name); stack_free(var->cvalue); stack_free(var); } for(scif = c->iflist;scif;scif = scifnext) { scifnext = scif->next; stack_free(scif); } for(scfor = c->forlist;scfor;scfor = scfornext) { scfornext = scfor->next; stack_free(scfor); } for(gosub = c->gosublist;gosub;gosub = gosubnext) { gosubnext = gosub->next; stack_free(gosub); } for(input = c->input;input;input = inputnext) { inputnext = input->next; stack_free(input); } stack_free(c); code_ptr = NULL; } static void eat_whitespace(char *command) { char *p; for(p = command;*p;p++) { if(*p == '"') { p++; while(*p && *p != '"') p++; continue; } if(isspace(*p)) if(isspace(*(p+1)) || *(p+1) == '=') { extract_char(p); p--; continue; } if(*p == '=') if(isspace(*(p+1))) { extract_char(p+1); p--; continue; } } } static char *skip_quotes(char *str) { while(*str == '"') { while(*str && *str != '"') str++; if(*str) str++; } return(str); } static void expand_variables(char *pure, char *retbuf) { char *p, *s, *b, *v; int ch; /* 0 if variable is an integer, 1 if char or string */ char varname[4096]; for(b = retbuf, p = pure;*p;) { ch = 0; if(*p == '"') { if(!*(s = skip_quotes(p))) { strcpy(b, p); return; } else { strncpy(b, p, s-p); *(b+(s-p)) = '\0'; } } if(*p != '~') { *b++ = *p++; continue; } for(v = ++p;isalnum(*v);v++); /* Whole loop */ strncpy(varname, p, v-p); *(varname+(v-p)) = '\0'; if(*v == '$') { ch = 1; v++; } if(ch) strcpy(b, cvar_val(varname)); else sprintf(b, "%d", ivar_val(varname)); b += strlen(b); p = v; } *b = '\0'; } static void parse_functions(char *str, char *retbuf) { char buf[4096], buf2[4096]; char buff[4096]; /* Pass this to the functions */ char *args[10]; int num_args; char *p, *q, *r, *s, *b = retbuf; int i, deep; int check = 0; struct fun *func; *retbuf = '\0'; strcpy(buf, str); for(p = buf;*p;p++) { if(check++ > 999) { notify(code_ptr->executor, tprintf("|+R|Recursion check in |+Y|%d|+R|.", code_ptr->pline->line)); show_line_and_end(); return; } for(;;) { if(*p != '"') break; *b++ = *p++; while(*p && *p != '"') *b++ = *p++; *b = '\0'; if(!*p) break; *b++ = *p++; *b = '\0'; } if(*p == ']') { syntax_error(); return; } if(*p != '[') { *b++ = *p; continue; } p++; /* Don't save the '[' to the return buffer */ if(*p == ']') { extract_char(p-1); extract_char(p-1); p = buf; p--; *retbuf = '\0'; b = retbuf; continue; } if(!strchr(p, ']')) { syntax_error(); return; } r = NULL; q = p; while((q = strchr(q, '('))) r = q++; q = r; if(!r) { b = retbuf; *b = '\0'; continue; } r--; while(*r != '[' && *r != '(' && *r != ',') r--; r++; strncpy(buf2, r, q-r); *(buf2+(q-r)) = '\0'; if(!(func = lookup_func(buf2))) { if(!chk_user_func(code_ptr->object, buf, r)) { syntax_error(); return; } p = buf; p--; b = retbuf; *b = '\0'; continue; } s = buf2; *s = '\0'; deep = 0; num_args = 0; for(i = 0;i < 10;++i) args[i] = NULL; for(++q;*q && *q != ')';q++) { switch(*q) { case ',': if(num_args < 9 && !deep) { args[num_args] = stack_string_alloc(buf2, 0); s = buf2; *s = '\0'; num_args++; } break; case '{': deep++; break; case '}': if(!deep) { notify(code_ptr->executor, tprintf("|+R|Unmatched } in |+Y|%d|+R|.", code_ptr->pline->line)); show_line_and_end(); return; } deep--; break; default: if(!deep) { *s++ = *q; *s = '\0'; } } } if(*buf2) { args[num_args] = stack_string_alloc(buf2, 0); num_args++; } if(!*q) { syntax_error(); return; } if(deep) { notify(code_ptr->executor, tprintf("|+R|Unmatched { in |+Y|%d|+R|.", code_ptr->pline->line)); show_line_and_end(); return; } if(func->nargs != -1 && num_args != func->nargs) { notify(code_ptr->executor, tprintf("|+R|Expect %d args for function |+G|%s |+R|in |+Y|%d|+R|.", func->nargs, func->name, code_ptr->pline->line)); show_line_and_end(); return; } for(i = 0;i < num_args;++i) { if(*args[i] == '"') { extract_char(args[i]); if(*(args[i]+strlen(args[i])-1) != '"') { notify(code_ptr->executor, tprintf("|+R|Unterminated string in |+Y|%d|+R|.", code_ptr->pline->line)); show_line_and_end(); return; } extract_char(args[i]+strlen(args[i])-1); } } *buff = '\0'; if(func->nargs == -1) deep = func->func(buff, args, code_ptr->executor, num_args); else deep = func->func(buff, args, code_ptr->executor); if(deep) { notify(code_ptr->executor, tprintf("|+R|Error returned from function %s()", func->name)); notify(code_ptr->executor, tprintf("Message returned: %s", buff)); end_program(code_ptr->program, 0); return; } while(r <= q) { extract_char(r); q--; } if(*(r-1) == '[' && *r == ']') { r--; extract_char(r); extract_char(r); } strncpy(buf2, buf, r-buf); *(buf2+(r-buf)) = '\0'; if(func->type == FNUM) sprintf(buf2+strlen(buf2), "%s%s", buff, r); else sprintf(buf2+strlen(buf2), "\"%s\"%s", buff, r); strcpy(buf, buf2); b = retbuf; *b = '\0'; p = buf; p--; } /* Terminate retbuf */ *b = '\0'; } static void process_line(PROGLINE *pline) { char command[4096]; char buf[4096]; char arg1[4096], arg2[4096], *pure1, *pure2; SCSET *s; /* Make a copy of the code so we can modify it */ strcpy(command, pline->code); eat_whitespace(command); for(pure1 = command;*pure1 && !isspace(*pure1);pure1++); if(!*pure1) { pure1 = ""; pure2 = ""; } else { *pure1++ = '\0'; for(pure2 = pure1;*pure2 && *pure2 != '=';pure2++); if(!*pure2) pure2 = ""; else *pure2++ = '\0'; } expand_variables(pure1, arg1); expand_variables(pure2, arg2); parse_functions(arg1, buf); if(!code_ptr) return; strcpy(arg1, buf); parse_functions(arg2, buf); if(!code_ptr) return; strcpy(arg2, buf); for(s = softcode_command_set;*(s->name);s++) if(!string_compare(s->name, command)) break; if(!*(s->name)) { syntax_error(); return; } if(s->flags & ARG1 || s->flags & PURE1) { if(s->flags & ARG1) { if(s->flags & ARG2 || s->flags & PURE2) { if(s->flags & ARG2) s->func(arg1, arg2); else s->func(arg1, pure2); } else { if(*pure2) { syntax_error(); return; } s->func(arg1); } } else { if(s->flags & ARG2 || s->flags & PURE2) { if(s->flags & ARG2) s->func(pure1, arg2); else s->func(pure1, pure2); } else { if(*pure2) { syntax_error(); return; } s->func(pure1); } } } else { if(*pure1 || *pure2) { syntax_error(); return; } s->func(); } } char *get_command(char *str) { char command[4096]; char *p; if(!(p = strchr(str, ' '))) strcpy(command, str); else { strncpy(command, str, p-str); *(command+(p-str)) = '\0'; } return(stack_string_alloc(command, 0)); } static void parse_one_line() { /* Sanity check */ if(!code_ptr) return; if(code_ptr->wait) { if(now >= code_ptr->wait) code_ptr->wait = 0; else return; } if(code_ptr->input) return; while(!string_compare(get_command(code_ptr->pline->code), "rem")) code_ptr->pline = code_ptr->pline->next; process_line(code_ptr->pline); /* See if the program was aborted */ if(!code_ptr) return; if(code_ptr->advance) code_ptr->pline = code_ptr->pline->next; else code_ptr->advance =1; if(!code_ptr->pline) end_program(code_ptr->program, 1); } void run_softcode() { /* See if there's anything to execute */ if(!code_list) return; if(!code_ptr) code_ptr = code_list; parse_one_line(); if(code_ptr) code_ptr = code_ptr->next; /* Keep the stack small */ stack_unalloc(); } static CODE *new_code() { CODE *newcode, *c; newcode = (CODE *)stack_alloc(sizeof(CODE), 1, 0); newcode->program = NULL; newcode->pline = NULL; newcode->vars = NULL; newcode->iflist = NULL; newcode->forlist = NULL; newcode->gosublist = NULL; newcode->input = NULL; newcode->advance = 1; newcode->wait = 0; newcode->next = NULL; if(!code_list) code_list = newcode; else { for(c = code_list;c->next;c = c->next); c->next = newcode; } return(newcode); } static void setup_vars(OBJ *player, CODE *code, char **args) { SCVAR *newvar; int i; bool last = 0; /* Setup ~player$ */ newvar = new_scvar(code, "player", VAR_STR); newvar->can_modify = 0; newvar->cvalue = stack_string_realloc(newvar->cvalue, tprintf("\"#%d\"", player->dbref)); /* Setup ~player */ newvar = new_scvar(code, "player", VAR_NUM); newvar->can_modify = 0; newvar->nvalue = player->dbref; /* Setup ~arg1$ through ~arg9$ for user softcode functions */ for(i = 1;i < 10;++i) { newvar = new_scvar(code, tprintf("arg%d", i), VAR_STR); newvar->can_modify = 0; } for(i = 0;i < 10;++i) { newvar = new_scvar(code, tprintf("wm%d", i), VAR_STR); if(args && !last) { if(args[i]) newvar->cvalue = stack_string_realloc(newvar->cvalue, tprintf("\"%s\"", args[i])); else last = 1; } newvar->can_modify = 0; } } static void run_it(OBJ *player, OBJ *thing, PROG *program, int start_line, int triggered, char **args) { CODE *newcode; int pid; if((pid = next_pid()) < 0) { notify(player, "No available PIDs!"); return; } newcode = new_code(); newcode->pid = pid; newcode->executor = player; newcode->object = thing; newcode->triggered = triggered; newcode->start_time = now; newcode->program = program; newcode->pline = program->program; setup_vars(player, newcode, args); if(start_line != 1) while(newcode->pline && newcode->pline->line != start_line) newcode->pline = newcode->pline->next; if(!newcode->pline) { notify(player, tprintf("Invalid start line: %d", start_line)); end_program(newcode->program, 0); return; } } static int num_running(OBJ *player) { CODE *c; int num = 0; for(c = code_list;c;c = c->next) if(c->executor == player) num++; return(num); } void do_run(OBJ *player, char *arg1, char *arg2) { OBJ *thing; int start_line = 1; char *progname; PROG *program; char **args; int num_args = 0; char *p; if(num_running(player) >= config.max_queue) { notify(player, "You may not add anymore programs to the queue."); return; } if(*arg2) { if((p = strchr(arg2, ':'))) { *p++ = '\0'; start_line = atoi(p); if(start_line < 1 || start_line > 65535) { notify(player, tprintf("Invalid start line: %d", start_line)); return; } } } if(!(progname = strchr(arg1, '/'))) { notify(player, "Usage: run <object>/<program>"); return; } *progname++ = '\0'; thing = match_object(player, arg1, NOTYPE); if(!thing) { notify(player, no_match(arg1)); return; } if(!(program = find_prog(thing, progname))) { notify(player, tprintf("Program %s doesn't exist on %s.", progname, unparse_object(player, thing))); return; } if(!could_doit(player, thing, "LUSE") && !power(player, POW_SECURITY)) { notify(player, perm_denied()); return; } args = (char **)stack_alloc(sizeof(char *), 0, 0); args[0] = NULL; /* Parse up the args */ while((p = parse_up(&arg2, ','))) { args = (char **)stack_realloc_tmp(args, sizeof(char *)*(num_args+2)); args[num_args++] = p; args[num_args] = NULL; } run_it(player, thing, program, start_line, 0, args); } void do_kill(OBJ *player, char *arg) { int pid; char *pidlist = NULL; CODE *c, *cnext; if(!strcmp(arg, "ALL")) { for(c = code_list;c;c = cnext) { cnext = c->next; if(c->executor == player || power(player, POW_QUEUE)) { if(!pidlist) pidlist = stack_string_alloc(tprintf("%d", c->pid), 0); else pidlist = stack_string_realloc_tmp(pidlist, tprintf("%s, %d", pidlist, c->pid)); end_program(c->program, 0); } } if(!pidlist) notify(player, "No processes to kill."); else notify(player, tprintf("Process(es) %s killed.", pidlist)); return; } pid = atoi(arg); if(pid < 1 || pid > 65535) { notify(player, "Invalid PID."); return; } for(c = code_list;c;c = c->next) if(c->pid == pid) break; if(!c) { notify(player, tprintf("No such process: %d", pid)); return; } notify(player, tprintf("Program %s(%d) killed.", c->program->name, pid)); end_program(c->program, 0); } void show_softcode_queue(OBJ *player) { int num = 0; CODE *c; for(c = code_list;c;c = c->next) { if(c->object != player && !power(player, POW_QUEUE)) continue; if(!num++) { notify(player, "|+W|softcode commands:"); notify(player, "|+G| PID PROGRAM LINE OBJECT EXECUTOR"); } notify(player, tprintf("|+Y|%-5d %s %-5d %s %s", c->pid, my_ljust(c->program->name, 8), c->pline->line, my_ljust(unparse_object(player, c->object), 15), unparse_object(player, c->executor))); } if(!num) notify(player, "|+W|No programs in softcode queue."); } int check_softcode_match(OBJ *player, char *command) { OBJ *thing; PROG *p = NULL; char **args; if(num_running(player) >= config.max_queue) return(0); for(thing = player->location->contents;thing;thing = thing->next_con) { for(p = thing->program;p;p = p->next) if(wildcard_match(p->trigger, command, &args)) break; if(p) break; } if(!p || !thing) { for(p = player->location->program;p;p = p->next) if(wildcard_match(p->trigger, command, &args)) break; if(!p) return(0); thing = player->location; } if(!could_doit(player, thing, "LUSE")) return(0); run_it(player, thing, p, 1, 1, args); return(1); }