nakedmud-mod/
nakedmud-mod/html/tutorials/
nakedmud-mod/html/tutorials/building_extras/
nakedmud-mod/html/tutorials/c/
nakedmud-mod/html/tutorials/reference/
nakedmud-mod/html/tutorials/scripting/
nakedmud-mod/html/tutorials/scripting_extras/
nakedmud-mod/lib/
nakedmud-mod/lib/help/A/
nakedmud-mod/lib/help/B/
nakedmud-mod/lib/help/C/
nakedmud-mod/lib/help/D/
nakedmud-mod/lib/help/G/
nakedmud-mod/lib/help/H/
nakedmud-mod/lib/help/J/
nakedmud-mod/lib/help/L/
nakedmud-mod/lib/help/M/
nakedmud-mod/lib/help/O/
nakedmud-mod/lib/help/P/
nakedmud-mod/lib/help/R/
nakedmud-mod/lib/help/S/
nakedmud-mod/lib/help/W/
nakedmud-mod/lib/logs/
nakedmud-mod/lib/misc/
nakedmud-mod/lib/players/
nakedmud-mod/lib/pymodules/polc/
nakedmud-mod/lib/txt/
nakedmud-mod/lib/world/
nakedmud-mod/lib/world/zones/examples/
nakedmud-mod/lib/world/zones/examples/mproto/
nakedmud-mod/lib/world/zones/examples/oproto/
nakedmud-mod/lib/world/zones/examples/reset/
nakedmud-mod/lib/world/zones/examples/rproto/
nakedmud-mod/lib/world/zones/examples/trigger/
nakedmud-mod/lib/world/zones/limbo/
nakedmud-mod/lib/world/zones/limbo/room/
nakedmud-mod/lib/world/zones/limbo/rproto/
nakedmud-mod/src/alias/
nakedmud-mod/src/dyn_vars/
nakedmud-mod/src/editor/
nakedmud-mod/src/example_module/
nakedmud-mod/src/help2/
nakedmud-mod/src/set_val/
nakedmud-mod/src/socials/
nakedmud-mod/src/time/
//*****************************************************************************
//
// editor.c
//
// one of the problems that came up with the origional text editor what that
// it started becoming rather kludgy and hackish as we started adding more
// features to it, like script editing capabilities. It was difficult to
// coordinate when we wanted certain options and when we didn't. This is an
// attempt to make things a bit easier to scale up with.
//
//*****************************************************************************

#include "../mud.h"
#include "../utils.h"
#include "../auxiliary.h"
#include "../socket.h"
#include "editor.h"



//*****************************************************************************
// mandatory modules
//*****************************************************************************
#include "../scripts/scripts.h"
#include "../scripts/pyplugs.h"



//*****************************************************************************
// auxiliary data for sockets
//*****************************************************************************
typedef struct editor_aux_data {
  EDITOR   *editor;      // the editor we're using
  BUFFER   *buf;         // the buffer we're editing (if any)
  BUFFER   *working_buf; // the buffer where we do our work
  PyObject *py_complete; // python function to call on completion of editing
  void    (* on_complete)(SOCKET_DATA *sock, const char *str);
} EDITOR_AUX_DATA;

EDITOR_AUX_DATA *newEditorAuxData() {
  EDITOR_AUX_DATA *data = malloc(sizeof(EDITOR_AUX_DATA));
  bzero(data, sizeof(EDITOR_AUX_DATA));
  return data; // the buffer, working buffer, and editor are null 'til needed
}

void deleteEditorAuxData(EDITOR_AUX_DATA *data) {
  // if we have a working buf, free it. Don't touch anything else. that
  // stuff will be needed by the rest of the program
  if(data->working_buf) deleteBuffer(data->working_buf);
  if(data->py_complete) { Py_DECREF(data->py_complete); }
  free(data);
}

void clearEditorAuxData(EDITOR_AUX_DATA *data) {
  if(data->working_buf) deleteBuffer(data->working_buf);
  if(data->py_complete) { Py_DECREF(data->py_complete); }
  data->working_buf = NULL;
  data->editor      = NULL;
  data->buf         = NULL;
  data->on_complete = NULL;
  data->py_complete = NULL;
}



//*****************************************************************************
// local structures, functions, and defines
//*****************************************************************************
// a basic text editor for use by other modules
EDITOR *text_editor = NULL;

// an editor for dialog. Essentially, text without newlines
EDITOR *dialog_editor = NULL;

struct editor_data {
  HASHTABLE *cmds;  // mappings from commands to their functions and descs
  void (* prompt)(SOCKET_DATA *sock); // the prompt display
  void (*   init)(SOCKET_DATA *sock); // reset variables for using the editor
  void (* append)(SOCKET_DATA *sock, char *arg, BUFFER *buf); // append text to the buffer
};

