/*
 *  New editor code
 *  Author: Cronel (cronel_kal@hotmail.com)
 *  of FrozenMUD (empire.digiunix.net 4000)
 *
 *  Permission to use and distribute this code is granted provided
 *  this header is retained and unaltered, and the distribution
 *  package contains all the original files unmodified.
 *  If you modify this code and use/distribute modified versions
 *  you must give credit to the original author(s).
 */
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "mud.h"

/****************************************************************************
 * Data types and other definitions
 */

typedef struct editor_line EDITOR_LINE;

#define CHAR_BLOCK (80)

#define BLOCK_ROUNDUP( size )		(((size)+CHAR_BLOCK-1) / CHAR_BLOCK * CHAR_BLOCK)

#define RESIZE_IF_NEEDED( buf, buf_size, buf_used, added_use )	\
	if( (buf_used) + (added_use) >= (buf_size) ) 		\
	{ 							\
		sh_int added_size; 				\
		added_size = BLOCK_ROUNDUP( added_use );	\
		if( added_size == 0 ) 				\
			added_size = CHAR_BLOCK; 		\
		RECREATE( (buf), char, buf_size + added_size );	\
		(buf_size) += added_size; 			\
	}

struct editor_line
{
	char *			line;		/* line text */
	sh_int			line_size;	/* size allocated in "line" */
	sh_int			line_used;	/* bytes used of "line" */
	EDITOR_LINE *	next;
};

struct	editor_data
{
	EDITOR_LINE *	first_line;	/* list of lines */
	sh_int			line_count;	/* number of lines allocated */
	EDITOR_LINE *	on_line;	/* pointer to the line being edited */
	int				text_size;	/* total size of text (not counting
				    		       newlines). */
	int				max_size;	/* max size in chars of string being
								   edited (counting newlines) */
	char *			desc;		/* buffer description */
};
/* "max_size" is the maximum size of the final text converted to string */
/* "text_size" is equal to the strlen of all lines added up; the actual
 * total length when converted to string is equal to this number plus
 * line_count * 2, because of the trailing "\n\r" that has to be added
 * to each line (of course, plus 1 because of the final \0).
 * Thus, if(total_size + line_count * 2 +1) > max_size, the buffer cant
 * hold more data.
 */
/* Hence, this define: */
#define TOTAL_BUFFER_SIZE( edd )	(edd->text_size + edd->line_count * 2 +1 )



/****************************************************************************
 * Function declarations
 */

/* funcs to manipulate editor datas */
EDITOR_LINE *make_new_line( char *str );
void discard_editdata( EDITOR_DATA *edd );
EDITOR_DATA *clone_editdata( EDITOR_DATA *edd );
EDITOR_DATA *str_to_editdata( char *str, sh_int max_size );
char *editdata_to_str( EDITOR_DATA *edd );

/* simple functions to set a description for what's currently
 * being edited */
void set_editor_desc( CHAR_DATA *ch, char *new_desc );
void editor_desc_printf( CHAR_DATA *ch, char *desc_fmt, ... );

/* the main editor functions visible to the rest of the code */
void start_editing_nolimit( CHAR_DATA *ch, char *old_text, sh_int max_total );
char *copy_buffer( CHAR_DATA *ch );
void stop_editing( CHAR_DATA *ch );
/* main editing function */
void edit_buffer( CHAR_DATA *ch, char *argument );

/* misc functions */
char *finer_one_argument( char *argument, char *arg_first );
char *text_replace( char *src, char *word_src, char *word_dst, sh_int *pnew_size, sh_int *prepl_count );

/* editor sub functions */
void editor_print_info( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument );
void editor_help( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument );
void editor_clear_buf( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument );
void editor_search_and_replace( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument );
void editor_insert_line( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument );
void editor_delete_line( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument );
void editor_goto_line( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument );
void editor_list( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument );
void editor_abort( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument );
void editor_escaped_cmd( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument );
void editor_save( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument );
void editor_format(CHAR_DATA *ch, EDITOR_DATA *edd, char *argument );



/****************************************************************************
 * Edit_data manipulation functions
 */

EDITOR_LINE *make_new_line( char *str )
{
	EDITOR_LINE *new_line;
	sh_int size;

	size = strlen( str );
	size = BLOCK_ROUNDUP( size );
	if( size == 0 )
		size = CHAR_BLOCK;

	CREATE(new_line, EDITOR_LINE, 1);
	CREATE(new_line->line, char, size);
	new_line->line_size = size;
	new_line->line_used = strlen( str );
	strcpy( new_line->line, str );

	return new_line;
}

void discard_editdata( EDITOR_DATA *edd )
{
	EDITOR_LINE *eline, *elnext;

	eline = edd->first_line;
	while( eline )
	{
		elnext = eline->next;
		DISPOSE( eline->line );
		DISPOSE( eline );
		eline = elnext;
	}
	if( edd->desc )
		STRFREE( edd->desc );
	DISPOSE( edd );
}

