empiremud/cnf/
empiremud/doc/
empiremud/lib/boards/
empiremud/lib/etc/
empiremud/lib/misc/
empiremud/lib/plralias/F-J/
empiremud/lib/plralias/K-O/
empiremud/lib/plralias/P-T/
empiremud/lib/plralias/U-Z/
empiremud/lib/plrobjs/
empiremud/lib/plrobjs/F-J/
empiremud/lib/plrobjs/K-O/
empiremud/lib/plrobjs/P-T/
empiremud/lib/plrobjs/U-Z/
empiremud/lib/world/
empiremud/lib/world/mob/
empiremud/lib/world/obj/
empiremud/log/
/* ************************************************************************
*   File: modify.c                                       EmpireMUD AD 1.0 *
*  Usage: Run-time modification of game variables                         *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Code base by Paul Clarke.  EmpireMUD Project, a tbgMUD Production.     *
*  Based upon CircleMUD 3.0, beta patch level 17, by Jeremy Elson.        *
*                                                                         *
*  Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
************************************************************************ */

#include "conf.h"
#include "sysdep.h"

#include "structs.h"
#include "utils.h"
#include "interpreter.h"
#include "handler.h"
#include "db.h"
#include "comm.h"
#include "mail.h"
#include "boards.h"


/*
 * Action modes for parse_action().
 */
#define PARSE_FORMAT		0
#define PARSE_REPLACE		1
#define PARSE_HELP			2
#define PARSE_DELETE		3
#define PARSE_INSERT		4
#define PARSE_LIST_NORM		5
#define PARSE_LIST_NUM		6
#define PARSE_EDIT			7

/*
 * Defines for the action variable.
 */
#define STRINGADD_OK		0	/* Just keep adding text.				*/
#define STRINGADD_SAVE		1	/* Save current text.					*/
#define STRINGADD_ABORT		2	/* Abort edit, restore old text.		*/
#define STRINGADD_ACTION	4	/* Editor action, don't append \r\n.	*/

#define FORMAT_INDENT	(1 << 0)


void show_string(Descr d, char *input);
extern const char *MENU;

/* local functions */
void smash_tilde(char *str);
char *next_page(char *str);
int count_pages(char *str);
void paginate_string(char *str, Descr d);


/* ************************************************************************
*  modification of malloc'ed strings                                      *
************************************************************************ */

/*
 * Put '#if 1' here to erase ~, or roll your own method.  A common idea
 * is smash/show tilde to convert the tilde to another innocuous character
 * to save and then back to display it. Whatever you do, at least keep the
 * function around because other MUD packages use it, like mudFTP.
 *   -gg 9/9/98
 */
void smash_tilde(char *str) {
#if 0
	/*
	 * Erase any ~'s inserted by people in the editor.  This prevents anyone
	 * using online creation from causing parse errors in the world files.
	 * Derived from an idea by Sammy <samedi@dhc.net> (who happens to like
	 * his tildes thank you very much.), -gg 2/20/98
	 */
	while ((str = strchr(str, '~')) != NULL)
		*str = ' ';
#endif
	}


/*
 * Basic API function to start writing somewhere.
 *
 * 'data' isn't used in stock EmpireMUD but you can use it to pass whatever
 * else you may want through it.  The improved editor patch when updated
 * could use it to pass the old text buffer, for instance.
 */
void string_write(Descr d, char **writeto, size_t len, long mailto, void *data) {
	if (d->character && !IS_NPC(d->character))
		SET_BIT(PLR_FLAGS(d->character), PLR_WRITING);

	d->backstr = (char *)data;

	d->str = writeto;
	d->max_str = len;
	d->mail_to = mailto;
	}


