#include "copyright.h" #include "config.h" #include "money.h" #include "db.h" #include "interface.h" #include "externs.h" #include "params.h" #include "match.h" #include <ctype.h> extern char *lowercase, *uppercase; #define DOWNCASE(x) (lowercase[x]) static char buf[BUFFER_LEN]; void editor(dbref player, char *command); void do_insert(dbref player, dbref program, int arg[], int argc); void do_delete(dbref player, dbref program, int arg[], int argc); void do_quit(dbref player, dbref program); void do_list(dbref player, dbref program, int arg[], int argc); void insert(dbref player, char *line1); line *get_new_line(void); line *read_program(dbref i); char *macro_expansion(macrotable *node, char *match); void do_compile(dbref player, dbref program); void free_line(line *l); void free_prog_text(line *l); void val_and_head(dbref player, int arg[], int argc); void do_list_header(dbref player, dbref program); void toggle_numbers(dbref player); /* Editor routines --- Also contains routines to handle input */ /* This routine determines if a player is editing or running an interactive command. It does it by checking the frame pointer field of the player --- if the program counter is NULL, then the player is not running anything The reason we don't just check the pointer but check the pc too is because I plan to leave the frame always on to save the time required allocating space each time a program is run. */ void interactive(dbref player, char *command) { frame *fr; if ((fr = find_frame(DBFETCH(player)->sp.player.pid)) && (fr->status == STATUS_READ) && (fr->player == player) && (DBFETCH(player)->curr_prog == NOTHING)) { if (!string_compare(command, BREAK_COMMAND)) fr->status = STATUS_DEAD; else { if (fr->argument.top >= STACK_SIZE) { notify(player, player, "Program stack overflow."); fr->status = STATUS_DEAD; return; } fr->argument.st[fr->argument.top].type = PROG_STRING; fr->argument.st[fr->argument.top++].data.string = dup_string(command); fr->status = STATUS_RUN; } } else editor(player, command); DBDIRTY(player); } char *macro_expansion(macrotable *node, char *match) { int value; if (!node) return NULL; else { value = string_compare(match, node->name); if (value < 0) return macro_expansion(node->left, match); else if (value > 0) return macro_expansion(node->right, match); else return dup_string (node->definition); } } macrotable *new_macro(char *name, char *definition, dbref player) { macrotable *newmacro; int i; newmacro = (macrotable *)malloc (sizeof (macrotable)); for (i = 0; name[i]; i++) buf[i] = DOWNCASE(name[i]); buf[i] = '\0'; newmacro->name = dup_string(buf); newmacro->definition = dup_string(definition); newmacro->implementor = player; newmacro->left = NULL; newmacro->right = NULL; return (newmacro); } int grow_macro_tree(macrotable *node, macrotable *newmacro) { int value; value = strcmp (newmacro->name, node->name); if (!value) return 0; else if (value < 0) { if (node->left) return grow_macro_tree (node->left, newmacro); else { node->left = newmacro; return 1; } } else if (node->right) return grow_macro_tree (node->right, newmacro); else { node->right = newmacro; return 1; } } void insert_macro(char *word[], dbref player) { macrotable *newmacro; newmacro = new_macro (word[1], word[2], player); if (!macrotop) macrotop = newmacro; else if (!grow_macro_tree(macrotop, newmacro)) notify (player, player, "Oopsie! That macro's already been defined."); else notify (player, player, "Entry created."); } void do_list_tree(macrotable *node, char *first, char *last, int length, dbref player) { static char buff[BUFSIZ]; if (!node) return; else { if (strncmp(node->name, first, strlen(first)) >= 0) do_list_tree(node->left, first, last, length, player); if ((strncmp(node->name, first, strlen(first)) >= 0) && (strncmp(node->name, last, strlen(last)) <= 0)) { if (length) { sprintf(buff, "%-16s %-16s %s", node->name, unparse_name(node->implementor), node->definition); notify(player, player, buff); buff[0] = '\0'; } else { sprintf(buff + strlen(buff), "%-16s", node->name); if (strlen(buff) > 70) { notify(player, player, buff); buff[0] = '\0'; } } } if (strncmp(last, node->name, strlen(last)) >= 0) do_list_tree(node->right, first, last, length, player); if ((node == macrotop) && !length) { notify(player, player, buff); buff[0] = '\0'; } } } void list_macros(char *word[], int k, dbref player, int length) { if (!k--) do_list_tree(macrotop, "a", "z", length, player); else do_list_tree(macrotop, word[0], word[k], length, player); notify(player, player, "End of list."); return; } int erase_node(macrotable *oldnode, macrotable *node, char *killname) { if (!node) return 0; else if(strcmp(killname, node->name) < 0) return erase_node(node, node->left, killname); else if(strcmp(killname, node->name)) return erase_node(node, node->right, killname); else { if (node == oldnode->left) { oldnode->left = node->left; if (node->right) grow_macro_tree (macrotop, node->right); free ((void *) node); return 1; } else { oldnode->right = node->right; if (node->left) grow_macro_tree (macrotop, node->left); free ((void *) node); return 1; } } } void kill_macro(char *word[], dbref player) { if (!Wizard(player)) { notify (player, player, "I'm sorry, Dave, I can't let you do that."); return; } else if (!macrotop) { notify (player, player, "You've got nothing and you want to kill? Sheesh!"); return; } else if (!string_compare(word[0], macrotop->name)) { macrotable *macrotemp = macrotop; int whichway = (macrotop->left) ? 1 : 0; macrotop = whichway ? macrotop->left : macrotop->right; if (macrotop && (whichway ? macrotemp->right : macrotemp->left)) grow_macro_tree(macrotop, whichway ? macrotemp->right : macrotemp->left); free ((void *) macrotemp); notify (player, player, "Entry removed."); } else if (erase_node(macrotop, macrotop, word[0])) notify (player, player, "Entry removed."); else notify (player, player, "Entry to remove not found."); } /* The editor itself --- this gets called each time every time to * parse a command. */ void editor(dbref player, char *command) { dbref program; int arg[MAX_ARG+1]; char buff[BUFFER_LEN]; char *word[MAX_ARG+1]; int i, j; /* loop variables */ program = DBFETCH(player)->curr_prog; /* check to see if we are insert mode */ if (DBFETCH(player)->sp.player.insert_mode) { insert(player, command); /* insert it! */ return; } /* parse the commands */ for (i = 0; i <= MAX_ARG && *command; i++) { while (*command && isspace(*command)) command++; j = 0; while (*command && !isspace(*command)) { buff[j] = *command; command++, j++; } buff[j] = '\0'; word[i] = dup_string(buff); if ((i == 1) && !string_compare(word[0], "def")) { while (*command && isspace(*command)) command++; word[2] = dup_string(command); if (!word[2]) notify (player, player, "Invalid definition syntax."); else insert_macro(word, player); for (; i >= 0; i--) { if (word[i]) free ((void *) word[i]); } return; } arg[i] = atoi(buff); if (arg[i] < 0) { notify(player, player, "Negative arguments not allowed!"); for (; i >= 0; i--) { if (word[i]) free ((void *) word[i]); } return; } } i--; while ((i >= 0) && !word[i]) i--; if (i < 0) return; else { switch (word[i][0]) { case KILL_COMMAND: kill_macro(word, player); break; case SHOW_COMMAND: list_macros(word, i, player, 1); break; case SHORTSHOW_COMMAND: list_macros(word, i, player, 0); break; case INSERT_COMMAND: notify(player, player, "Entering insert mode."); do_insert(player, program, arg, i); break; case DELETE_COMMAND: do_delete(player, program, arg, i); break; case QUIT_EDIT_COMMAND: do_quit(player, program); notify(player, player, "Editor exited."); break; case COMPILE_COMMAND: /* compile code belongs in compile.c, not in the editor */ do_compile(player, program); notify(player, player, "Compiler done."); break; case LIST_COMMAND: do_list(player, program, arg, i); break; case EDITOR_HELP_COMMAND: spit_file(player, EDITOR_HELP_FILE); break; case VIEW_COMMAND: val_and_head(player, arg, i); break; case UNASSEMBLE_COMMAND: disassemble(player, program); break; case NUMBER_COMMAND: toggle_numbers(player); break; default: notify(player, player, "Illegal editor command."); break; } } for (; i >= 0; i--) { if (word[i]) free ((void *) word[i]); } } /* puts program into insert mode */ void do_insert(dbref player, dbref program, int arg[], int argc) { DBFETCH(player)->sp.player.insert_mode++; DBDIRTY(player); if (argc) DBSTORE(program, sp.program.curr_line, arg[0] - 1); /* set current line to something else */ if (FLAGS(player) & INTERNAL) { sprintf (buf, "%d>", DBFETCH(program)->sp.program.curr_line + 1); notify (player, player, buf); } } /* deletes line n if one argument, lines arg1 -- arg2 if two arguments current line if no argument */ void do_delete(dbref player, dbref program, int arg[], int argc) { line *curr, *temp; char buff[BUFFER_LEN]; int i; switch (argc) { case 0: arg[0] = DBFETCH(program)->sp.program.curr_line; case 1: arg[1] = arg[0]; case 2: /* delete from line 1 to line 2 */ /* first, check for conflict */ if (arg[0] > arg[1]) { notify(player, player, "Nonsensical arguments."); return; } i = arg[0] - 1; for (curr = DBFETCH(program)->sp.program.first; curr && i; i--) curr = curr->next; if (curr) { DBFETCH(program)->sp.program.curr_line = arg[0]; i = arg[1] - arg[0] + 1; /* delete n lines */ while (i && curr) { temp = curr; if (curr->prev) curr->prev->next = curr->next; else DBFETCH(program)->sp.program.first = curr->next; if (curr->next) curr->next->prev = curr->prev; curr = curr->next; free_line(temp); i--; } sprintf(buff, "%d lines deleted", arg[1] - arg[0] - i + 1); notify(player, player, buff); } else notify(player, player, "No line to delete!"); break; default: notify(player, player, "Too many arguments!"); break; } } /* quit from edit mode. Put player back into the regular game mode */ void do_quit(dbref player, dbref program) { write_program(DBFETCH(program)->sp.program.first, program); free_prog_text(DBFETCH(program)->sp.program.first); DBSTORE(program, sp.program.editlocks, dbreflist_remove(DBFETCH(program)->sp.program.editlocks, player)); FLAGS(player) &= ~INTERACTIVE; DBSTORE(player, curr_prog, NOTHING); DBDIRTY(player); DBDIRTY(program); } void match_and_list(dbref player, char *name, char *linespec) { dbref thing; char *p; char *q; int range[2]; int argc; match_data md; if(Typeof(player) != TYPE_PLAYER) { notify(OWNER(player), OWNER(player), "Only Players can list programs."); return; } init_match(player, name, TYPE_PROGRAM, &md); match_neighbor(&md); match_possession(&md); match_absolute(&md); if ((thing = noisy_match_result(&md)) == NOTHING) return; if (Typeof(thing) != TYPE_PROGRAM) { notify(player, player, "You can't list anything but a program."); return; } if (!(controls(player, thing) || Linkable(thing))) { notify(player, player, "Permission denied."); return; } if (DBFETCH(thing)->sp.program.editlocks != NULL) { notify(player, player, "Sorry, that program is being edited."); return; } if (!*linespec) { range[0] = 1; range[1] = -1; argc = 2; } else { q = p = linespec; while(*p) { while(*p && !isspace(*p)) *q++ = *p++; while(*p && isspace(*++p)); } *q = '\0'; argc = 1; if (isdigit(*linespec)) { range[0] = atoi(linespec); while(*linespec && isdigit(*linespec)) linespec++; } else range[0] = 1; if (*linespec) { argc = 2; while(*linespec && !isdigit(*linespec)) linespec++; if (*linespec) range[1] = atoi(linespec); else range[1] = -1; } } DBSTORE(thing, sp.program.first, read_program(thing)); do_list(player, thing, range, argc); if (!(FLAGS(thing) & INTERNAL)) free_prog_text(DBFETCH(thing)->sp.program.first); return; } /* list --- if no argument, redisplay the current line if 1 argument, display that line if 2 arguments, display all in between */ void do_list(dbref player, dbref program, int oarg[], int argc) { line *curr; int i, count; int arg[2]; char buff[BUFFER_LEN]; if (oarg) { arg[0] = oarg[0]; arg[1] = oarg[1]; } else arg[0] = arg[1] = 0; switch (argc) { case 0: arg[0] = DBFETCH(program)->sp.program.curr_line; case 1: arg[1] = arg[0]; case 2: if ((arg[0] > arg[1]) && (arg[1] != -1)) { notify(player, player, "Arguments don't make sense!"); return; } i = arg[0] - 1; for (curr = DBFETCH(program)->sp.program.first; i && curr; i--, curr = curr->next); if (curr) { i = arg[1] - arg[0] + 1; /* display n lines */ for (count = arg[0]; curr && (i || (arg[1] == -1)); i--) { if (FLAGS(player) & INTERNAL) sprintf(buff, "%3d: %s", count, DoNull(curr->this_line)); else sprintf(buff, "%s", DoNull(curr->this_line)); notify(player, player, buff); count++; curr = curr->next; } if (count - arg[0] > 1) { sprintf(buff, "%d lines displayed.", count - arg[0]); notify(player, player, buff); } } else notify(player, player, "Line not available for display."); break; default: notify(player, player, "Too many arguments!"); break; } } void val_and_head(dbref player, int arg[], int argc) { dbref program; if (argc != 1) { notify(player, player, "I don't understand which header you're trying to look at."); return; } program = arg[0]; if (program < 0 || program >= db_top || Typeof(program) != TYPE_PROGRAM) { notify(player, player, "That isn't a program."); return; } if (!(controls(player, program) || Linkable(program))) { notify(player, player, "That's not a public program."); return; } do_list_header(player, program); } void do_list_header(dbref player, dbref program) { line *curr = read_program(program); while (curr && (curr->this_line)[0] == '(') { notify (player, player, curr->this_line); curr = curr->next; } if (!(FLAGS(program) & INTERNAL)) free_prog_text(curr); notify (player, player, "Done."); } void toggle_numbers(dbref player) { if (FLAGS(player) & INTERNAL) { FLAGS(player) &= ~INTERNAL; notify(player, player, "Line numbers off."); } else { FLAGS(player) |= INTERNAL; notify(player, player, "Line numbers on."); } } /* insert this line into program */ void insert(dbref player, char *line_text) { dbref program; int i; line *curr; line *new_line; program = DBFETCH(player)->curr_prog; if (!string_compare(line_text, EXIT_INSERT)) { DBSTORE(player, sp.player.insert_mode, 0); /* turn off insert mode */ return; } i = DBFETCH(program)->sp.program.curr_line - 1; for (curr = DBFETCH(program)->sp.program.first; curr && i && i + 1; i--, curr = curr->next); new_line = get_new_line(); /* initialize line */ new_line->this_line = dup_string(line_text); if (!DBFETCH(program)->sp.program.first) /* nothing --- insert in front */ { DBFETCH(program)->sp.program.first = new_line; DBFETCH(program)->sp.program.curr_line = 2; /* insert at the end */ DBDIRTY(program); if (FLAGS(player) & INTERNAL) { sprintf (buf, "%d>", DBFETCH(program)->sp.program.curr_line); notify (player, player, buf); } return; } if (!curr) /* insert at the end */ { i = 1; for (curr = DBFETCH(program)->sp.program.first; curr->next; curr = curr->next, i++); DBFETCH(program)->sp.program.curr_line = i + 2; new_line->prev = curr; curr->next = new_line; DBDIRTY(program); if (FLAGS(player) & INTERNAL) { sprintf (buf, "%d>", DBFETCH(program)->sp.program.curr_line); notify (player, player, buf); } return; } if (!DBFETCH(program)->sp.program.curr_line) /* insert at the beginning */ { DBFETCH(program)->sp.program.curr_line = 1; /* insert after this new line */ new_line->next = DBFETCH(program)->sp.program.first; DBFETCH(program)->sp.program.first = new_line; DBDIRTY(program); if (FLAGS(player) & INTERNAL) { sprintf (buf, "%d>", DBFETCH(program)->sp.program.curr_line + 1); notify (player, player, buf); } return; } /* inserting in the middle */ DBFETCH(program)->sp.program.curr_line++; if (FLAGS(player) & INTERNAL) { sprintf (buf, "%d>", DBFETCH(program)->sp.program.curr_line + 1); notify (player, player, buf); } new_line->prev = curr; new_line->next = curr->next; if (new_line->next) new_line->next->prev = new_line; curr->next = new_line; DBDIRTY(program); }