EDITOR_DATA *clone_editdata( EDITOR_DATA *edd )
{
	EDITOR_DATA *new_edd;
	EDITOR_LINE *new_line, *eline, root_line;

	CREATE( new_edd, EDITOR_DATA, 1 );
	new_line = &root_line;
	for( eline = edd->first_line ; eline ; eline = eline->next )
	{
		new_line->next = make_new_line( eline->line );
		if( edd->on_line == eline )
			new_edd->on_line = new_line->next;
		new_line = new_line->next;
	}

	new_edd->max_size = edd->max_size;
	new_edd->text_size = edd->text_size;
	new_edd->line_count = edd->line_count;
	new_edd->first_line = root_line.next;
	new_edd->desc = STRALLOC( edd->desc );

	return new_edd;
}

EDITOR_DATA *str_to_editdata( char *str, sh_int max_size )
{
	char *p;
	EDITOR_DATA *edd;
	EDITOR_LINE *eline;
	sh_int i;
	sh_int tsize, line_count;

	CREATE(edd, EDITOR_DATA, 1);
	eline = make_new_line( "" );
	edd->first_line = eline;
	i = 0;

	tsize = 0;
	line_count = 1;
	p = str;
	while( *p )
	{
		if( tsize + line_count*2 + 1 >= max_size )
			break;
		if( *p == '\r' )
			;
		else if( *p == '\n' )
		{
			eline->line[i] = '\0';
			eline->next = make_new_line( "" );
			eline = eline->next;
			line_count++;
			i=0;
		}
		else
		{
			eline->line[i] = *p;
			eline->line_used++;
			tsize++;
			i++;
			RESIZE_IF_NEEDED( eline->line, eline->line_size,
				eline->line_used, 1 );
		}
		p++;
	}

	if( eline->line[0] != '\0' )
	{
		eline->line[i] = '\0';
		eline->next = make_new_line( "" );
		line_count++;
		eline = eline->next;
	}

	edd->line_count = line_count;
	edd->on_line = eline;
	edd->max_size = max_size;
	edd->text_size = tsize;

	return edd;
}

char *editdata_to_str( EDITOR_DATA *edd )
{
	EDITOR_LINE *eline;
	char *buf, *src, *tmp;
	sh_int size, used, i;

	CREATE(buf, char, MAX_STRING_LENGTH );
	size = MAX_STRING_LENGTH;
	used = 0;
	buf[0] = '\0';

	eline = edd->first_line;
	i = 0;
	while( eline )
	{
		/* ignore the last empty line */
		if( eline->next == NULL && eline->line[0] == '\0' )
			break;
		src = eline->line;
		while( *src )
		{
			buf[i++] = *src++;
			used++;

			if( used >= size-3 )
			{
				RECREATE(buf, char, size + MAX_STRING_LENGTH );
				size += MAX_STRING_LENGTH;
			}
		}
		buf[i++] = '\n';
		buf[i++] = '\r';
		used += 2;
		eline = eline->next;
	}

	buf[i++] = '\0';
	used++;

	tmp = STRALLOC( buf );
	DISPOSE(buf);
	smush_tilde(tmp);
	return tmp;
}



/****************************************************************************
 * Main editor functions
 */

void set_editor_desc( CHAR_DATA *ch, char *new_desc )
{
	if( !ch || !ch->editor )
		return;

	if( ch->editor->desc )
		STRFREE( ch->editor->desc );
	ch->editor->desc = STRALLOC( new_desc );
}

void editor_desc_printf( CHAR_DATA *ch, char *desc_fmt, ... )
{
	char buf[ MAX_STRING_LENGTH * 2 ]; /* umpf.. */
	va_list args;

	va_start(args, desc_fmt);
	vsprintf(buf, desc_fmt, args);
	va_end(args);

	set_editor_desc( ch, buf );
}

void start_editing_nolimit( CHAR_DATA *ch, char *old_text, sh_int max_total )
{
	if ( !ch->desc )
	{
	   bug( "Fatal: start_editing: no desc", 0 );
	   return;
	}
	if ( ch->substate == SUB_RESTRICTED )
	   bug( "NOT GOOD: start_editing: ch->substate == SUB_RESTRICTED", 0 );

	set_char_color( AT_GREEN, ch );
	send_to_char( "Begin entering your text now (/? = help /s = save /c = clear /l = list)\n\r", ch );
	send_to_char( "-----------------------------------------------------------------------\n\r", ch );
	if ( ch->editor )
	  stop_editing( ch );

	ch->editor = str_to_editdata( old_text, max_total );
	ch->editor->desc = STRALLOC( "Unknown buffer" );
		ch->desc->connected = CON_EDITING;

    	send_to_char( "> ", ch );
}

char *copy_buffer( CHAR_DATA *ch )
{
   char *buf;

   if ( !ch )
   {
	bug( "copy_buffer: null ch", 0 );
	return STRALLOC( "" );
   }

   if ( !ch->editor )
   {
	bug( "copy_buffer: null editor", 0 );
	return STRALLOC( "" );
   }

   buf = editdata_to_str( ch->editor );
   return buf;
}

void stop_editing( CHAR_DATA *ch )
{
    set_char_color( AT_PLAIN, ch );
    discard_editdata( ch->editor );
    ch->editor = NULL;
    send_to_char( "Done.\n\r", ch );
    ch->dest_buf  = NULL;
    ch->spare_ptr = NULL;
    ch->substate  = SUB_NONE;
    if ( !ch->desc )
    {
	bug( "Fatal: stop_editing: no desc", 0 );
	return;
    }
    	ch->desc->connected = CON_PLAYING;
}