/* Add user input to the 'current' string (as defined by d->str) */
void string_add(Descr d, char *str) {
	extern char *stripcr(char *dest, const char *src);
	FILE *fl;
	extern int improved_editor_execute(Descr d, char *str);
	int action;

	delete_doubledollar(str);
	smash_tilde(str);

	if ((action = improved_editor_execute(d, str)) == STRINGADD_ACTION)
		return;

	if (!(*d->str)) {
		if (strlen(str) + 3 > d->max_str) {
			send_to_char("String too long - Truncated.\r\n", d->character);
			strcpy(&str[d->max_str - 3], "\r\n");
			CREATE(*d->str, char, d->max_str);
			strcpy(*d->str, str);
			}
		else {
			CREATE(*d->str, char, strlen(str) + 3);
			strcpy(*d->str, str);
			}
		}
	else {
		if (strlen(str) + strlen(*d->str) + 3 > d->max_str)
			send_to_char("String too long.  Last line skipped.\r\n", d->character);
		else {
			RECREATE(*d->str, char, strlen(*d->str) + strlen(str) + 3);
			strcat(*d->str, str);
			}
		}

	switch (action) {
		case STRINGADD_ABORT:
			if (STATE(d) == CON_TEDIT || STATE(d) == CON_EDIT_DESCRIPTION) {
				free(*d->str);
				*d->str = d->backstr;
				d->backstr = NULL;
				d->str = NULL;
				}
			break;
		case STRINGADD_SAVE:
			if (d->str && *d->str && **d->str == '\0') {
				free(d->str);
				*d->str = str_dup("Nothing.\r\n");
				}
			if (d->backstr)
				free(d->backstr);
			d->backstr = NULL;
			break;
		}

	if (action) {
		if (STATE(d) == CON_PLAYING) {
			if (PLR_FLAGGED(d->character, PLR_MAILING)) {
				if (action == STRINGADD_SAVE && *d->str) {
					store_mail(d->mail_to, GET_IDNUM(d->character), *d->str);
					SEND_TO_Q("Message sent!\r\n", d);
					}
				else
					SEND_TO_Q("Mail aborted.\r\n", d);
				free(*d->str);
				free(d->str);
				}
			if (d->mail_to >= BOARD_MAGIC) {
				Board_save_board(d->mail_to - BOARD_MAGIC);
				if (action == STRINGADD_ABORT)
					SEND_TO_Q("Post not aborted, use REMOVE <post #>.\r\n", d);
				}
			}
		else if (STATE(d) == CON_TEDIT || STATE(d) == CON_EDIT_DESCRIPTION) {
			if (action != STRINGADD_ABORT) {
				if (d->storage) {
					if ((fl = fopen((char *)d->storage, "w"))){
						if (*d->str)
							fputs(stripcr(buf1, *d->str), fl);
						fclose(fl);

						if (STATE(d) == CON_TEDIT) {
							syslog(GET_INVIS_LEV(d->character), TRUE, "GC: %s saves '%s'.", GET_NAME(d->character), d->storage);
							SEND_TO_Q("Saved.\r\n", d);
							}
						else if (STATE(d) == CON_EDIT_DESCRIPTION)
							SEND_TO_Q("Description saved.\r\n", d);
						}
					}
				}
			else	
				SEND_TO_Q("Edit aborted.\r\n", d);
			if (STATE(d) == CON_TEDIT)
				act("$n stops editing a file.", TRUE, d->character, 0, 0, TO_ROOM);
			else if (STATE(d) == CON_EDIT_DESCRIPTION)
				act("$n stops editing the description.", TRUE, d->character, 0, 0, TO_ROOM);
			free(d->storage);
			d->storage = NULL;
			STATE(d) = CON_PLAYING;
			}

		d->str = NULL;
		d->mail_to = 0;
		d->max_str = 0;
		if (d->character && !IS_NPC(d->character))
			REMOVE_BIT(PLR_FLAGS(d->character), PLR_WRITING | PLR_MAILING);
		}
	else if (strlen(*d->str) <= (d->max_str-3))
		strcat(*d->str, "\r\n");
	}


/*********************************************************************
* New Pagination Code
* Michael Buselli submitted the following code for an enhanced pager
* for CircleMUD.  All functions below are his.  --JE 8 Mar 96
*********************************************************************/

#define PAGE_LENGTH     22
#define PAGE_WIDTH      80

/* Traverse down the string until the begining of the next page has been
 * reached.  Return NULL if this is the last page of the string.
 */
