pdirt/data/
pdirt/data/HELP/
pdirt/data/HELP/0/
pdirt/data/HELP/F/
pdirt/data/HELP/G/
pdirt/data/HELP/H/
pdirt/data/HELP/J/
pdirt/data/HELP/K/
pdirt/data/HELP/O/
pdirt/data/HELP/Q/
pdirt/data/HELP/R/
pdirt/data/HELP/U/
pdirt/data/HELP/V/
pdirt/data/HELP/Y/
pdirt/data/HELP/Z/
pdirt/data/MESSAGES/
pdirt/data/POWERINFO/
pdirt/data/WIZ_ZONES/
pdirt/drv/
pdirt/drv/bin/
pdirt/drv/compiler/converter/
pdirt/drv/compiler/libs/
pdirt/drv/compiler/scripts/
pdirt/drv/include/AberChat/
pdirt/drv/include/InterMud/
pdirt/drv/include/machine/
pdirt/drv/src/InterMud/
pdirt/drv/src/Players/
pdirt/drv/utils/UAFPort/
pdirt/drv/utils/dnsresolv/
pdirt/drv/utils/gdbm/
/****************************************************************************
 ** pDIRT: GPE (General Purpose Editor)
 **
 ** Author       : Peter Eussen
 ** Version      : 1.01
 ** Last Revision: 18 Jan 1996
 ** 
 ** This editor replaces the old 'writer' with a almost fully featured 
 ** editor. Although the editing has been greatly improved, the interface
 ** has simplified, which causes some problems with implementation, but 
 ** that's not of my concern any more, since it works for me ;)
 **
 ** To start the editor, use the start_editor() function which needs:
 **    Player number, function that handles the file created by the editor
 **    Maximum amount of lines the player can type, and an optional parameter,
 **    which you can use to store data (use NULL if not needed).
 **
 ** The editor allows you to review your text, overwrite lines, delete lines,
 ** append lines, cancel and quit. The prompt is automatically built.
 **************************************************************************/
#include "kernel.h"
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "mud.h"
#include "log.h"
#include "parse.h"
#include "editor.h"
#include "bprintf.h"

/****************************************************************************
 ** PROTOTYPES
 ****************************************************************************/
void editor_reader(char *inp);
void end_editor(void);
void delete_line(int i);
void review(Line *start);
void reinitialise_editor(void);
Line *find_line(int nr);

static char *Edit_Header = 
   "&+w[ &+wpDirt&+w Editor V1.01 ========================================================&+w]&*\n"
   "&+w[  &+w~c &+wCancel Editor                    &+w~d <n> &+wDelete Line &+wn&*                  &+w]&*\n"
   "&+w[  &+w~q &+wEnd Editor                       &+w~r &+wReview Current Message             &+w]&*\n"
   "&+w[  &+w~h &+wEditor Help                      &+w~e <command> &+wExecute &+wCommand&*.         &+w]&*\n"
   "&+w[  &+w~a &+wAppend at the End.               &+w~o <n> &+wStart Overwriting at line &+wn&*    &+w]&*\n"
   "&+w[&+w============================================================================&+w]&*\n";

int start_file_editor(FILE *outp, void (*func)(char *,char *), int max_lines, 
                     char *fn)
{   if (cur_player->editor.active)
    {  bprintf("Close other editor first.\n");
       return 0;
    }

    if (fn == NULL)
    {  mudlog("ERROR: start_file_writer() with no filename");
       return -1;
    }

    if (outp == NULL)
    {  mudlog("ERROR: start_file_writer() with no file.");
       return -1;
    }

    bprintf(Edit_Header); 

    cur_player->editor.fp = outp;
    strcpy(cur_player->editor.filename,fn);
    strcpy(cur_player->editor.old_prompt,cur_player->prompt);
    cur_player->editor.func = func;

    cur_player->editor.start = NULL;
    cur_player->editor.current = NULL;
    cur_player->editor.parameter = NULL;
    cur_player->editor.num_lines = 0;
    cur_player->editor.max_lines = max_lines;
    cur_player->editor.active = True;
    cur_player->editor.mode = Append;

    push_input_handler(editor_reader);
    sprintf(cur_player->prompt,"&+w[&+w%.2d&+w]: &*",cur_player->editor.num_lines+1);
    bprintf("\r%s",cur_player->prompt);
    return 1;
}