void edit_buffer( CHAR_DATA *ch, char *argument )
{
    DESCRIPTOR_DATA *d;
    EDITOR_DATA *edd;
    EDITOR_LINE *newline;
    char cmd[MAX_INPUT_LENGTH];
    sh_int linelen;
    bool cont_line;
    char *p;

    d = ch->desc;
    if ( d == NULL )
    {
	send_to_char( "You have no descriptor.\n\r", ch );
	return;
    }

    if ( d->connected != CON_EDITING)
    {
	send_to_char( "You can't do that!\n\r", ch );
	bug( "Edit_buffer: d->connected != CON_EDITING", 0 );
	return;
    }

    if ( ch->substate <= SUB_PAUSE )
    {
	send_to_char( "You can't do that!\n\r", ch );
	bug( "Edit_buffer: illegal ch->substate (%d)", ch->substate );
	d->connected = CON_PLAYING;
	return;
    }

    if ( !ch->editor )
    {
	send_to_char( "You can't do that!\n\r", ch );
	bug( "Edit_buffer: null editor", 0 );
	d->connected = CON_PLAYING;
	return;
    }

    edd = ch->editor;

    if ( argument[0] == '/' || argument[0] == '\\' )
    {
	argument = one_argument( argument, cmd );

	if ( !str_cmp( cmd+1, "?" ) )
		editor_help( ch, edd, argument );
    else if( !str_cmp( cmd+1, "c") )
		editor_clear_buf( ch, edd, argument );
	else if( !str_cmp( cmd+1, "r") )
//		send_to_char( "'/r' is temporally disabled until it can be fixed.\n\r", ch );
		editor_search_and_replace( ch, edd, argument );
	else if( !str_cmp( cmd+1, "i") )
		editor_insert_line( ch, edd, argument );
	else if( !str_cmp( cmd+1, "d") )
		editor_delete_line( ch, edd, argument );
	else if( !str_cmp( cmd+1, "g") )
		editor_goto_line( ch, edd, argument );
	else if( !str_cmp( cmd+1, "l") )
		editor_list( ch, edd, argument );
	else if( !str_cmp( cmd+1, "a") )
		editor_abort( ch, edd, argument );
	else if( !str_cmp( cmd+1, "s") )
		editor_save( ch, edd, argument );
	else if( !str_cmp( cmd+1, "!") )
		editor_escaped_cmd( ch, edd, argument );
	else if( !str_cmp( cmd+1, "p") )
		editor_print_info( ch, edd, argument );
	else if ( !str_cmp( cmd+1, "f" ) )
	{/*
	    if( !IS_IMMORTAL( ch ) )
		send_to_char( "'/f' is temporally disabled until it can be fixed.\n\r", ch );
	    else
*/		editor_format( ch, edd, argument );
	}
	else
		send_to_char( "Uh? Type '/?' to see the list of valid editor commands.\n\r", ch );

	if( str_cmp(cmd+1, "a") && str_cmp(cmd+1, "s") )
		send_to_char( "> ", ch );
	return;
    }

    /* Kludgy fix. Read_from_buffer in comm.c adds a space on
     * empty lines. Don't let this fill up usable buffer space.. */
    if( !str_cmp( argument, " " ) )
	strcpy( argument, "" );

    linelen = strlen(argument);

    p = argument + linelen - 1;
    while( p > argument && isspace(*p) )
	p--;
    if( p > argument && *p == '\\' )
    {
	cont_line = TRUE;
	*p = '\0';
    }
    else
	cont_line = FALSE;


    if( TOTAL_BUFFER_SIZE(edd) + linelen+2 >= edd->max_size )
    {
	send_to_char( "Buffer full.\n\r", ch );
	editor_save( ch, edd, "");
    }
    else
    {
	/* add it to the current line */
	RESIZE_IF_NEEDED( edd->on_line->line, edd->on_line->line_size,
		edd->on_line->line_used, linelen+1 );
	strcat( edd->on_line->line, argument );
	edd->on_line->line_used += linelen;
	edd->text_size += linelen;

	/* create a line and advance to it */
	if( !cont_line )
	{
		newline = make_new_line( "" );
		newline->next = edd->on_line->next;
		edd->on_line->next = newline;
		edd->on_line = newline;
		edd->line_count++;
	}
	else
		send_to_char( "(Continued)\n\r", ch );

 	send_to_char( "> ", ch );
    }
}

void editor_print_info( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument )
{
	sh_int i;
	EDITOR_LINE *eline;

	eline = edd->first_line;
	i = 0;
	while( eline )
	{
		i++;
		if( eline == edd->on_line )
			break;
		eline = eline->next;
	}

	ch_printf( ch,
		"Currently editing: %s\n\r"
		"Total lines: %4d   On line:  %4d\n\r"
		"Buffer size: %4d   Max size: %4d\n\r",
		edd->desc ? edd->desc : "(Null description)",
		edd->line_count, i,
		TOTAL_BUFFER_SIZE(edd), edd->max_size );
}