char *next_page(char *str) {
	int col = 1, line = 1, spec_code = FALSE;

	for (;; str++) {
		/* If end of string, return NULL. */
		if (*str == '\0')
			return (NULL);

		/* If we're at the start of the next page, return this fact. */
		else if (line > PAGE_LENGTH)
			return (str);

		/* Check for the begining of an ANSI color code block. */
		else if (*str == '\x1B' && !spec_code)
			spec_code = TRUE;

		/* Check for the end of an ANSI color code block. */
		else if (*str == 'm' && spec_code)
			spec_code = FALSE;

		/* Check for everything else. */
		else if (!spec_code) {
			/* Carriage return puts us in column one. */
			if (*str == '\r')
				col = 1;
			/* Newline puts us on the next line. */
			else if (*str == '\n')
				line++;

			/*
			 * We need to check here and see if we are over the page width,
			 * and if so, compensate by going to the begining of the next line.
			 */
			else if (col++ > PAGE_WIDTH) {
				col = 1;
				line++;
				}
			}
		}
	}


/* Function that returns the number of pages in the string. */
int count_pages(char *str) {
	int pages;

	for (pages = 1; (str = next_page(str)); pages++);
	return (pages);
	}


/*
 * This function assigns all the pointers for showstr_vector for the
 * page_string function, after showstr_vector has been allocated and
 * showstr_count set.
 */
void paginate_string(char *str, Descr d) {
	int i;

	if (d->showstr_count)
		*(d->showstr_vector) = str;

	for (i = 1; i < d->showstr_count && str; i++)
		str = d->showstr_vector[i] = next_page(str);

	d->showstr_page = 0;
	}


/* The call that gets the paging ball rolling... */
void page_string(Descr d, char *str, int keep_internal) {
	if (!d)
		return;

	if (!str || !*str)
		return;

	if (d->character && PRF_FLAGGED(d->character, PRF_SCROLLING)) {
		msg_to_char(d->character, str);
		return;
		}

	d->showstr_count = count_pages(str);
	CREATE(d->showstr_vector, char *, d->showstr_count);

	if (keep_internal) {
		d->showstr_head = str_dup(str);
		paginate_string(d->showstr_head, d);
		}
	else
		paginate_string(str, d);

	show_string(d, "");
	}


/* The call that displays the next page. */
void show_string(Descr d, char *input) {
	char buffer[MAX_STRING_LENGTH];
	int diff;

	any_one_arg(input, buf);

	/* Q is for quit. :) */
	if (LOWER(*buf) == 'q') {
		free(d->showstr_vector);
		d->showstr_count = 0;
		if (d->showstr_head) {
			free(d->showstr_head);
			d->showstr_head = NULL;
			}
		return;
		}
	/*
	 * R is for refresh, so back up one page internally so we can display
	 * it again.
	 */
	else if (LOWER(*buf) == 'r')
		d->showstr_page = MAX(0, d->showstr_page - 1);

	/*
	 * B is for back, so back up two pages internally so we can display the
	 * correct page here.
	 */
	else if (LOWER(*buf) == 'b')
		d->showstr_page = MAX(0, d->showstr_page - 2);

	/*
	 * Feature to 'goto' a page.  Just type the number of the page and you
	 * are there!
	 */
	else if (isdigit(*buf))
		d->showstr_page = MAX(0, MIN(atoi(buf) - 1, d->showstr_count - 1));

	else if (*buf) {
		send_to_char("Valid commands while paging are RETURN, Q, R, B, or a numeric value.\r\n", d->character);
		return;
		}
	/*
	 * If we're displaying the last page, just send it to the character, and
	 * then free up the space we used.
	 */
	if (d->showstr_page + 1 >= d->showstr_count) {
		send_to_char(d->showstr_vector[d->showstr_page], d->character);
		free(d->showstr_vector);
		d->showstr_count = 0;
		if (d->showstr_head) {
			free(d->showstr_head);
			d->showstr_head = NULL;
			}
		}
	/* Or if we have more to show.... */
	else {
		diff = d->showstr_vector[d->showstr_page + 1] - d->showstr_vector[d->showstr_page];
		if (diff >= MAX_STRING_LENGTH)
			diff = MAX_STRING_LENGTH - 1;
		strncpy(buffer, d->showstr_vector[d->showstr_page], diff);
		buffer[diff] = '\0';
		send_to_char(buffer, d->character);
		d->showstr_page++;
		}
	}


/* End of code by Michael Buselli */


