/***************************************************************************
* File: string.c *
* *
* Much time and thought has gone into this software and you are *
* benefitting. We hope that you share your changes too. What goes *
* around, comes around. *
* *
* This code was freely distributed with the The Isles 1.1 source code, *
* and has been used here for OLC - OLC would not be what it is without *
* all the previous coders who released their source code. *
* *
***************************************************************************/
#if defined(macintosh)
#include <types.h>
#else
#include <sys/types.h>
#endif
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "merc.h"
void show_line_numbers args( ( CHAR_DATA * ch, char *oldstring ) );
char *line_replace args( ( char *orig, int line, char *arg3 ) );
int count_lines args( ( const char *orig ) );
char *line_delete args( ( char *orig, int line ) );
/*
The line_replace, count_lines, and line_delete functions
were provided by Thanatos (Jonathan Rose). I wrote the new
line_add and show_line_numbers functions and modified the
string_add function. --Kyle Boyd
*/
/*****************************************************************************
Name: line_replace
Purpose: Substitutes one line of text for another.
Called by: string_add(string.c) (aedit_builder)olc_act.c.
****************************************************************************/
char *line_replace( char *orig, int line, char *arg3 )
{
unsigned int len;
int count = 0;
char *pOut, outbuf[4 * MAX_STRING_LENGTH], buf[4 * MAX_STRING_LENGTH];
bool copy = TRUE;
strcpy( buf, orig );
outbuf[0] = '\0';
pOut = outbuf;
if ( line == 1 )
{
*pOut = '\0';
strcat( outbuf, arg3 );
pOut += strlen( arg3 );
if ( *pOut - 2 != '\n' )
*pOut++ = '\n';
if ( *pOut - 1 != '\r' )
*pOut++ = '\r';
copy = FALSE;
}
for ( len = 0; len < strlen( buf ); len++ )
{
if ( buf[len] == '\r' )
{
if ( copy )
*pOut++ = '\r';
count++;
if ( count == line - 1 )
{
*pOut = '\0';
strcat( outbuf, arg3 );
pOut += strlen( arg3 );
if ( *pOut - 2 != '\n' )
*pOut++ = '\n';
if ( *pOut - 1 != '\r' )
*pOut++ = '\r';
copy = FALSE;
}
else if ( count == line )
copy = TRUE;
}
else if ( copy )
*pOut++ = buf[len];
}
*pOut = '\0';
free_string( &orig );
return str_dup( outbuf );
}
/*****************************************************************************
Name: count_lines
Purpose: counts the number of lines in the string
Called by: string_add for use in line_delete
*****************************************************************************/
int count_lines( const char *orig )
{
int line;
for ( line = 0; *orig; )
{
if ( *orig++ == '\r' )
line++;
}
return line;
}
/*****************************************************************************
Name: line_delete
Purpose: deletes one line of text
Called by: string_add(string.c) (aedit_builder)olc_act.c.
****************************************************************************/
char *line_delete( char *orig, int line )
{
int len, buflen;
int count = 0;
char *pOut, outbuf[4 * MAX_STRING_LENGTH], buf[4 * MAX_STRING_LENGTH];
strcpy( buf, orig );
buflen = strlen( buf );
outbuf[0] = '\0';
pOut = outbuf;
len = 0;
if ( line == 1 )
{
*pOut = '\0';
for ( ; buf[len] != '\r'; len++ )
continue;
len++;
}
for ( ; len < buflen; len++ )
{
if ( buf[len] == '\r' )
{
count++;
if ( count == line - 1 )
{
for ( len++; len < buflen; len++ )
{
if ( buf[len] == '\r' )
{
break;
}
}
*pOut++ = '\r';
}
else
*pOut++ = buf[len];
}
else
*pOut++ = buf[len];
}
*pOut = '\0';
free_string( &orig );
return str_dup( outbuf );
}
/*****************************************************************************
Name: line_add
Purpose: adds one line of text at line # specified
Called by: string_add(string.c) (aedit_builder)olc_act.c.
****************************************************************************/
/* The old one was buggy, at lesat for my mud */
/* So I rewrote it. -- Kyle Boyd */
char *line_add( char *orig, int line, char *add )
{
char *string, outbuf[MAX_STRING_LENGTH], buf[MAX_STRING_LENGTH];
int count = 1, pos;
strcpy( buf, orig );
string = buf;
outbuf[0] = '\0';
if ( line == 1 )
{
outbuf[0] = '\0';
strcat( outbuf, add );
strcat( outbuf, "\n\r" );
strcat( outbuf, orig );
free_string( &orig );
return str_dup( outbuf );
}
for ( pos = 0; string[pos] != '\0'; pos++ )
{
if ( string[pos] == '\n' && string[pos + 1] != '\0' )
{
if ( string[pos + 1] == '\r' && string[pos + 2] != '\0' )
{
count++;
if ( count == line )
{
strncat( outbuf, orig, pos + 2 );
strcat( outbuf, add );
strcat( outbuf, "\n\r" );
break;
}
}
}
}
for ( ; string[pos] != '\0'; pos++ )
{
if ( string[pos] == '\n' && string[pos + 1] != '\0' )
{
if ( string[pos + 1] == '\r' )
{
strcat( outbuf, &orig[pos + 2] );
free_string( &orig );
return str_dup( outbuf );
}
}
}
return orig;
}
/*****************************************************************************
Name: string_edit
Purpose: Clears string and puts player into editing mode.
Called by: none
****************************************************************************/
void string_edit( CHAR_DATA * ch, char **pString )
{
send_to_char( "-========- Entering EDIT Mode -=========-\n\r", ch );
send_to_char( " Type .h on a new line for help\n\r", ch );
send_to_char( " Terminate with a ~ or @ on a blank line.\n\r", ch );
send_to_char( "-=======================================-\n\r", ch );
if ( *pString == NULL )
{
*pString = str_dup( "" );
}
else
{
**pString = '\0';
}
ch->desc->pString = pString;
return;
}
/*****************************************************************************
Name: string_append
Purpose: Puts player into append mode for given string.
Called by: (many)olc_act.c
****************************************************************************/
void string_append( CHAR_DATA * ch, char **pString )
{
send_to_char( "-=======- Entering APPEND Mode -========-\n\r", ch );
send_to_char( " Type .h on a new line for help\n\r", ch );
send_to_char( " Terminate with a ~ or @ on a blank line.\n\r", ch );
send_to_char( "-=======================================-\n\r", ch );
if ( *pString == NULL )
{
*pString = str_dup( "" );
}
show_line_numbers( ch, *pString );
if ( *( *pString + strlen( *pString ) - 1 ) != '\r' )
send_to_char( "\n\r", ch );
ch->desc->pString = pString;
return;
}
/*****************************************************************************
Name: string_replace
Purpose: Substitutes one string for another.
Called by: string_add(string.c) (aedit_builder)olc_act.c.
****************************************************************************/
char *string_replace( char *orig, char *old, char *new )
{
char xbuf[MAX_STRING_LENGTH];
int i;
xbuf[0] = '\0';
strcpy( xbuf, orig );
if ( strstr( orig, old ) != NULL )
{
i = strlen( orig ) - strlen( strstr( orig, old ) );
xbuf[i] = '\0';
strcat( xbuf, new );
strcat( xbuf, &orig[i + strlen( old )] );
free_string( &orig );
}
if ( orig )
free_string( &orig );
return str_dup( xbuf );
}
/*****************************************************************************
Name: string_add
Purpose: Interpreter for string editing.
Called by: game_loop_xxxx(comm.c).
****************************************************************************/
void string_add( CHAR_DATA * ch, char *argument )
{
char arg[MAX_INPUT_LENGTH];
char buf[MAX_STRING_LENGTH];
/*
* Thanks to James Seng
*/
if ( *argument == '~' )
{
ch->desc->pString = NULL;
return;
}
smash_tilde( argument );
if ( *argument == '.' )
{
char arg2[MAX_INPUT_LENGTH];
char arg3[MAX_INPUT_LENGTH];
argument = one_argument( argument, arg );
argument = first_arg( argument, arg2, FALSE );
argument = first_arg( argument, arg3, FALSE );
if ( !str_cmp( arg, ".c" ) )
{
send_to_char( "String cleared.\n\r", ch );
**ch->desc->pString = '\0';
return;
}
if ( !str_cmp( arg, ".s" ) )
{
send_to_char( "String so far:\n\r", ch );
show_line_numbers( ch, *ch->desc->pString );
return;
}
if ( !str_cmp( arg, ".r" ) )
{
if ( arg2[0] == '\0' )
{
send_to_char( "usage: .r \"old string\" \"new string\"\n\r",
ch );
return;
}
smash_tilde( arg3 ); /* Just to be sure -- Hugin */
*ch->desc->pString =
string_replace( *ch->desc->pString, arg2, arg3 );
sprintf( buf, "'%s' replaced with '%s'.\n\r", arg2, arg3 );
send_to_char( buf, ch );
return;
}
if ( !str_cmp( arg, ".l" ) )
{
if ( !( *ch->desc->pString ) || !( *ch->desc->pString[0] ) )
{
send_to_char( "No lines to replace.\n\r", ch );
return;
}
if ( !is_number( arg2 ) || !arg3[0] )
{
send_to_char( "usage: .l line# 'new text'\n\r", ch );
return;
}
if ( atoi( arg2 ) < 1 )
{
send_to_char( "Line numbers start at 1.\n\r", ch );
return;
}
smash_tilde( arg3 );
*ch->desc->pString =
line_replace( *ch->desc->pString, atoi( arg2 ), arg3 );
return;
}
if ( !str_cmp( arg, ".a" ) )
{
if ( !( *ch->desc->pString ) || !( *ch->desc->pString[0] ) )
{
send_to_char( "No lines to replace.\n\r", ch );
return;
}
if ( !is_number( arg2 ) || !arg3[0] )
{
send_to_char( "usage: .a line# 'new text'\n\r", ch );
return;
}
if ( atoi( arg2 ) < 1 )
{
send_to_char( "Line numbers start at 1.\n\r", ch );
return;
}
smash_tilde( arg3 );
*ch->desc->pString =
line_add( *ch->desc->pString, atoi( arg2 ), arg3 );
return;
}
if ( !str_cmp( arg, ".d" ) )
{
int line;
if ( !( *ch->desc->pString ) || !( *ch->desc->pString[0] ) )
{
send_to_char( "No lines to delete.\n\r", ch );
return;
}
if ( arg2[0] == '\0' )
{
line = count_lines( *ch->desc->pString );
}
else
line = atoi( arg2 );
if ( line < 1 )
{
send_to_char( "Line numbers start at 1.\n\r", ch );
return;
}
smash_tilde( arg3 );
*ch->desc->pString = line_delete( *ch->desc->pString, line );
return;
}
if ( !str_cmp( arg, ".f" ) )
{
*ch->desc->pString = format_string( *ch->desc->pString );
send_to_char( "String formatted.\n\r", ch );
return;
}
if ( !str_cmp( arg, ".h" ) )
{
send_to_char( "Sedit help (commands on blank line): \n\r", ch );
send_to_char( ".r 'old' 'new' - replace a substring \n\r", ch );
send_to_char( " (requires '', \"\") \n\r", ch );
send_to_char( ".l line# 'new' - replace a line \n\r", ch );
send_to_char( ".d - delete last line \n\r", ch );
send_to_char( ".d line# - delete a line \n\r", ch );
send_to_char( ".a line# 'new' - add a new line \n\r", ch );
send_to_char( ".h - get help (this info)\n\r", ch );
send_to_char( ".s - show string so far \n\r", ch );
send_to_char( ".f - (word wrap) string \n\r", ch );
send_to_char( ".c - clear string so far \n\r", ch );
send_to_char( "@ - end string \n\r", ch );
return;
}
send_to_char( "SEdit: Invalid dot command.\n\r", ch );
return;
}
if ( *argument == '~' || *argument == '@' )
{
ch->desc->pString = NULL;
return;
}
strcpy( buf, *ch->desc->pString );
/*
* Truncate strings to MAX_STRING_LENGTH.
* --------------------------------------
*/
if ( strlen( buf ) + strlen( argument ) >= ( MAX_STRING_LENGTH - 4 ) )
{
send_to_char( "String too long, last line skipped.\n\r", ch );
/* Force character out of editing mode. */
ch->desc->pString = NULL;
return;
}
/*
* Ensure no tilde's inside string.
* --------------------------------
*/
smash_tilde( argument );
strcat( buf, argument );
strcat( buf, "\n\r" );
free_string( ch->desc->pString );
*ch->desc->pString = str_dup( buf );
return;
}
/*
* Thanks to Kalgen for the new procedure (no more bug!)
* Original wordwrap() written by Surreality.
*/
/*****************************************************************************
Name: format_string
Purpose: Special string formating and word-wrapping.
Called by: string_add(string.c) (many)olc_act.c
****************************************************************************/
char *format_string( char *oldstring /*, bool fSpace */ )
{
char xbuf[MAX_STRING_LENGTH];
char xbuf2[MAX_STRING_LENGTH];
char *rdesc;
int i = 0;
bool cap = TRUE;
xbuf[0] = xbuf2[0] = 0;
i = 0;
for ( rdesc = oldstring; *rdesc; rdesc++ )
{
if ( *rdesc == '\n' )
{
if ( xbuf[i - 1] != ' ' )
{
xbuf[i] = ' ';
i++;
}
}
else if ( *rdesc == '\r' );
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
{
xbuf[i] = *rdesc;
i++;
}
}
else if ( *rdesc == '.' || *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++;
}
}
xbuf[i] = 0;
strcpy( xbuf2, xbuf );
rdesc = xbuf2;
xbuf[0] = 0;
for ( ;; )
{
for ( i = 0; i < 77; i++ )
{
if ( !*( rdesc + i ) )
break;
}
if ( i < 77 )
{
break;
}
for ( i = ( xbuf[0] ? 76 : 73 ); 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
{
bug( "No spaces", 0 );
*( rdesc + 75 ) = 0;
strcat( xbuf, rdesc );
strcat( xbuf, "-\n\r" );
rdesc += 76;
}
}
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" );
free_string( &oldstring );
return ( str_dup( xbuf ) );
}
/*
* Used above in string_add. Because this function does not
* modify case if fCase is FALSE and because it understands
* parenthesis, it would probably make a nice replacement
* for one_argument.
*/
/*****************************************************************************
Name: first_arg
Purpose: Pick off one argument from a string and return the rest.
Understands quates, parenthesis (barring ) ('s) and
percentages.
Called by: string_add(string.c)
****************************************************************************/
char *first_arg( char *argument, char *arg_first, bool fCase )
{
char cEnd;
while ( *argument == ' ' )
argument++;
cEnd = ' ';
if ( *argument == '\'' || *argument == '"'
|| *argument == '%' || *argument == '(' )
{
if ( *argument == '(' )
{
cEnd = ')';
argument++;
}
else
cEnd = *argument++;
}
while ( *argument != '\0' )
{
if ( *argument == cEnd )
{
argument++;
break;
}
if ( fCase )
*arg_first = LOWER( *argument );
else
*arg_first = *argument;
arg_first++;
argument++;
}
*arg_first = '\0';
while ( *argument == ' ' )
argument++;
return argument;
}
/*
* Used in olc_act.c for aedit_builders.
*/
char *string_unpad( char *argument )
{
char *s;
char *result;
char *tmp;
s = argument;
while ( *s == ' ' )
s++;
result = s;
tmp = s;
if ( *s != '\0' )
{
while ( *s != '\0' )
if ( *s++ != ' ' )
tmp = s - 1;
*tmp = '\0';
}
free_string( &argument );
return str_dup( result );
}
/*
* Same as capitalize but changes the pointer's data.
* Used in olc_act.c in aedit_builder.
*/
char *string_proper( char *argument )
{
char *s;
s = argument;
while ( *s != '\0' )
{
if ( *s != ' ' )
{
*s = UPPER( *s );
while ( *s != ' ' && *s != '\0' )
s++;
}
else
{
s++;
}
}
return argument;
}
/* This functions by Kyle Boyd. */
/* If you see any bugs, check to see if all of your strings end like that. */
void show_line_numbers( CHAR_DATA * ch, char *string )
{
char *ptr;
char newstring[MAX_STRING_LENGTH];
int line;
if ( !*string )
{
send_to_char( "\n\r", ch );
return;
}
ptr = newstring;
*ptr = '\0';
sprintf( ptr, " 1 " );
ptr += 3;
for ( line = 2; *string; string++ )
{
if ( *string == '\n' || *string == '\r' )
{
/*
* Allow for both \n\r and \r\n and \n. Don't expect two chars
* because you could condense two rows down to one in the case of
* \n\n
*/
if ( ( *string == '\n' && *( string + 1 ) == '\r' )
|| ( *string == '\r' && *( string + 1 ) == '\n' ) )
string += 2;
else
string++;
if ( *string == '\0' )
break;
sprintf( ptr, "\n\r%2d ", line++ );
ptr += 5;
}
*ptr++ = *string;
}
sprintf( ptr, "\n\r" );
send_to_char( newstring, ch );
return;
}