void editor_help( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument )
{
    sh_int i;
    char *arg[] = {"", "l", "c", "d", "g", "i", "r", "a", "p", "!", "s", NULL};
    char *editor_help[] = {
	/* general help */
        "Editing commands\n\r"
	"---------------------------------\n\r"
	"/l [range]      list buffer\n\r"
    	"/c              clear buffer\n\r"
    	"/d <line>       delete line\n\r"
    	"/g <line>       goto line\n\r"
    	"/i <line>       insert line\n\r"
    	"/r <old> <new>  global replace (temporarily disabled)\n\r"
    	"/a              abort editing\n\r"
    	"/f <format>     format text in buffer (temporarily disabled)\n\r"
	"/p              print information\n\r"
	"/! <command>    execute command (do not use another editing command)\n\r"
	"/s              save buffer\n\r"
	"Type /? <command>  to get more information on each command.\n\r\n\r",

	"/l [range]: Lists the buffer. Shows what you've written. Optionaly\n\r"
	"   takes a range of lines as argument.\n\r",

	"/c: Clears the buffer, leaving only one empty line.\n\r",

	"/d <line>: Deletes a line. If you delete the line currently being\n\r"
	"   edited, the insertion point is moved down if possible, if not, up.\n\r",

	"/g <line>: Moves the insertion point to a given line.\n\r",

	"/i <line>: Inserts an empty line before the given line.\n\r",

	"/r <old text> <new text>: Global search and replace text. The arguments\n\r"
	"  are case-sensitive. To replace a multi-word text, surround it with\n\r"
	"  single quotes. When inside quotes, you must escape the single quote\n\r"
	"  character, double quote character, and the bar: (') becomes (\\'),\n\r"
	"  (\") becomes (\\\") and (\\) becomes (\\\\)\n\r",

	"/a: Aborts edition, terminating the edition session and throwing\n\r"
	"   away what you've edited.\n\r",

	"/p: Prints information about the current editing session.\n\r",

	"/!: Escaped command. Executes the given command as if you were\n\r"
	"   outside the editor. This is only allowed to imms, since it can\n\r"
	"   potentialy crash the mud.\n\r",

	"/s: Saves the current buffer, terminating the edition session.\n\r",
	};

    for( i=0; arg[i] != NULL ; i++ )
    {
	if( !str_cmp( argument, arg[i] ) )
	    break;
    }

    if( arg[i] == NULL )
	send_to_char( "No editor help about that.\n\r", ch );
    else
	send_to_char( editor_help[i], ch );
}

void editor_clear_buf( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument )
{
    char *desc;
    sh_int max_size;
    max_size = edd->max_size;
    if (edd->desc)
    desc = STRALLOC( edd->desc );
    else
    desc = STRALLOC( "" );
    discard_editdata( edd );
    ch->editor = str_to_editdata( "", max_size );
    ch->editor->desc = desc;
    send_to_char( "Buffer cleared.\n\r", ch );
}


void editor_search_and_replace( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument )
{
    char word_src[ MAX_INPUT_LENGTH];
    char word_dst[ MAX_INPUT_LENGTH];
    EDITOR_DATA *cloned_edd;
    EDITOR_LINE *eline;
    char *new_text;
    sh_int new_size, repl_count, line_repl;

    argument = finer_one_argument( argument, word_src );
    argument = finer_one_argument( argument, word_dst );
    if ( word_src[0] == '\0' || word_dst[0] == '\0' )
    {
	send_to_char( "Need word to replace, and replacement.\n\r", ch );
	return;
    }
    if ( strcmp( word_src, word_dst ) == 0 )
    {
	send_to_char( "Done.\n\r", ch );
	return;
    }

    /* Warning: the replacement of the word can result in the buffer growing
     * larger than its maximum allowed size. To control this, the buffer is
     * cloned, the replacement is applied to the clone, and if the size results
     * legal after the operation, the original buffer is discarded and the
     * clone is assigned as the current editing buffer. If the clone's size
     * results too large after the replacement, the clone is simply discarded
     * and a warning is given to the user */

    cloned_edd = clone_editdata( edd );

    eline = cloned_edd->first_line;
    repl_count = 0;
    while( eline )
    {
	new_text = text_replace( eline->line, word_src, word_dst, &new_size, &line_repl );
	DISPOSE( eline->line );
	eline->line = new_text;
	cloned_edd->text_size -= eline->line_used;
	eline->line_used = strlen( eline->line );
	cloned_edd->text_size += eline->line_used;
	eline->line_size = new_size;
	repl_count += line_repl;
	eline = eline->next;
    }

    if( TOTAL_BUFFER_SIZE( cloned_edd ) >= cloned_edd->max_size )
    {
	send_to_char( "As a result of this operation, the buffer would grow\n\r"
                      "larger than its maximum allowed size. Operation has been\n\r"
		      "cancelled.\n\r", ch );
	discard_editdata( cloned_edd );
    }
    else
    {
    	ch_printf( ch, "Replacing all occurrences of '%s' with '%s'...\n\r", word_src, word_dst );
	discard_editdata( edd );
	ch->editor = cloned_edd;
    	ch_printf( ch, "Found and replaced %d occurrence(s).\n\r", repl_count );
    }

}