/************************************************************************
* The following modify code was shipped with OasisOLC.  Here are the    *
* credits from the patch I borrowed it from.                            *
* OasisOLC                                                         v2.0 *
* Original author: Levork                                               *
* Copyright 1996 by Harvey Gilpin                                       *
* Copyright 1997-1999 by George Greer (greerga@circlemud.org)           *
************************************************************************/


int improved_editor_execute(Descr d, char *str) {
	void parse_action(int command, char *string, Descr d);
	char actions[MAX_INPUT_LENGTH];

	if (*str != '/')
		return STRINGADD_OK;

	strncpy(actions, str + 2, sizeof(actions) - 1);
	actions[sizeof(actions)-1] = '\0';
	*str = '\0';

	switch(str[1]) {
		case 'a':
			return STRINGADD_ABORT;
		case 'c':
			if (*(d->str)) {
				free(*d->str);
				*(d->str) = NULL;
				SEND_TO_Q("Current buffer cleared.\r\n", d);
				}
			else
				SEND_TO_Q("Current buffer empty.\r\n", d);
			break;
		case 'd':
			parse_action(PARSE_DELETE, actions, d);
			break;
		case 'e':
			parse_action(PARSE_EDIT, actions, d);
			break;
		case 'f':
			if (*(d->str))
				parse_action(PARSE_FORMAT, actions, d);
			else
				SEND_TO_Q("Current buffer empty.\r\n", d);
			break;
		case 'i':
			if (*(d->str))
				parse_action(PARSE_INSERT, actions, d);
			else
				SEND_TO_Q("Current buffer empty.\r\n", d);
			break;
		case 'h':
			parse_action(PARSE_HELP, actions, d);
			break;
		case 'l':
			if (*d->str)
				parse_action(PARSE_LIST_NORM, actions, d);
			else
				SEND_TO_Q("Current buffer empty.\r\n", d);
			break;
		case 'n':
			if (*d->str)
				parse_action(PARSE_LIST_NUM, actions, d);
			else
				SEND_TO_Q("Current buffer empty.\r\n", d);
			break;
		case 'r':
			parse_action(PARSE_REPLACE, actions, d);
			break;
		case 's':
			return STRINGADD_SAVE;
		default:
			SEND_TO_Q("Invalid editor option.\r\n", d);
			break;
		}
	return STRINGADD_ACTION;
	}