// data for a command in the editor
typedef struct command_data {
  // the function this command calls
  void (* func)(SOCKET_DATA *sock, char *arg, BUFFER *buf);
  char *desc;    // the one-line helpfile description
  bool reserved; // is this command protected from being written over?
} ECMD_DATA;

ECMD_DATA *
newEditorCommand(const char *desc, 
		 void func(SOCKET_DATA *sock, char *arg, BUFFER *buf),
		 bool reserved) {
  ECMD_DATA *cmd = malloc(sizeof(ECMD_DATA));
  cmd->desc     = strdup(desc ? desc : "");
  cmd->func     = func;
  cmd->reserved = reserved;
  return cmd;
}

void deleteEditorCommand(ECMD_DATA *cmd) {
  if(cmd->desc) free(cmd->desc);
  free(cmd);
}


//
// the default prompt display for editors
//
void editorDefaultPrompt(SOCKET_DATA *sock) {
  text_to_buffer(sock, "] ");
}


//
// the default header display for editors
//
void editorDefaultHeader(SOCKET_DATA *sock) {
  text_to_buffer(sock, 
"================================================================================\r\n"
"Begin editing. /q on a new line to quit, /a to abort. /h for help         \r\n"
"================================================================================\r\n"
		 );
}


//
// the default function for appending text to the working buffer
//
void editorDefaultAppend(SOCKET_DATA *sock, char *arg, BUFFER *buf) {
  bufferCat(buf, arg);
  bufferCat(buf, "\r\n");
}


//
// the function for appending text to a dialog buffer. 
//
void editorDialogAppend(SOCKET_DATA *sock, char *arg, BUFFER *buf) {
  // if the buffer isn't empty, cat a space
  if(bufferLength(buf) > 0)
    bufferCat(buf, " ");
  bufferCat(buf, arg);
}


//
// The function that takes in a new command and figures out what to
// do with it
//
void editorInputHandler(SOCKET_DATA *sock, char *arg) {
  EDITOR_AUX_DATA *data = socketGetAuxiliaryData(sock, "editor_aux_data");

  // is it a command?
  if(*arg == '/') {
    // separate the command and the argument
    char buf[SMALL_BUFFER];
    arg = one_arg(arg, buf);

    // pull up the command
    ECMD_DATA *cmd = hashGet(data->editor->cmds, buf+1);
    if(cmd == NULL)
      text_to_buffer(sock, "Invalid command.\r\n");
    else
      cmd->func(sock, arg, data->working_buf);
  }
  else
    data->editor->append(sock, arg, data->working_buf);
}


//*****************************************************************************
// implementation of basic editor commands
//*****************************************************************************
void editorQuit(SOCKET_DATA *sock, char *arg, BUFFER *buf) { 
  // save the current changes
  EDITOR_AUX_DATA *data = socketGetAuxiliaryData(sock, "editor_aux_data");
  if(data->on_complete)
    data->on_complete(sock, bufferString(buf));
  else if(data->py_complete) {
    PyObject *ret = PyObject_CallFunction(data->py_complete, "Os", 
					  socketGetPyFormBorrowed(sock),
					  bufferString(buf));
    if(ret == NULL)
      log_pyerr("Error quitting the buffer editor.");
    Py_XDECREF(ret);
  }

  //bufferCopyTo(buf, data->buf);
  clearEditorAuxData(data);
  text_to_buffer(sock, "Saved and quit.\r\n");
  // and then pop the input handler
  socketPopInputHandler(sock);
}

void editorAbort(SOCKET_DATA *sock, char *arg, BUFFER *buf) { 
  EDITOR_AUX_DATA *data = socketGetAuxiliaryData(sock, "editor_aux_data");
  clearEditorAuxData(data);
  text_to_buffer(sock, "Editor aborted.\r\n");
  socketPopInputHandler(sock);
}

void editorDisplayHelp(SOCKET_DATA *sock, char *arg, BUFFER *buf) { 
  const char *key = NULL;
  ECMD_DATA   *val = NULL;
  HASH_ITERATOR *hash_i = newHashIterator(socketGetEditor(sock)->cmds);

  // print out all of the commands and their descriptions
  ITERATE_HASH(key, val, hash_i)
    send_to_socket(sock, "/%-3s %s\r\n", key, val->desc);
  deleteHashIterator(hash_i);
}

void editorDeleteLine(SOCKET_DATA *sock, char *arg, BUFFER *buf) { 
  char tmp[SMALL_BUFFER];
  arg = one_arg(arg, tmp);
  int line = atoi(tmp);
  if(!isdigit(*tmp) || !bufferRemove(buf, line))
    text_to_buffer(sock, "Line does not exist.\r\n");
  else
    text_to_buffer(sock, "Line deleted.\r\n");
}