void editor_insert_line( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument )
{
	sh_int lineindex, num;
	EDITOR_LINE *eline, *newline;

	if( argument[0] == '\0' || !is_number(argument) )
	{
		send_to_char( "Must supply the line number.\n\r", ch );
		return;
	}
	lineindex = atoi(argument);

	if( lineindex < 1 || lineindex > edd->line_count )
	{
		ch_printf( ch, "Line number is out of range (1-%d).\n\r", edd->line_count );
		return;
	}

	newline = make_new_line( "" );
	if( lineindex == 1 )
	{
		newline->next = edd->first_line;
		edd->first_line = newline;
	}
	else
	{
		num = 1;
		eline = edd->first_line;
		while( num < lineindex-1 )
		{
			eline = eline->next;
			num++;
		}
		newline->next = eline->next;
		eline->next = newline;
	}

	edd->line_count++;

	ch_printf( ch, "Inserted line at %d.\n\r", lineindex );
}

void editor_delete_line( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument )
{
	sh_int lineindex, num;
	EDITOR_LINE *prev_line, *del_line;

	if( argument[0] == '\0' || !is_number(argument) )
	{
		send_to_char( "Must supply the line number.\n\r", ch );
		return;
	}
	lineindex = atoi(argument);

	if( lineindex < 1 || lineindex > edd->line_count )
	{
		ch_printf( ch, "Line number is out of range (1-%d).\n\r", edd->line_count );
		return;
	}

	prev_line = NULL;
	if( lineindex == 1 )
	{
		if( edd->line_count == 1 )
		{
			if( edd->first_line->line[0] != '\0' )
			{
				edd->first_line->line[0] = '\0';
				edd->first_line->line_used = 0;
				edd->text_size = 0;
				send_to_char( "Deleted line 1.\n\r", ch );
			}
			else
				send_to_char( "The buffer is empty.\n\r", ch );
			return;
		}

		del_line = edd->first_line;
		edd->first_line = del_line->next;
	}
	else
	{
		num = 1;
		prev_line = edd->first_line;
		while( num < lineindex-1 )
		{
			prev_line = prev_line->next;
			num++;
		}
		del_line = prev_line->next;
		prev_line->next = del_line->next;
	}

	if( edd->on_line == del_line )
	{
		if( del_line->next )
			edd->on_line = del_line->next;
		else if( prev_line != NULL )
			edd->on_line = prev_line;
		else
			edd->on_line = edd->first_line;
	}
	edd->line_count--;
	DISPOSE(del_line->line);
	DISPOSE(del_line);

	ch_printf( ch, "Deleted line %d.\n\r", lineindex);
}

void editor_goto_line( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument )
{
	sh_int lineindex, num;

	if( argument[0] == '\0' || !is_number(argument) )
	{
		send_to_char( "Must supply the line number.\n\r", ch );
		return;
	}
	lineindex = atoi(argument);

	if( lineindex < 1 || lineindex > edd->line_count )
	{
		ch_printf( ch, "Line number is out of range (1-%d).\n\r", edd->line_count );
		return;
	}

	edd->on_line = edd->first_line;
	num = 1;
	while( num < lineindex )
	{
		edd->on_line = edd->on_line->next;
		num++;
	}

	ch_printf( ch, "On line %d.\n\r", lineindex);
}

void editor_list( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument )
{
	EDITOR_LINE *eline;
	sh_int line_num;
	sh_int from, to;
	char arg1[ MAX_INPUT_LENGTH ];

	argument = one_argument( argument, arg1 );
	if( arg1[0] != '\0' && is_number(arg1) )
		from = atoi(arg1);
	else
		from = 1;
	argument = one_argument( argument, arg1 );
	if( arg1[0] != '\0' && is_number(arg1) )
		to = atoi(arg1);
	else
		to = edd->line_count;

	send_to_pager( "------------------\n\r", ch );
	line_num = 1;
	eline = edd->first_line;
	while( eline )
	{
	    if( line_num >= from && line_num <= to )
		pager_printf( ch, "%2d>%c%s\n\r",
		    line_num,
		    eline == edd->on_line ? '*' : ' ',
		    eline->line );
	    eline = eline->next;
	    line_num++;
	}
	send_to_pager( "------------------\n\r", ch );
}

void editor_abort( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument )
{
    send_to_char( "\n\rAborting... ", ch );
    stop_editing( ch );
}

void editor_escaped_cmd( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument )
{
    if ( get_trust(ch) > LEVEL_IMMORTAL )
    {
	DO_FUN *last_cmd;
	int substate = ch->substate;

	last_cmd = ch->last_cmd;
	ch->substate = SUB_RESTRICTED;
	interpret(ch, argument);
	ch->substate = substate;
	ch->last_cmd = last_cmd;
	set_char_color( AT_GREEN, ch );
	send_to_char( "\n\r", ch );
    }
    else
	send_to_char( "You can't use '/!'.\n\r", ch );
}

void editor_save( CHAR_DATA *ch, EDITOR_DATA *edd, char *argument )
{
    DESCRIPTOR_DATA *d;

    d = ch->desc;

    d->connected = CON_PLAYING;
    if ( !ch->last_cmd )
      return;
    (*ch->last_cmd) ( ch, "" );
}