void parse_action(int command, char *string, Descr d) {
	void format_text(char **ptr_string, int mode, Descr d, unsigned int maxlen);
	extern int replace_str(char **string, char *pattern, char *replacement, int rep_all, unsigned int max_size);
	int indent = 0, rep_all = 0, flags = 0, replaced, i, line_low, line_high, j = 0;
	unsigned int total_len;
	char *s, *t, temp;

	switch (command) {
		case PARSE_HELP:
			sprintf(buf,
				"Editor command formats: /<letter>\r\n"
				"/a         - aborts editor without saving\r\n"
				"/c         - clears buffer\r\n"
				"/d#        - deletes a line\r\n"
				"/e# <text> - changes line # to <text>\r\n"
				"/f         - formats text\r\n"
				"/fi        - formats text and indents\r\n"
				"/i# <text> - inserts <text> before line #\r\n"
				"/l         - lists the buffer\r\n"
				"/n         - lists the buffer with line numbers\r\n"
				"/r 'a' 'b' - replaces 1st occurance of <a> with <b>\r\n"
				"/ra 'a' 'b'- replaces all occurances of <a> with <b>\r\n"
				"             usage: /r[a] 'pattern' 'replacement'\r\n"
				"/s         - saves the buffer and exits\r\n");
			SEND_TO_Q(buf, d);
			break;
		case PARSE_FORMAT:
			while (isalpha(string[j]) && j < 2)
				if (string[j++] == 'i' && !indent) {
					indent = TRUE;
					flags += FORMAT_INDENT;
					}
			format_text(d->str, flags, d, d->max_str);
			sprintf(buf, "Text formatted with%s indent.\r\n", indent ? "" : "out");
			SEND_TO_Q(buf, d);
			break;
		case PARSE_REPLACE:
			while (isalpha(string[j]) && j < 2)
				if (string[j++] == 'a' && !indent)
					rep_all = 1;
			if ((s = strtok(string, "'")) == NULL) {
				SEND_TO_Q("Invalid format.\r\n", d);
				return;
				}
			else if ((s = strtok(NULL, "'")) == NULL) {
				SEND_TO_Q("Target string must be enclosed in single quotes.\r\n", d);
				return;
				}
			else if ((t = strtok(NULL, "'")) == NULL) {
				SEND_TO_Q("Replacement string must be enclosed in single quotes.\r\n", d);
				return;
				}
			else if ((total_len = ((strlen(t) - strlen(s)) + strlen(*d->str))) <= d->max_str) {
				if ((replaced = replace_str(d->str, s, t, rep_all, d->max_str)) > 0) {
					sprintf(buf, "Replaced %d occurance%sof '%s' with '%s'.\r\n", replaced, replaced != 1 ? "s " : " ", s, t);
					SEND_TO_Q(buf, d);
					}
				else if (replaced == 0) {
					 sprintf(buf, "String '%s' not found.\r\n", s);
					 SEND_TO_Q(buf, d);
					 }
				else
					SEND_TO_Q("ERROR: Replacement string causes buffer overflow, aborted replace.\r\n", d);
				}
			else
				SEND_TO_Q("Not enough space left in buffer.\r\n", d);
			break;
		case PARSE_DELETE:
			switch (sscanf(string, " %d - %d ", &line_low, &line_high)) {
				case 0:
					SEND_TO_Q("You must specify a line number or range to delete.\r\n", d);
					return;
				case 1:
					line_high = line_low;
					break;
				case 2:
					if (line_high < line_low) {
						SEND_TO_Q("That range is invalid.\r\n", d);
						return;
						}
					break;
				}
			i = 1;
			total_len = 1;
			if ((s = *d->str) == NULL) {
				SEND_TO_Q("Buffer is empty.\r\n", d);
				return;
				}
			else if (line_low > 0) {
				while (s && i < line_low)
					if ((s = strchr(s, '\n')) != NULL) {
						i++;
						s++;
						}
				if (s == NULL || i < line_low) {
					SEND_TO_Q("Line(s) out of range; not deleting.\r\n", d);
					return;
					}
				t = s;
				while (s && i < line_high)
					if ((s = strchr(s, '\n')) != NULL) {
						i++;
						total_len++;
						s++;
						}
				if (s && (s = strchr(s, '\n')) != NULL) {
					while (*(++s))
						*(t++) = *s;
					}
				else
					total_len--;
				*t = '\0';
				RECREATE(*d->str, char, strlen(*d->str) + 3);

				sprintf(buf, "%d line%sdeleted.\r\n", total_len, total_len != 1 ? "s " : " ");
				SEND_TO_Q(buf, d);
				}
			else {
				SEND_TO_Q("Invalid, line numbers to delete must be higher than 0.\r\n", d);
				return;
				}
			break;
		case PARSE_LIST_NORM:
			*buf = '\0';
			if (*string)
				switch(sscanf(string, " %d - %d ", &line_low, &line_high)) {
					case 0:
						line_low = 1;
						line_high = 999999;
						break;
					case 1:
						line_high = line_low;
						break;
					}
			else {
				line_low = 1;
				line_high = 999999;
				}

			if (line_low < 1) {
				SEND_TO_Q("Line numbers must be greater than 0.\r\n", d);
				return;
				}
			else if (line_high < line_low) {
				SEND_TO_Q("That range is invalid.\r\n", d);
				return;
				}
			*buf = '\0';
			if (line_high < 999999 || line_low > 1)
				sprintf(buf, "Current buffer range [%d - %d]:\r\n", line_low, line_high);
			i = 1;
			total_len = 0;
			s = *d->str;
			while (s && i < line_low)
				if ((s = strchr(s, '\n')) != NULL) {
					i++;
					s++;
					}
			if (i < line_low || s == NULL) {
				SEND_TO_Q("Line(s) out of range; no buffer listing.\r\n", d);
				return;
				}
			t = s;
			while (s && i <= line_high)
				if ((s = strchr(s, '\n')) != NULL) {
					i++;
					total_len++;
					s++;
					}
			if (s) {
				temp = *s;
				*s = '\0';
				strcat(buf, t);
				*s = temp;
				}
			else
				strcat(buf, t);
			sprintf(buf + strlen(buf), "\r\n%d line%sshown.\r\n", total_len, total_len != 1 ? "s " : " ");
			page_string(d, buf, TRUE);
			break;
		case PARSE_LIST_NUM:
			*buf = '\0';
			if (*string)
				switch (scanf(string, " %d - %d ", &line_low, &line_high)) {
					case 0:
						line_low = 1;
						line_high = 999999;
						break;
					case 1:
						line_high = line_low;
						break;
					}
			else {
				line_low = 1;
				line_high = 999999;
				}

			if (line_low < 1) {
				SEND_TO_Q("Line numbers must be greater than 0.\r\n", d);
				return;
				}
			if (line_high < line_low) {
				SEND_TO_Q("That range is invalid.\r\n", d);
				return;
				}
			*buf = '\0';
			i = 1;
			total_len = 0;
			s = *d->str;
			while (s && i < line_low)
				if ((s = strchr(s, '\n')) != NULL) {
					i++;
					s++;
					}
			if (i < line_low || s == NULL) {
				SEND_TO_Q("Line(s) out of range; no buffer listing.\r\n", d);
				return;
				}
			t = s;
			while (s && i <= line_high)
				if ((s = strchr(s, '\n')) != NULL) {
					i++;
					total_len++;
					s++;
					temp = *s;
					*s = '\0';
					sprintf(buf, "%s%4d:\r\n", buf, (i-1));
					strcat(buf, t);
					*s = temp;
					t = s;
					}
			if (s && t) {
				temp = *s;
				*s = '\0';
				strcat(buf, t);
				*s = temp;
				}
			else if (t)
				strcat(buf, t);

			page_string(d, buf, TRUE);
			break;
		case PARSE_INSERT:
			half_chop(string, buf, buf2);
			if (*buf == '\0') {
				SEND_TO_Q("You must specify a line number before which to insert text.\r\n", d);
				return;
				}
			line_low = atoi(buf);
			strcat(buf2, "\r\n");

			i = 1;
			*buf = '\0';
			if ((s = *d->str) == NULL) {
				SEND_TO_Q("Buffer is empty, nowhere to insert.\r\n", d);
				return;
				}
			if (line_low > 0) {
				while (s && i < line_low)
					if ((s = strchr(s, '\n')) != NULL) {
						i++;
						s++;
						}
				temp = *s;
				*s = '\0';
				if ((strlen(*d->str) + strlen(buf2) + strlen(s + 1) + 3) > d->max_str) {
					*s = temp;
					SEND_TO_Q("Insert text pushes buffer over maximum size, insert aborted.\r\n", d);
					return;
					}
				if (*d->str && **d->str)
					strcat(buf, *d->str);
				*s = temp;
				strcat(buf, buf2);
				if (s && *s)
					strcat(buf, s);
				RECREATE(*d->str, char, strlen(buf) + 3);

				strcpy(*d->str, buf);
				SEND_TO_Q("Line instered.\r\n", d);
				}
			else {
				SEND_TO_Q("LIne number must be higher than 0.\r\n", d);
				return;
				}
			break;
		case PARSE_EDIT:
			half_chop(string, buf, buf2);
			if (*buf == '\0') {
				SEND_TO_Q("You must specify a line number at which to tchange text.\r\n", d);
				return;
				}
			line_low = atoi(buf);
			strcat(buf2, "\r\n");

			i = 1;
			*buf = '\0';
			if ((s = *d->str) == NULL) {
				SEND_TO_Q("Buffer is empty, nothing to change.\r\n", d);
				return;
				}
			if (line_low > 0) {
				while (s && i < line_low)
					if ((s = strchr(s, '\n')) != NULL) {
						i++;
						s++;
						}
				if (s == NULL || i < line_low) {
					SEND_TO_Q("Line number out of range; change aborted.\r\n", d);
					return;
					}
				if (s != *d->str) {
					temp = *s;
					*s = '\0';
					strcat(buf, *d->str);
					*s = temp;
					}
				strcat(buf, buf2);
				if ((s = strchr(s, '\n')) != NULL) {
					s++;
					strcat(buf, s);
					}
				if (strlen(buf) > d->max_str) {
					SEND_TO_Q("Change causes new length to exceed buffer maximum size, aborted.\r\n", d);
					return;
					}
				RECREATE(*d->str, char, strlen(buf) + 3);
				strcpy(*d->str, buf);
				SEND_TO_Q("Line changed.\r\n", d);
				}
			else {
				SEND_TO_Q("Line number must be higher than 0.\r\n", d);
				return;
				}
			break;
		default:
			SEND_TO_Q("Invalid option.\r\n", d);
			syslog(0, TRUE, "SYSERR: invalid command passed to parse_action");
			return;
		}
	}


