#include /* This software is Copyright 1989, 1990, 1992, 1993 by various individuals. Please see the accompanying file COPYRIGHT for details. */ #include "db.h" #include "defs.h" #include "ctype.h" #include "match.h" #include "prims.h" #include "config.h" #include "params.h" #include "strings.h" #include "externs.h" #include "interface.h" void editor(dbref player, const 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 insert(dbref player, const char *line); struct line *get_new_line(void); struct line *read_program(dbref i); void free_line(struct 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, const char *command) { struct frame *fr; fr = DBFETCH(player)->sp.player.run; if (fr) { if (fr->reading) { if (fr->argument.top >= STACK_SIZE) { interp_err(player,fr->prog,insttotext(fr->pc-1),"Stack overflow"); remove_proglock(player,fr->prog); prog_abort_frame(player); return; } fr->argument.st[fr->argument.top].type = PROG_STRING; fr->argument.st[fr->argument.top++].data.string = dup_string(command); fr->reading = 0; } if (string_compare(command,BREAK_COMMAND)) { interp_resume(player); } else { prog_abort(player); } } else { editor(player,command); } DBDIRTY(player); } char *macro_expansion(struct macrotable *node, char *match) { if (!node) return NULL; else { register int 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); } } struct macrotable *new_macro(const char *name, const char *definition, dbref player) { struct macrotable *newmacro; char buf[BUFFER_LEN]; int i; newmacro = (struct macrotable *) malloc(sizeof(struct 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 = 0; newmacro->right = 0; return(newmacro); } static void free_macro_tree(struct macrotable *m) { if (! m) return; free_macro_tree(m->left); free_macro_tree(m->right); free(m->name); free(m->definition); free(m); } void free_macros(void) { free_macro_tree(macrotop); macrotop = 0; } static int grow_macro_tree(struct macrotable *node, struct macrotable *newmacro) { register int 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; } } static void insert_macro(const char *word[], dbref player) { struct macrotable *newmacro; newmacro = new_macro (word[1], word[2], player); if (!macrotop) macrotop = newmacro; else if (!grow_macro_tree(macrotop, newmacro)) notify (player, "Oopsie! That macro's already been defined."); else notify (player, "Entry created."); } static void do_list_tree(struct macrotable *node, const char *first, const char *last, int length, dbref player) { static char buf[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(buf, "%-16s %-16s %s", node->name, NAME(node->implementor), node->definition); notify(player, buf); buf[0] = '\0'; } else { sprintf(buf + strlen(buf), "%-16s", node->name); if (strlen(buf) > 70) { notify(player, buf); buf[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, buf); buf[0] = '\0'; } } } static void list_macros(const 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, "End of list."); return; } static int erase_node(struct macrotable *oldnode, struct macrotable *node, const 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; } } } static void kill_macro(const char *word[], dbref player) { if (!Wizard(player)) { notify (player, "I'm sorry, Dave, I can't let you do that."); return; } else if (!macrotop) { notify (player, "You've got nothing and you want to kill? Sheesh!"); return; } else if (!string_compare(word[0], macrotop->name)) { struct 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, "Entry removed."); } else if (erase_node(macrotop, macrotop, word[0])) notify (player, "Entry removed."); else notify (player, "Entry to remove not found."); } /* The editor itself --- this gets called each time every time to * parse a command. */ void editor(dbref player, const char *command) { dbref program; int arg[MAX_ARG+1]; char buf[BUFFER_LEN]; const char *word[MAX_ARG+1]; int i, j; /* loop variables */ program = DBFETCH(player)->sp.player.curr_prog; /* check to see if we are insert mode */ if (DBFETCH(player)->sp.player.insert_mode) { if (!command || !*command) command = " "; insert(player, command); return; } if (!command || !*command) return; /* parse the commands */ for (i = 0; i <= MAX_ARG && *command; i++) { while (*command && Cisspace(*command)) command++; j = 0; while (*command && !Cisspace(*command)) { buf[j] = *command; command++, j++; } buf[j] = '\0'; word[i] = dup_string(buf); if ((i == 1) && !string_compare(word[0], "def")) { while (*command && Cisspace(*command)) command++; word[2] = dup_string(command); if (!word[2]) notify (player, "Invalid definition syntax."); else insert_macro(word, player); for (; i >= 0; i--) { if (word[i]) cfree (word[i]); } return; } arg[i] = atoi(buf); if (arg[i] < 0) { notify(player, "Negative arguments not allowed!"); for (; i >= 0; i--) { if (word[i]) cfree (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, "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, "Editor exited."); break; case COMPILE_COMMAND: do_compile(player, program, player); notify(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, arg, i); break; case NUMBER_COMMAND: toggle_numbers(player); break; default: notify(player, "Illegal editor command."); break; } } for (; i >= 0; i--) { if (word[i]) cfree (word[i]); } } /* puts program into insert mode */ void do_insert(dbref player, dbref program, int arg[], int argc) { char buf[BUFFER_LEN]; DBFETCH(player)->sp.player.insert_mode = 1; 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, 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) { struct line *curr, *temp; char buf[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, "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(buf, "%d lines deleted", arg[1] - arg[0] - i + 1); notify(player, buf); } else notify(player, "No line to delete!"); break; default: notify(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); remove_proglock(player, program); FLAGS(player) &= ~INTERACTIVE; DBFETCH(player)->sp.player.curr_prog = NOTHING; DBDIRTY(player); DBDIRTY(program); } void match_and_list(dbref player, const char *name, char *linespec) { dbref thing; char *p; char *q; int range[2]; int argc; struct match_data md; 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, "You can't list anything but a program."); return; } if (!(controls(player, thing) || Linkable(thing))) { notify(player, "Permission denied."); return; } if (check_proglock(thing,PROGLOCK_WRITE)) { notify(player,"Sorry, that program is currently being edited. Try later."); return; } if (!*linespec) { range[0] = 1; range[1] = -1; argc = 2; } else { q = p = linespec; while(*p) { while(*p && !Cisspace(*p)) *q++ = *p++; while(*p && Cisspace(*++p)); } *q = '\0'; argc = 1; if (Cisdigit(*linespec)) { range[0] = atoi(linespec); while(*linespec && Cisdigit(*linespec)) linespec++; } else range[0] = 1; if (*linespec) { argc = 2; while(*linespec && !Cisdigit(*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) { struct line *curr; int i, count; int arg[2]; char buf[BUFFER_LEN]; if ( (FLAGS(program) & UNLISTABLE) && (!Wizard(player) || Wizard(program)) ) { notify(player,"This program is unlistable."); return; } 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, "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(buf, "%3d: %s", count, DoNull(curr->this_line)); else sprintf(buf, "%s", DoNull(curr->this_line)); notify(player, buf); count++; curr = curr->next; } if (count - arg[0] > 1) { sprintf(buf, "%d lines displayed.", count - arg[0]); notify(player, buf); } } else notify(player, "Line not available for display."); break; default: notify(player, "Too many arguments!"); break; } } void val_and_head(dbref player, int arg[], int argc) { dbref program; if (argc != 1) { notify(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, "That isn't a program."); return; } if (!(controls(player, program) || Linkable(program))) { notify(player, "That's not a public program."); return; } do_list_header(player, program); } void do_list_header(dbref player, dbref program) { struct line *curr = read_program(program); while (curr && (curr->this_line)[0] == '(') { notify (player, curr->this_line); curr = curr->next; } if (!(FLAGS(program) & INTERNAL)) free_prog_text(curr); notify (player, "Done."); } void toggle_numbers(dbref player) { if (FLAGS(player) & INTERNAL) { FLAGS(player) &= ~INTERNAL; notify(player, "Line numbers off."); } else { FLAGS(player) |= INTERNAL; notify(player, "Line numbers on."); } } /* insert this line into program */ void insert(dbref player, const char *line) { dbref program; int i; struct line *curr; struct line *new_line; char buf[BUFFER_LEN]; program = DBFETCH(player)->sp.player.curr_prog; if (!string_compare(line, EXIT_INSERT)) { notify(player,"Exiting insert mode."); 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); 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, 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, 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, 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, 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); }