char *Justify( char *oldstring, int iAlignment )
{
  char xbuf[MAX_STRING_LENGTH*2];
  char xbuf2[MAX_STRING_LENGTH*2];
//  char xbuf[(4*MAX_STRING_LENGTH - 1000)];
//  char xbuf2[(4*MAX_STRING_LENGTH - 1000)];
  char *rdesc;
  int i = 0;
  int end_of_line;
  bool cap = TRUE;
  bool bFormat = TRUE;

  xbuf[0] = xbuf2[0] = 0;

  i = 0;

  if ( strlen( oldstring ) > MAX_STRING_LENGTH-1 )
  {
    return (oldstring);
  }

  if( oldstring[0] == '\0' )
  {
    return oldstring;
  }

  for (rdesc = oldstring; *rdesc; rdesc++)
  {
      if (bFormat)
      {
        if (*rdesc == '\n')
        {
          if (*(rdesc + 1) == '\r' && *(rdesc + 2) == ' ' && *(rdesc + 3) == '\n' && xbuf[i - 1] != '\r')
          {
            xbuf[i] = '\n';
            xbuf[i + 1] = '\r';
            xbuf[i + 2] = '\n';
            xbuf[i + 3] = '\r';
            i += 4;
            rdesc += 2;
          }
          else if (*(rdesc + 1) == '\r' && *(rdesc + 2) == ' ' && *(rdesc + 2) == '\n' && xbuf[i - 1] == '\r')
          {
            xbuf[i] = '\n';
            xbuf[i + 1] = '\r';
            i += 2;
          }
          else if (*(rdesc + 1) == '\r' && *(rdesc + 2) == '\n' && xbuf[i - 1] != '\r')
          {
            xbuf[i] = '\n';
            xbuf[i + 1] = '\r';
            xbuf[i + 2] = '\n';
            xbuf[i + 3] = '\r';
            i += 4;
            rdesc += 1;
          }
          else if (*(rdesc + 1) == '\r' && *(rdesc + 2) == '\n' && xbuf[i - 1] == '\r')
          {
            xbuf[i] = '\n';
            xbuf[i + 1] = '\r';
            i += 2;
          }
          else if (xbuf[i - 1] != ' ' && xbuf[i - 1] != '\r')
          {
            xbuf[i] = ' ';
            i++;
          }
        }
        else if (*rdesc == '\r');
        else if (*rdesc == 'i' && *(rdesc + 1) == '.' && *(rdesc + 2) == 'e' && *(rdesc + 3) == '.')
        {
          xbuf[i] = 'i';
          xbuf[i + 1] = '.';
          xbuf[i + 2] = 'e';
          xbuf[i + 3] = '.';
          i += 4;
          rdesc += 3;
        }
        else if (*rdesc == ' ')
        {
          if (xbuf[i - 1] != ' ')
          {
            xbuf[i] = ' ';
            i++;
          }
        }
        else if (*rdesc == ')')
        {
          if (xbuf[i - 1] == ' ' && xbuf[i - 2] == ' '
          && (xbuf[i - 3] == '.' || xbuf[i - 3] == '?' || xbuf[i - 3] == '!'))
          {
            xbuf[i - 2] = *rdesc;
            xbuf[i - 1] = ' ';
            xbuf[i] = ' ';
            i++;
          }
          else if (xbuf[i - 1] == ' ' && (xbuf[i - 2] == ',' || xbuf[i - 2] == ';'))
          {
            xbuf[i - 1] = *rdesc;
            xbuf[i] = ' ';
            i++;
          }
          else
          {
            xbuf[i] = *rdesc;
            i++;
          }
        }
        else if (*rdesc == ',' || *rdesc == ';')
        {
          if (xbuf[i - 1] == ' ')
          {
            xbuf[i - 1] = *rdesc;
            xbuf[i] = ' ';
            i++;
          }
          else
          {
            xbuf[i] = *rdesc;
            if (*(rdesc + 1) != '\"')
            {
              xbuf[i + 1] = ' ';
              i += 2;
            }
            else
            {
              xbuf[i + 1] = '\"';
              xbuf[i + 2] = ' ';
              i += 3;
              rdesc++;
            }
          }

        }
        else if ((*rdesc == '.' && *(rdesc + 1) == ' ')
               || *rdesc == '?' || *rdesc == '!')
        {
          if (xbuf[i - 1] == ' ' && xbuf[i - 2] == ' '
          && (xbuf[i - 3] == '.' || xbuf[i - 3] == '?' || xbuf[i - 3] == '!'))
          {
            xbuf[i - 2] = *rdesc;
            if (*(rdesc + 1) != '\"')
            {
              xbuf[i - 1] = ' ';
              xbuf[i] = ' ';
              i++;
            }
            else
            {
              xbuf[i - 1] = '\"';
              xbuf[i] = ' ';
              xbuf[i + 1] = ' ';
              i += 2;
              rdesc++;
            }
          }
          else
          {
            xbuf[i] = *rdesc;
            if (*(rdesc + 1) != '\"')
            {
              xbuf[i + 1] = ' ';
              xbuf[i + 2] = ' ';
              i += 3;
            }
            else
            {
              xbuf[i + 1] = '\"';
              xbuf[i + 2] = ' ';
              xbuf[i + 3] = ' ';
              i += 4;
              rdesc++;
            }
          }
          cap = TRUE;
        }
        else
        {
          xbuf[i] = *rdesc;
          if (cap)
          {
            cap = FALSE;
            xbuf[i] = UPPER(xbuf[i]);
          }
          i++;
        }
      }
      else
      {
        xbuf[i] = *rdesc;
        i++;
      }
  }
  xbuf[i] = 0;
  strcpy(xbuf2, xbuf);

  rdesc = xbuf2;

  xbuf[0] = 0;

  for (;;)
  {
    end_of_line = iAlignment;
    for (i = 0; i < end_of_line; i++)
    {
      if (*(rdesc + i) == '`')
      {
        end_of_line += 2;
        i++;
      }

      if (!*(rdesc + i))
        break;

      if (*(rdesc + i) == '\r')
        end_of_line = i;
    }
    if (i < end_of_line)
    {
      break;
    }
    if (*(rdesc + i - 1) != '\r')
    {
      for (i = (xbuf[0] ? (end_of_line - 1) : (end_of_line - 4)); i; i--)
      {
        if (*(rdesc + i) == ' ')
          break;
      }
      if (i)
      {
        *(rdesc + i) = 0;
        strcat(xbuf, rdesc);
        strcat(xbuf, "\n\r");
        rdesc += i + 1;
        while (*rdesc == ' ')
          rdesc++;
      }
      else
      {
        *(rdesc + (end_of_line - 2)) = 0;
        strcat(xbuf, rdesc);
        strcat(xbuf, "-\n\r");
        rdesc += end_of_line - 1;
      }
    }
    else
    {
      *(rdesc + i - 1) = 0;
      strcat(xbuf, rdesc);
      strcat(xbuf, "\r");
      rdesc += i;
      while (*rdesc == ' ')
        rdesc++;
    }
  }
  while (*(rdesc + i) && (*(rdesc + i) == ' ' ||
                          *(rdesc + i) == '\n' ||
                          *(rdesc + i) == '\r'))
    i--;
  *(rdesc + i + 1) = 0;
  strcat(xbuf, rdesc);
  if (xbuf[strlen(xbuf) - 2] != '\n')
    strcat(xbuf, "\n\r");

  return (str_dup(xbuf));
}