void format_text(char **ptr_string, int mode, Descr d, unsigned int maxlen) {
	int line_chars, cap_next = TRUE, cap_next_next = FALSE;
	char *flow, *start = NULL, temp;
	char formatted[MAX_STRING_LENGTH];

	if (d->max_str > MAX_STRING_LENGTH) {
		log("SYSERR: format_text: max_str is greater than buffer size.");
		return;
		}

	if ((flow = *ptr_string) == NULL)
		return;

	if (IS_SET(mode, FORMAT_INDENT)) {
		strcpy(formatted, "   ");
		line_chars = 3;
		}
	else {
		*formatted = '\0';
		line_chars = 0;
		}

	while (*flow) {
		while (*flow && strchr("\n\r\f\t\v ", *flow))
			flow++;

		if (*flow) {
			start = flow++;
			while (*flow && !strchr("\n\r\f\t\v .?!", *flow))
				flow++;
			if (cap_next_next) {
				cap_next_next = FALSE;
				cap_next = TRUE;
				}
			while (strchr(".!?", *flow)) {
				cap_next_next = TRUE;
				flow++;
				}
			temp = *flow;
			*flow = '\0';

			if (line_chars + strlen(start) + 1 > 79) {
				strcat(formatted, "\r\n");
				line_chars = 0;
				}
			if (!cap_next) {
				if (line_chars > 0) {
					strcat(formatted, " ");
					line_chars++;
					}
				}
			else {
				cap_next = FALSE;
				*start = UPPER(*start);
				}
			line_chars += strlen(start);
			strcat(formatted, start);
			*flow = temp;
			}
		if (cap_next_next) {
			if (line_chars + 3 > 79) {
				strcat(formatted, "\r\n");
				line_chars = 0;
				}
			else {
				strcat(formatted, "  ");
				line_chars += 2;
				}
			}
		}

	strcat(formatted, "\r\n");

	if (strlen(formatted) + 1 > maxlen)
		formatted[maxlen-1] = '\0';
	RECREATE(*ptr_string, char, MIN(maxlen, strlen(formatted) + 1));
	strcpy(*ptr_string, formatted);
	}