/*****************************************************************************
 ** START_EDITOR
 ** Initialisation routine. Sets up all parameters needed for this editor.
 *****************************************************************************/
int start_editor(int player, void (*func)(char *,char *),int max_lines, char *param)
{   int save_mynum = real_mynum;

    setup_globals(player);

    if (cur_player->editor.active)
    {   bprintf("Close other editor first.\n");
        return 0;
    }

    bprintf(Edit_Header);

    sprintf(cur_player->editor.filename,"%s/TMP/Editor.%s",data_dir,pname(mynum));
    cur_player->editor.fp = fopen(cur_player->editor.filename,"w");
  
    if (cur_player->editor.fp == NULL)
    {   fwerror(cur_player->editor.filename);
        return -1;
    }

    strcpy(cur_player->editor.old_prompt,cur_player->prompt);
    cur_player->editor.func = func;

    cur_player->editor.start = NULL;
    cur_player->editor.current = NULL;
    cur_player->editor.num_lines = 0;
    cur_player->editor.max_lines = max_lines;
    cur_player->editor.active = True;
    cur_player->editor.mode = Append;

    if (param == NULL)
       cur_player->editor.parameter = NULL;
    else
    {  cur_player->editor.parameter = NEW(char,strlen(param) + 1);
       strcpy(cur_player->editor.parameter,param);
    }

    push_input_handler(editor_reader);
    sprintf(cur_player->prompt,"&+w[&+w%.2d&+w]: &*",cur_player->editor.num_lines+1);
    bprintf("\r%s",cur_player->prompt);
    setup_globals(save_mynum);
    return 1;
}

