/* $header: /belch_a/users/rearl/tm/src/RCS/edit.c,v 1.3 90/07/29 17:33:10 rearl Exp $ */
#include "copyright.h"
#include "config.h"
#include "db.h"
#include "props.h"
#include "interface.h"
#include "externs.h"
#include "params.h"
#include "tune.h"
#include "match.h"
#include "fbstrings.h"
#include <ctype.h>
#define DOWNCASE(x) (tolower(x))
void editor(int descr, 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 do_list(dbref player, dbref program, int *arg, int argc);
void insert(dbref player, const char *line);
struct line *get_new_line(void);
struct line *read_program(dbref i);
void do_compile(int descr, dbref player, dbref program, int force_err_disp);
void free_line(struct line *l);
void free_prog_text(struct line *l);
void prog_clean(struct frame *fr);
void val_and_head(dbref player, int arg[], int argc);
void do_list_header(dbref player, dbref program);
void list_publics(int descr, dbref player, int arg[], int argc);
void do_list_publics(dbref player, dbref program);
void toggle_numbers(dbref player, int arg[], int argc);
/* 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(int descr, dbref player, const char *command)
{
if (FLAGS(player) & READMODE) {
/*
* process command, push onto stack, and return control to forth
* program
*/
handle_read_event(descr, player, command);
} else {
editor(descr, player, command);
}
}
char *
macro_expansion(struct macrotable *node, const 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;
}
}
int
insert_macro(const char *macroname, const char *macrodef,
dbref player, struct macrotable **node)
{
struct macrotable *newmacro;
newmacro = new_macro(macroname, macrodef, player);
if (!(*node)) {
*node = newmacro;
return 1;
} else
return (grow_macro_tree((*node), newmacro));
}
void
do_list_tree(struct macrotable *node, const char *first, const char *last,
int length, dbref player)
{
static char buf[BUFFER_LEN];
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) {
snprintf(buf, sizeof(buf), "%-16s %-16s %s", node->name,
NAME(node->implementor), node->definition);
notify(player, buf);
buf[0] = '\0';
} else {
int blen = strlen(buf);
snprintf(buf + blen, sizeof(buf) - blen, "%-16s", node->name);
buf[sizeof(buf)-1] = '\0';
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;
}
void
purge_macro_tree(struct macrotable *node)
{
if (!node)
return;
purge_macro_tree(node->left);
purge_macro_tree(node->right);
if (node->name)
free(node->name);
if (node->definition)
free(node->definition);
free(node);
}
int
erase_node(struct macrotable *oldnode, struct macrotable *node,
const char *killname, struct macrotable *mtop)
{
if (!node)
return 0;
else if (strcmp(killname, node->name) < 0)
return erase_node(node, node->left, killname, mtop);
else if (strcmp(killname, node->name))
return erase_node(node, node->right, killname, mtop);
else {
if (node == oldnode->left) {
oldnode->left = node->left;
if (node->right)
grow_macro_tree(mtop, node->right);
if (node->name)
free(node->name);
if (node->definition)
free(node->definition);
free((void *) node);
return 1;
} else {
oldnode->right = node->right;
if (node->left)
grow_macro_tree(mtop, node->left);
if (node->name)
free(node->name);
if (node->definition)
free(node->definition);
free((void *) node);
return 1;
}
}
}
int
kill_macro(const char *macroname, dbref player, struct macrotable **mtop)
{
if (!(*mtop)) {
return (0);
} else if (!string_compare(macroname, (*mtop)->name)) {
struct macrotable *macrotemp = (*mtop);
int whichway = ((*mtop)->left) ? 1 : 0;
*mtop = whichway ? (*mtop)->left : (*mtop)->right;
if ((*mtop) && (whichway ? macrotemp->right : macrotemp->left))
grow_macro_tree((*mtop), whichway ? macrotemp->right : macrotemp->left);
if (macrotemp->name)
free(macrotemp->name);
if (macrotemp->definition)
free(macrotemp->definition);
free((void *) macrotemp);
return (1);
} else if (erase_node((*mtop), (*mtop), macroname, (*mtop)))
return (1);
else
return (0);
}
void
free_old_macros(void)
{
purge_macro_tree(macrotop);
}
/* The editor itself --- this gets called each time every time to
* parse a command.
*/
void
editor(int descr, 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 = PLAYER_CURR_PROG(player);
/* check to see if we are insert mode */
if (PLAYER_INSERT_MODE(player)) {
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 {
if (insert_macro(word[1], word[2], player, ¯otop)) {
notify(player, "Entry created.");
} else {
notify(player, "That macro already exists!");
}
}
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:
if (!Wizard(player)) {
notify(player, "I'm sorry Dave, but I can't let you do that.");
} else {
if (kill_macro(word[0], player, ¯otop))
notify(player, "Macro entry deleted.");
else
notify(player, "Macro to delete not found.");
}
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(descr, player, program, 1);
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, arg, i);
break;
case PUBLICS_COMMAND:
list_publics(descr, player, arg, i);
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)
{
PLAYER_SET_INSERT_MODE(player, PLAYER_INSERT_MODE(player) + 1);
/* DBDIRTY(player); */
if (argc)
PROGRAM_SET_CURR_LINE(program, 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] = PROGRAM_CURR_LINE(program);
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 = PROGRAM_FIRST(program); curr && i; i--)
curr = curr->next;
if (curr) {
PROGRAM_SET_CURR_LINE(program, 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
PROGRAM_SET_FIRST(program, curr->next);
if (curr->next)
curr->next->prev = curr->prev;
curr = curr->next;
free_line(temp);
i--;
}
snprintf(buf, sizeof(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)
{
log_status("PROGRAM SAVED: %s by %s(%d)\n", unparse_object(player, program),
NAME(player), player);
write_program(PROGRAM_FIRST(program), program);
if (tp_log_programs)
log_program_text(PROGRAM_FIRST(program), player, program);
free_prog_text(PROGRAM_FIRST(program));
PROGRAM_SET_FIRST(program, NULL);
FLAGS(program) &= ~INTERNAL;
FLAGS(player) &= ~INTERACTIVE;
PLAYER_SET_CURR_PROG(player, NOTHING);
DBDIRTY(player);
DBDIRTY(program);
}
void
match_and_list(int descr, dbref player, const char *name, char *linespec)
{
dbref thing;
char *p;
char *q;
int range[2];
int argc;
struct match_data md;
struct line *tmpline;
init_match(descr, player, name, TYPE_PROGRAM, &md);
match_neighbor(&md);
match_possession(&md);
match_registered(&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))) { */
if (!(controls(player, thing) || (FLAGS(thing) & VEHICLE))) {
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;
}
}
tmpline = PROGRAM_FIRST(thing);
PROGRAM_SET_FIRST(thing, read_program(thing));
do_list(player, thing, range, argc);
free_prog_text(PROGRAM_FIRST(thing));
PROGRAM_SET_FIRST(thing, tmpline);
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] = PROGRAM_CURR_LINE(program);
case 1:
arg[1] = arg[0];
case 2:
if ((arg[0] > arg[1]) && (arg[1] != -1)) {
notify_nolisten(player, "Arguments don't make sense!", 1);
return;
}
i = arg[0] - 1;
for (curr = PROGRAM_FIRST(program); 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)
snprintf(buf, sizeof(buf), "%3d: %s", count, DoNull(curr->this_line));
else
snprintf(buf, sizeof(buf), "%s", DoNull(curr->this_line));
notify_nolisten(player, buf, 1);
count++;
curr = curr->next;
}
if (count - arg[0] > 1) {
snprintf(buf, sizeof(buf), "%d lines displayed.", count - arg[0]);
notify_nolisten(player, buf, 1);
}
} else
notify_nolisten(player, "Line not available for display.", 1);
break;
default:
notify_nolisten(player, "Too many arguments!", 1);
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
list_publics(int descr, dbref player, int arg[], int argc)
{
dbref program;
if (argc > 1) {
notify(player,
"I don't understand which program you want to list PUBLIC functions for.");
return;
}
program = (argc == 0) ? PLAYER_CURR_PROG(player) : 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;
}
if (!(PROGRAM_CODE(program))) {
if (program == PLAYER_CURR_PROG(player)) {
do_compile(descr, OWNER(program), program, 0);
} else {
struct line *tmpline;
tmpline = PROGRAM_FIRST(program);
PROGRAM_SET_FIRST(program, (struct line *) read_program(program));
do_compile(descr, OWNER(program), program, 0);
free_prog_text(PROGRAM_FIRST(program));
PROGRAM_SET_FIRST(program, tmpline);
}
if (!PROGRAM_CODE(program)) {
notify(player, "Program not compilable.");
return;
}
}
do_list_publics(player, program);
}
void
do_list_publics(dbref player, dbref program)
{
struct publics *ptr;
notify(player, "PUBLIC funtions:");
for (ptr = PROGRAM_PUBS(program); ptr; ptr = ptr->next)
notify(player, ptr->subname);
}
void
toggle_numbers(dbref player, int arg[], int argc)
{
if (argc) {
switch (arg[0]) {
case 0:
FLAGS(player) &= ~INTERNAL;
notify(player, "Line numbers off.");
break;
default:
FLAGS(player) |= INTERNAL;
notify(player, "Line numbers on.");
break;
}
} else 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 = PLAYER_CURR_PROG(player);
if (!string_compare(line, EXIT_INSERT)) {
PLAYER_SET_INSERT_MODE(player, 0); /* turn off insert mode */
notify_nolisten(player, "Exiting insert mode.", 1);
return;
}
i = PROGRAM_CURR_LINE(program) - 1;
for (curr = PROGRAM_FIRST(program); curr && i && i + 1; i--)
curr = curr->next;
new_line = get_new_line(); /* initialize line */
if (!*line) {
new_line->this_line = alloc_string(" ");
} else {
new_line->this_line = alloc_string(line);
}
if (!PROGRAM_FIRST(program)) { /* nothing --- insert in front */
PROGRAM_SET_FIRST(program, new_line);
PROGRAM_SET_CURR_LINE(program, 2); /* insert at the end */
/* DBDIRTY(program); */
return;
}
if (!curr) { /* insert at the end */
i = 1;
for (curr = PROGRAM_FIRST(program); curr->next; curr = curr->next)
i++; /* count lines */
PROGRAM_SET_CURR_LINE(program, i + 2);
new_line->prev = curr;
curr->next = new_line;
/* DBDIRTY(program); */
return;
}
if (!PROGRAM_CURR_LINE(program)) { /* insert at the
* beginning */
PROGRAM_SET_CURR_LINE(program, 1); /* insert after this new
* line */
new_line->next = PROGRAM_FIRST(program);
PROGRAM_SET_FIRST(program, new_line);
/* DBDIRTY(program); */
return;
}
/* inserting in the middle */
PROGRAM_SET_CURR_LINE(program, PROGRAM_CURR_LINE(program) + 1);
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); */
}