int replace_str(char **string, char *pattern, char *replacement, int rep_all, unsigned int max_size) {
	char *replace_buffer = NULL;
	char *flow, *jetsam, temp;
	int len, i;

	if ((strlen(*string) - strlen(pattern)) + strlen(replacement) > max_size)
		return -1;

	CREATE(replace_buffer, char, max_size);
	i = 0;
	jetsam = *string;
	flow = *string;
	*replace_buffer = '\0';

	if (rep_all) {
		while ((flow = (char *)strstr(flow, pattern)) != NULL) {
			i++;
			temp = *flow;
			*flow = '\0';
			if ((strlen(replace_buffer) + strlen(jetsam) + strlen(replacement)) > max_size) {
				i = -1;
				break;
				}
			strcat(replace_buffer, jetsam);
			strcat(replace_buffer, replacement);
			*flow = temp;
			flow += strlen(pattern);
			jetsam = flow;
			}
		strcat(replace_buffer, jetsam);
		}
	else {
		if ((flow = (char *)strstr(*string, pattern)) != NULL) {
			i++;
			flow += strlen(pattern);
			len = ((char *)flow - (char *)*string) - strlen(pattern);
			strncpy(replace_buffer, *string, len);
			strcat(replace_buffer, replacement);
			strcat(replace_buffer, flow);
			}
		}

	if (i <= 0)
		return 0;
	else {
		RECREATE(*string, char, strlen(replace_buffer) + 3);
		strcpy(*string, replace_buffer);
		}
	free(replace_buffer);
	return i;
	}