void editorEditLine(SOCKET_DATA *sock, char *arg, BUFFER *buf) { 
  char tmp[SMALL_BUFFER];
  arg = one_arg(arg, tmp);
  int line = atoi(tmp);
  if(!isdigit(*tmp) || !bufferReplaceLine(buf, arg, line))
    text_to_buffer(sock, "Line does not exist.\r\n");
  else
    text_to_buffer(sock, "Line replaced.\r\n");
}

void editorInsertLine(SOCKET_DATA *sock, char *arg, BUFFER *buf) { 
  char tmp[SMALL_BUFFER];
  arg = one_arg(arg, tmp);
  int line = atoi(tmp);
  if(!isdigit(*tmp) || !bufferInsert(buf, arg, line))
    text_to_buffer(sock, "Insertion failed.\r\n");
  else
    text_to_buffer(sock, "Line inserted.\r\n");
}

void editorListDialogBuffer(SOCKET_DATA *sock, char *arg, BUFFER *buf) { 
  if(*bufferString(buf))
    send_to_socket(sock, "%s\r\n", bufferString(buf));
}

void editorListBuffer(SOCKET_DATA *sock, char *arg, BUFFER *buf) { 
  if(*bufferString(buf))
    text_to_buffer(sock, bufferString(buf));
}

void editorReplace(SOCKET_DATA *sock, char *arg, BUFFER *buf, bool all) {
  char *a;
  char *b;

  if(count_letters(arg, '\'', strlen(arg)) != 4) {
    text_to_buffer(sock, "arguments must take the form: 'to replace' 'replacement'\r\n");
    return;
  }

  // pull the first argument
  a = strtok(arg, "\'");
  if(a == NULL) {
    text_to_buffer(sock, "format is: /r[a] 'to replace' 'replacement'\r\n");
    return;
  }

  // kill the leading the leading ' of b
  strtok(NULL, "\'");

  b = strtok(NULL, "\'");
  if(b == NULL)
    b = "\0"; // "\0" will get deleted at the end of this block

  int replaced = bufferReplace(buf, a, b, all);
  send_to_socket(sock, "%d occurence%s of '%s' replaced with '%s'.\r\n",
		 replaced, (replaced == 1 ? "" : "s"), a, b);
}

void editorReplaceString(SOCKET_DATA *sock, char *arg, BUFFER *buf) { 
  editorReplace(sock, arg, buf, FALSE);
}

void editorReplaceAllString(SOCKET_DATA *sock, char *arg, BUFFER *buf) { 
  editorReplace(sock, arg, buf, TRUE);
}

void editorClear(SOCKET_DATA *sock, char *arg, BUFFER *buf) {
  bufferClear(buf);
  text_to_buffer(sock, "Buffer cleared.\r\n");
}

void editorFormatBuffer(SOCKET_DATA *sock, char *arg, BUFFER *buf) {
  bufferFormat(buf, SCREEN_WIDTH, PARA_INDENT);
  text_to_buffer(sock, "Buffer formatted.\r\n");
}



//*****************************************************************************
// implementation of editor.h
//*****************************************************************************
void init_editor() {
  // install the editor components
  auxiliariesInstall("editor_aux_data", 
		     newAuxiliaryFuncs(AUXILIARY_TYPE_SOCKET,
				       newEditorAuxData, deleteEditorAuxData,
				       NULL, NULL, NULL, NULL));
  text_editor   = newEditor();
  dialog_editor = newEditor();
  editorSetAppend(dialog_editor, editorDialogAppend);
  editorRemoveCommand(dialog_editor, "f");
  editorAddCommand(dialog_editor, "l", 
		   "        List the current buffer contents",
		   editorListDialogBuffer);
}

EDITOR *newEditor() {
  EDITOR *editor = malloc(sizeof(EDITOR));
  // set up the default commands
  editor->cmds = newHashtable();
  hashPut(editor->cmds, "q", 
	  newEditorCommand("        Quit editor and save changes",
			   editorQuit, TRUE));
  hashPut(editor->cmds, "a", 
	  newEditorCommand("        Quit editor and don't save",
			   editorAbort, TRUE));
  hashPut(editor->cmds, "h", 
	  newEditorCommand("        Display editor commands",
			   editorDisplayHelp, TRUE));
  hashPut(editor->cmds, "c", 
	  newEditorCommand("        Clear the contents of the buffer",
			   editorClear, TRUE));
  hashPut(editor->cmds, "l",
	  newEditorCommand("        List the current buffer contents",
			   editorListBuffer, FALSE));
  hashPut(editor->cmds, "d", 
	  newEditorCommand("#       Delete line with the specified number", 
			   editorDeleteLine, FALSE));
  hashPut(editor->cmds, "e", 
	  newEditorCommand("# <txt> Sets the text at the specified line to the new text",
			   editorEditLine, FALSE));
  hashPut(editor->cmds, "i", 
	  newEditorCommand("# <txt> Insert new text at the specified line number",
			   editorInsertLine, FALSE));
  hashPut(editor->cmds, "f",
	  newEditorCommand("        Formats your text into a paragraph",
			   editorFormatBuffer, FALSE));
  hashPut(editor->cmds, "r",
	  newEditorCommand("'a' 'b' replace first occurence of 'a' with 'b'",
			   editorReplaceString, FALSE));
  hashPut(editor->cmds, "ra",
	  newEditorCommand("'a' 'b' repalce all occurences of 'a' with 'b'",
			   editorReplaceAllString, FALSE));

  // set up the default prompt, header, and appending function
  editor->prompt = editorDefaultPrompt;
  editor->append = editorDefaultAppend;
  editor->init   = NULL;
  return editor;
}