char *oldJustify( char *pszText, int iAlignment )
{
   static char s_szResult[4096];
   char *      pszResult = &s_szResult[0];
   char        szStore[4096];
   int         iMax;
   int         iLength = iAlignment-1;
   int         iLoop = 0;

   if ( strlen( pszText ) < 10 )
   {
      /* You may want to add your own error message routine in here */
//      strcpy( s_szResult, "BUG: Justified string cannot be less than 10 characters long." );
      return( pszText );
   }

   /* Discard all leading spaces */
//   while ( *pszText == ' ' ) pszText++;

   /* Store the character */
//   szStore[iLoop++] = *pszText++;

//   if ( szStore[iLoop-1] >= 'a' && szStore[iLoop-1] <= 'z' )
//   {
      /* Capitalize the first character if it's a letter */
//      szStore[iLoop-1] &= ~32;
//   }

   /* The first loop goes through the string, copying it into szStore.  The
    * string is formatted to remove all newlines, capitalise new sentences,
    * remove excess white spaces and ensure that full stops, commas and
    * exclaimation marks are all followed by two white spaces.
    */
   while ( *pszText )
   {
      switch ( *pszText )
      {
         default:
            /* Store the character */
            szStore[iLoop++] = *pszText++;
            break;
         case ' ':
            /* There shall only be one space between non-space characters */
            if ( *(pszText+1) != ' ' )
            {
               /* Store the character */
               szStore[iLoop++] = *pszText;
            }
            pszText++;
            break;
         case '.': case '?': case '!':
            /* Store the character */
            szStore[iLoop++] = *pszText++;
            switch ( *pszText )
            {
               default:
                  /* Sentence terminators shall be followed by two spaces */
                  szStore[iLoop++] = ' ';
                  szStore[iLoop++] = ' ';
                  /* Discard all leading spaces */
                  while ( *pszText == ' ' ) pszText++;
                  /* Store the character */
                  szStore[iLoop++] = *pszText++;
                  if ( szStore[iLoop-1] >= 'a' && szStore[iLoop-1] <= 'z' )
                  {
                     /* Capitalize if it's a letter */
                     szStore[iLoop-1] &= ~32;
                  }
                  break;
               case '.': case '?': case '!':
                  /* Multiple terminators shall not be separated by spaces */
                  break;
            }
            break;
         case ',':
            /* Store the character */
            szStore[iLoop++] = *pszText++;
            /* Discard all leading spaces */
            while ( *pszText == ' ' ) pszText++;
            /* Commas shall be followed by one space */
            szStore[iLoop++] = ' ';
            break;
         case '$':
            /* Store the character */
            szStore[iLoop++] = *pszText++;
            /* Discard all leading spaces */
            while ( *pszText == ' ' ) pszText++;
            break;
         case '\n': case '\r':
            /* Discard newlines and returns */
/*
            if (szStore[iLoop-1] != ' ')
			{
            szStore[iLoop++] = ' ';
        	}
*/
			if (*pszText == '\n')
				szStore[iLoop++] = ' ';
			if (*pszText == '\r' && szStore[iLoop] != ' ')
				szStore[iLoop++] = ' ';
            pszText++;
            break;
      }
   }

   /* Terminate the string */
   szStore[iLoop] = '\0';

   /* Initialise iMax to the size of szStore */
   iMax = strlen( szStore );

   /* The second loop goes through the string, inserting newlines at every
    * appropriate point.
    */
   while ( iLength < iMax )
   {
      /* Go backwards through the current line searching for a space */
      while ( szStore[iLength] != ' ' && iLength > 1 )
      {
         iLength--;
      }

      if ( szStore[iLength] == ' ' )
      {
         /* If a space is found, replace it with a newline */
         szStore[iLength] = '\n';
         iLength += iAlignment;
      }
      else
      {
         /* If no space is found, drop out of the loop */
         break;
      }
   }

   /* Add spaces to the front of the line as appropriate */
   /* Reset the counter */
   iLoop = 0;

   /* The third and final loop goes through the string, making sure that there
    * is a \r (return to beginning of line) following every newline, with no
    * white spaces at the beginning of a particular line of text.
    */
   while ( iLoop < iMax )
   {
      /* Store the character */
      *pszResult++ = szStore[iLoop];
      switch ( szStore[iLoop] )
      {
         default:
            break;
         case '\n':
            /* Insert a return after the newline and remove any leading spaces */
            *pszResult++ = '\r';
            while ( szStore[iLoop+1] == ' ' )
            {
            iLoop++;
            /* Add spaces to the front of the line as appropriate */
            }
            break;
      }
      iLoop++;
   }

   /* Terminate the string */
   *pszResult++ = '\0';

   return( &s_szResult[0] );
}

