/* $header: /belch_a/users/rearl/tm/src/RCS/edit.c,v 1.3 90/07/29 17:33:10 rearl Exp $ */ /* * $Log: edit.c,v $ * Revision 1.14 90/10/06 16:32:43 rearl * Fixes for 2.2 distribution. * * Revision 1.13 90/09/28 12:20:39 rearl * Fixed macro function declarations. * * Revision 1.12 90/09/18 07:56:03 rearl * Added number toggling in source listing. * * Revision 1.11 90/09/16 04:42:05 rearl * Preparation code added for disk-based MUCK. * * Revision 1.10 90/09/15 22:18:58 rearl * Made "show" and "abridged" a little more friendly. * * Revision 1.9 90/09/13 06:22:08 rearl * Took out some tabs so the server agrees with more clients. * * Revision 1.8 90/09/10 02:22:15 rearl * Fixed a NULL string bug in program listing. * * Revision 1.7 90/09/01 05:57:37 rearl * Fixed bug with program header listing. * * Revision 1.6 90/08/27 03:24:06 rearl * Disk-based MUF source code, added necessary locking. * * Revision 1.5 90/08/17 03:42:35 rearl * Fixed @list once and for all. Default is now to list all lines of a program * if no arguments are given. * * Revision 1.4 90/08/05 03:19:15 rearl * Redid match routines. * * Revision 1.3 90/07/29 17:33:10 rearl * Got rid of some unused variables. * * Revision 1.2 90/07/23 03:12:35 casie * Cleaned up various gcc warnings. * * Revision 1.1 90/07/19 23:03:31 casie * Initial revision * * */ #include "copyright.h" #include "config.h" #include "db.h" #include "interface.h" #include "externs.h" #include "params.h" #include "match.h" #include "strings.h" #include <ctype.h> #define DOWNCASE(x) (lowercase[x]) void editor(dbref, const char *); void do_insert(dbref, dbref, int [], int); void do_delete(dbref, dbref, int [], int); void do_quit(dbref, dbref); void do_list(dbref, dbref, int [], int); void insert(dbref, const char *); struct line * get_new_line(void); struct line * read_program(dbref); char * macro_expansion(struct macrotable *, char *); void do_compile(dbref, dbref); void free_line(struct line *); void free_prog_text(struct line *); void prog_clean(struct frame *); void val_and_head(dbref, int[], int); void do_list_header(dbref, dbref); void toggle_numbers(dbref); /* 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; if (DBFETCH(player)->run) { /* process command, push onto stack, and return control to forth program */ FLAGS(player) &= ~INTERACTIVE; if (!string_compare(command, BREAK_COMMAND)) { prog_clean(DBFETCH(player)->run); if (!(DBFETCH(player)->run -> pc)) { free((void *) DBFETCH(player)->run); DBFETCH(player)->run = 0; } DBDIRTY(player); return; } fr = DBFETCH(player)->run; if (fr -> argument.top >= STACK_SIZE) { notify(player, "Program stack overflow."); prog_clean(fr); DBDIRTY(player); return; } fr -> argument.st[fr -> argument.top++].type = PROG_STRING; fr -> argument.st[fr -> argument.top - 1].data.string = alloc_prog_string(command); (void) interp_loop(player, DBFETCH(player)->curr_prog, fr); if (!(DBFETCH(player)->run -> pc)) { free((void *) DBFETCH(player)->run); DBFETCH(player)->run = 0; } } 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 alloc_string (node -> definition); } } struct macrotable *new_macro(const char *name, const char *definition, dbref player) { struct macrotable *newmacro = (struct macrotable *)malloc (sizeof (struct macrotable)); char buf[BUFFER_LEN]; int i; for (i = 0; name[i]; i++) buf[i] = DOWNCASE(name[i]); buf[i] = '\0'; newmacro -> name = alloc_string(buf); newmacro -> definition = alloc_string(definition); newmacro -> implementor = player; newmacro -> left = NULL; newmacro -> right = NULL; return (newmacro); } 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; } } 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, "That macro's already been defined."); else notify (player, "Entry created."); } 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'; } } } 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; } 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; } } } 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)->curr_prog; /* check to see if we are insert mode */ if (DBFETCH(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)) { buf[j] = *command; command++, j++; } buf[j] = '\0'; word[i] = alloc_string(buf); if ((i == 1) && !string_compare(word[0], "def")) { while (*command && isspace(*command)) command++; word[2] = alloc_string(command); if (!word[2]) notify (player, "Invalid definition syntax."); else insert_macro(word, player); for (; i >= 0; i--) { if (word[i]) free ((void *) word[i]); } return; } arg[i] = atoi(buf); if (arg[i] < 0) { notify(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: do_insert(player, program, arg, i); notify(player, "Entering insert mode."); 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: /* compile code belongs in compile.c, not in the editor */ do_compile(player, program); 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); break; case NUMBER_COMMAND: toggle_numbers(player); break; default: notify(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)->insert_mode++; DBDIRTY(player); if (argc) DBSTORE(program, sp.program.curr_line, arg[0] - 1); /* set current line to something else */ } /* 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); FLAGS(program) &= ~INTERNAL; FLAGS(player) &= ~INTERACTIVE; DBFETCH(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) || (FLAGS(thing)&VISIBLE) || (Arch(player) && Mucker(player)))) { notify(player, "Permission denied."); 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) { struct line *curr; int i, count; int arg[2]; char buf[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, "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 (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; program = DBFETCH(player)->curr_prog; if (!string_compare(line, EXIT_INSERT)) { DBSTORE(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 = alloc_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); return; } if (!curr) /* insert at the end */ { i = 1; for (curr = DBFETCH(program)->sp.program.first; curr -> next; curr = curr -> next) i++; /* count lines */ DBFETCH(program)->sp.program.curr_line = i + 2; new_line -> prev = curr; curr -> next = new_line; DBDIRTY(program); 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); return; } /* inserting in the middle */ DBFETCH(program)->sp.program.curr_line++; 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); }