void editorSetPrompt(EDITOR *editor, void prompt(SOCKET_DATA *sock)) {
  editor->prompt = prompt;
}

void editorSetInit(EDITOR *editor, void init(SOCKET_DATA *sock)) {
  editor->init = init;
}

void editorSetAppend(EDITOR *editor,
		     void append(SOCKET_DATA *sock, char *arg, BUFFER *buf)) {
  editor->append = append;
}

void editorAddCommand(EDITOR *editor, const char *cmd, const char *desc, 
		      void func(SOCKET_DATA *sock, char *arg, BUFFER *buf)) {
  ECMD_DATA *old_cmd = hashGet(editor->cmds, cmd);
  // make sure we're not trying to replace a reserved command
  if(!old_cmd || !old_cmd->reserved) {
    hashPut(editor->cmds, cmd, newEditorCommand(desc, func, FALSE));
    if(old_cmd) deleteEditorCommand(old_cmd);
  }
}

void editorRemoveCommand(EDITOR *editor, const char *cmd) {
  ECMD_DATA *old_cmd = hashGet(editor->cmds, cmd);
  // make sure the command isn't reserved
  if(old_cmd && !old_cmd->reserved) {
    hashRemove(editor->cmds, cmd);
    deleteEditorCommand(old_cmd);
  }
}

void dflt_editor_complete(SOCKET_DATA *sock, const char *str) {
  EDITOR_AUX_DATA *data = socketGetAuxiliaryData(sock, "editor_aux_data"); 
  bufferClear(data->buf);
  bufferCat(data->buf, str);
}

void begin_editor(SOCKET_DATA *sock) {
  EDITOR_AUX_DATA *data = socketGetAuxiliaryData(sock, "editor_aux_data"); 
  if(data->editor->init) data->editor->init(sock);

  editorDefaultHeader(sock);
  
  // if we have a "list" command, execute it. Otherwise, cat the buf
  ECMD_DATA *list = NULL;
  if((list = hashGet(data->editor->cmds, "l")) != NULL)
    list->func(sock, "", data->working_buf);
  else
    text_to_buffer(sock, bufferString(data->working_buf));

  socketPushInputHandler(sock, editorInputHandler, data->editor->prompt, 
			 "text editor");
}

void socketStartPyEditorFunc(SOCKET_DATA *sock, EDITOR *editor,const char *dflt,
			     void *py_complete) {
  EDITOR_AUX_DATA *data = socketGetAuxiliaryData(sock, "editor_aux_data"); 
  data->working_buf = newBuffer(1);
  bufferCat(data->working_buf, dflt);
  data->editor      = editor;
  data->py_complete = py_complete;
  Py_XINCREF(data->py_complete);
  begin_editor(sock);
}

void socketStartEditorFunc(SOCKET_DATA *sock, EDITOR *editor, const char *dflt,
			   void (* on_complete)(SOCKET_DATA *, const char *)) {
  EDITOR_AUX_DATA *data = socketGetAuxiliaryData(sock, "editor_aux_data"); 
  data->working_buf = newBuffer(1);
  bufferCat(data->working_buf, dflt);
  data->editor      = editor;
  data->on_complete = on_complete;
  if(editor->init) editor->init(sock);
  begin_editor(sock);
}

void socketStartEditor(SOCKET_DATA *sock, EDITOR *editor, BUFFER *buf) {
  EDITOR_AUX_DATA *data = socketGetAuxiliaryData(sock, "editor_aux_data"); 
  data->buf         = buf;
  socketStartEditorFunc(sock, editor, bufferString(buf), dflt_editor_complete);
}

EDITOR *socketGetEditor(SOCKET_DATA *sock) {
  EDITOR_AUX_DATA *data = socketGetAuxiliaryData(sock, "editor_aux_data");
  return data->editor;
}