void editor_reader(char *inp)
{   Line *l=NULL;

    if (inp == NULL)
        replace_input_handler(editor_reader);
    else if (inp[0] == '~' || inp[0] == '$' || inp[0] == '*')
    {   /* Process commands */
        switch(inp[1]) {
        case 'c' :	/* Cancel Editor */
	case 'C' :
		bprintf("&+wEditor Cancelled.\n");
		end_editor();
		unlink(cur_player->editor.filename);
		cur_player->editor.func(NULL,cur_player->editor.parameter);
                reinitialise_editor();
		return;
	case '*' :	/* Quit Editor */
	case 'q' :
	case 'Q' :	
		bprintf("&+wQuitting Editor.\n");
		end_editor();

                if (cur_player->editor.num_lines == 0)
                   cur_player->editor.func(NULL,cur_player->editor.parameter);
                else
		   cur_player->editor.func(cur_player->editor.filename,
			cur_player->editor.parameter);

                reinitialise_editor();
		return;
	case 'D' :	/* Delete line or last line */
        case 'd' :	
                inp += 2;
                while(!isdigit(*inp) && *inp != '\0')
                  inp++;

                if (isdigit(*inp))
                  delete_line(atoi(inp));
		else 
                  delete_line(cur_player->editor.num_lines);
		break;
	case 'o':	/* Start Overwrite mode */
	case 'O':
		inp += 2;
                while(!isdigit(*inp) && *inp != '\0')
                  inp++;

                if (!isdigit(*inp))
                   bprintf("&+wOverwrite which line?\n");
                else
                {  if (atoi(inp) > cur_player->editor.num_lines)
                   {  bprintf("&+wYou still need to type that line.\n");
                      break;
                   }

                   cur_player->editor.current = find_line(atoi(inp));

                   if (cur_player->editor.current == NULL)
                   {   cur_player->editor.mode = Append;
                       sprintf(cur_player->prompt,"&+w[&+w%.2d&+w]: &*",cur_player->editor.current->nr+1);
                   }
                   else
		   {   cur_player->editor.mode = Overwrite;
                       sprintf(cur_player->prompt,"&+w[&+w%.2d&+w]&+w> &*",cur_player->editor.current->nr);
                   }
                }
                break;
	case 'a':	/* Start Append Mode */
	case 'A':
		bprintf("&+wAppending after line %d\n",cur_player->editor.num_lines);
		cur_player->editor.current = find_line(cur_player->editor.num_lines);
		cur_player->editor.mode = Append ;
                sprintf(cur_player->prompt,"&+w[&+w%.2d&+w]: &*",cur_player->editor.num_lines+1);
		break;
	case 'r':	/* Review current message */
	case 'R':
		review(cur_player->editor.start);
		break;
	case 'h':	/* Command information */
	case 'H':
		bprintf("&+wThe Editor has the following commands: \n"
			" &+w~q          :  &+wEnd Editor and save message.\n"
			" &+w~c          :  &+wEnd Editor and do NOT save the message.\n"
			" &+w~d <n>      :  &+wDelete line &+wn&+w.\n"
			" &+w~r          :  &+wReview the current text.\n"
			" &+w~a          :  &+wAppend at the end of the file.\n"
			" &+w~o <n>      :  &+wStart Overwriting at line &+wn&+w.\n"
			" &+w~h          :  &+wThis help file.\n"
			" &+w~e <command>:  &+wExecute a command.\n"
			"&+wBecause some mud clients have a problem with the &+w'~'&+w char, you can also use the &+w'$'&*.\n");
		break;
	case 'e':	/* Execute a game command */
	case 'E':
		inp += 2;
		while (isspace(*inp))
		   inp++;
		Game_Com(inp,False);
		break;
        default:
		bprintf("Unknown command, use &+w*h&* for help.\n");
        }
    }
    else if (cur_player->editor.mode == Overwrite)
    {   l = cur_player->editor.current;

        if (l->line != NULL)
          free(l->line);

        l->line = NEW(char,strlen(inp) + 2);
        strcpy(l->line,inp);
        strcat(l->line,"\n");
        
        if (l->next != NULL)
        {  cur_player->editor.current = l->next;
           sprintf(cur_player->prompt,"&+w[&+w%.2d&+w]&+w> &*",cur_player->editor.current->nr);
        }
        else
        {  /* Reached the end of the file, switch to append */
           cur_player->editor.mode = Append;
           sprintf(cur_player->prompt,"&+w[&+w%.2d&+w]: &*",cur_player->editor.num_lines+1);
        }
    }
    else
    {  char *p;

       if (cur_player->editor.num_lines == cur_player->editor.max_lines)
       {   bprintf("&+wWarning: &+wBuffer is full, can't add new lines to the text.\n");
           bprintf("\r%s\n",cur_player->prompt);
           return;
       }

       for (p = inp; strlen(p) > 0 && cur_player->editor.num_lines < cur_player->editor.max_lines; )
       {
           l = NEW(Line,1);
           if (strlen(p) > 74)
           {  l->line = NEW(char,77);
              strncpy(l->line,p,74);
              strcat(l->line,"\n");
              p += 74;
           }
           else 
           {  l->line = NEW(char,strlen(p) +2);
              strcpy(l->line,p);
              strcat(l->line,"\n");
              p += strlen(p);
           }
           l->nr = ++cur_player->editor.num_lines;

           if (cur_player->editor.start == NULL)
           {  cur_player->editor.start = l;
              l->next = NULL;
           }
       
           if (cur_player->editor.current != NULL)
           {  l->next = cur_player->editor.current->next;
              cur_player->editor.current->next = l;
           }

           cur_player->editor.current = l;
       }

       if (cur_player->editor.num_lines == (cur_player->editor.max_lines -5))
          bprintf("&+wWarning&* &+wReaching Buffer Limit, please end your message.\n");
       else if (cur_player->editor.num_lines == cur_player->editor.max_lines -5)
          bprintf("&+wWarning &+wBuffer full, line could not be added completely.\n");
       sprintf(cur_player->prompt,"&+w[&+w%.2d&+w]: &*",cur_player->editor.num_lines+1);
    }
    bprintf("\r%s",cur_player->prompt);
}

/* A little tricky, but the termination process is broken in two. This is
 * done cause sometimes you want to open the file in the handler, to write
 * some additional data. But it is asking for problems if you have two
 * links for writing to the same file, so that is done in end_editor().
 * This handles the cleanup of the file, if it is still there, and
 * reinitialises the data.
 */