// New editor format taken and altered from AFKMUD.  --Saiyr
void editor_format(CHAR_DATA *ch, EDITOR_DATA *edd, char *argument )
{
	char *buf;

	buf = editdata_to_str(ch->editor);
	buf = Justify(buf, 76);
	ch->editor = str_to_editdata(buf, MAX_STRING_LENGTH);
	ch->editor->desc = edd->desc;

    send_to_char( "OK.\n\r", ch );
    return;
}




/****************************************************************************
 * Misc functions
 */

char *text_replace( char *src, char *word_src, char *word_dst, sh_int *pnew_size, sh_int *prepl_count )
/* Replaces a word word_src in src for word_dst. Returns a pointer to a newly
 * allocated buffer containing the line with the replacements. Stores in
 * pnew_size the size of the allocated buffer, wich may be different from the
 * length of the string and is a multiple of CHAR_BLOCK. Stores in prepl_count
 * the number of replacements it made */
{
	char *dst_buf;
	char *next_found, *last_found;
	sh_int dst_used, dst_size, len;
	sh_int repl_count;

	/* prepare the destination buffer */
	CREATE( dst_buf, char, CHAR_BLOCK );
	dst_size = CHAR_BLOCK;
	dst_used = 0;
	dst_buf[0] = '\0';

	last_found = src;
	repl_count = 0;
	for(;;)
	{
		/* look for next instance of word */
		next_found = strstr( last_found, word_src );
		if( next_found == NULL )
		{
			/* if theres no more instances of word,
			 * copy the rest of the src */
			len = strlen( last_found );
			RESIZE_IF_NEEDED( dst_buf, dst_size, dst_used, len+1 );
			strcat( dst_buf, last_found );
			dst_used += len;
			break;
		}
		/* copy the buffer up to this instance of the word
		 * and then copy the replacement word */
		len = next_found-last_found + strlen(word_dst);
		RESIZE_IF_NEEDED( dst_buf, dst_size, dst_used, len+1 );
		strncat( dst_buf, last_found, next_found-last_found );
		strcat( dst_buf, word_dst );
		dst_used += len;

		last_found = next_found + strlen(word_src);
		repl_count ++;
	}
	*pnew_size = dst_size;
	*prepl_count = repl_count;
	return dst_buf;
}

/*
 * Pick off one argument from a string and return the rest.
 * Understands quotes.
 * A pickier version than regular one_argument, it will not
 * convert to lowercase, and it can handle the (') character
 * when it's escaped inside '.
 */
char *finer_one_argument( char *argument, char *arg_first )
{
    char cEnd;
    sh_int count;
    bool escaped;

    count = 0;

    while ( isspace(*argument) )
	argument++;

    cEnd = ' ';
    if ( *argument == '\'' || *argument == '"' )
	cEnd = *argument++;

    escaped = FALSE;
    while ( *argument != '\0' || ++count >= MAX_INPUT_LENGTH )
    {
	if( cEnd != ' ' && escaped )
	{
		if( *argument == '\\' )
			*arg_first = '\\';
		else if( *argument == '\'' )
			*arg_first = '\'';
		else if( *argument == '"' )
			*arg_first = '"';
		else
			*arg_first = *argument;
		arg_first++;
		argument++;
		escaped = FALSE;
		continue;
	}
	if( cEnd != ' ' && *argument == '\\' && !escaped )
	{
		escaped = TRUE;
		argument++;
		continue;
	}

	if ( *argument == cEnd )
	{
	    argument++;
	    break;
	}
	*arg_first = *argument;
	arg_first++;
	argument++;
    }
    *arg_first = '\0';

    while ( isspace(*argument) )
	argument++;

    return argument;
}