void reinitialise_editor(void)
{   int fd;

    if (cur_player->editor.parameter != NULL)
       free(cur_player->editor.parameter);

    cur_player->editor.parameter = NULL;
    cur_player->editor.func = NULL;
    cur_player->editor.num_lines = 0;

    fd = open(cur_player->editor.filename,O_RDONLY);
    if (fd != -1)
    {  close(fd);
       unlink(cur_player->editor.filename);
    }
    bprintf("\r%s",cur_player->prompt);
}

/* Writes buffer to a temporary file, and closes the file. Also resets the
 * input handler as it was before the writer was started.
 */
void end_editor(void)
{   Line *l = cur_player->editor.start, *p;

    while (l != NULL)
    {   fputs(l->line,cur_player->editor.fp);
        free(l->line);
        p = l;
        l = l->next;
        free(p);
    }
 
    cur_player->editor.start = cur_player->editor.current = NULL;
    cur_player->editor.active = False;
    fclose(cur_player->editor.fp);

    strcpy(cur_player->prompt,cur_player->editor.old_prompt);
    pop_input_handler();
}

void delete_line(int i)
{  Line *p = cur_player->editor.start, *l;

   if (i > cur_player->editor.num_lines || i < 1 || p == NULL)
   {   bprintf("&+wThere is nothing to be deleted.\n");
       return;
   }

   if (i == 1)
   {  cur_player->editor.start = p->next;
      cur_player->editor.num_lines--;

      if (p->next == NULL)
        cur_player->editor.current = NULL; 

      free(p->line);
      free(p);

      l = cur_player->editor.start;	/* Update line numbers */
      while (l != NULL)
      {   l->nr--;
          l = l->next;
      }
      
      if (cur_player->editor.start == NULL && cur_player->editor.mode == Overwrite)
         cur_player->editor.mode = Append;
   } 
   else
   {   l = p;
       while (l->next != NULL && l->next->nr != i)
           l = l->next;

       if (l->next == NULL)
       {  bprintf("&+wLine not found.\n");
          return;
       }

       if (l->next->nr == i)
       {   p = l->next;
           l->next = p->next;

           if (cur_player->editor.current == p)
           {  if (cur_player->editor.mode == Overwrite)
              {    if (l->next == NULL)
                   {   cur_player->editor.mode = Append;
                       cur_player->editor.current = l;
                   }
                   else
                       cur_player->editor.current =  l->next;
              }
              else
                cur_player->editor.current = l;
           }

           cur_player->editor.num_lines--;
           free(p->line);
           free(p);

           l = l->next;				/* Update line numbers */
           while (l != NULL)
           {   l->nr--;
               l = l->next;
           }
       }
   }
   bprintf("&+wDeleted line %d.\n",i);

   sprintf(cur_player->prompt,"&+w[&+w%.2d&+w]%s &*",
	cur_player->editor.mode == Overwrite ? cur_player->editor.current->nr :
	cur_player->editor.num_lines+1,
	cur_player->editor.mode == Overwrite ? "&+w>" : ":");
}

void review(Line *l)
{   bprintf("&+wThis is what you typed sofar: \n&+w%s&*\n",DOUBLELINE);

    while (l != NULL)
    {   bprintf("&+w[&+w%.2d&+w]&* %s",l->nr,l->line);
        l = l->next;
    }
    bprintf("&+w%s\n&+wEnd of Message.\n",DOUBLELINE);
}

Line *find_line(int nr)
{  Line *l = cur_player->editor.start;

   if (l == NULL)
     return l;

   while (l->nr != nr && l->next != NULL)
      l = l->next;

   return l;
}

void terminate_editors(void)
{   int plx,save = real_mynum;

    for (plx = 0; plx < max_players; plx++)
    {   if (players[plx].editor.active)
        {   setup_globals(real_mynum);
            end_editor();
            reinitialise_editor();
        }    
    }
    setup_globals(save);
}

/* Since the data is finally written to a file, we can use stat() to get the
 * filesize.
 */
long editor_message_size(char *filename)
{  struct stat buffer;

   stat(filename,&buffer);
   return buffer.st_size + 1;
}