dawn/notes/
dawn/src/
dawn/src/docs/
/**************************************************************************/
// string.cpp - string related functions
/***************************************************************************
 * The Dawn of Time v1.69r (c)1997-2004 Michael Garratt                    *
 * >> A number of people have contributed to the Dawn codebase, with the   *
 *    majority of code written by Michael Garratt - www.dawnoftime.org     *
 * >> To use this source code, you must fully comply with the dawn license *
 *    in licenses.txt... In particular, you may not remove this copyright  *
 *    notice.                                                              *
 **************************************************************************/
/***************************************************************************
 *  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.                *
 ***************************************************************************/
#include "include.h" // dawn standard includes
#include "interp.h" 
#include "olc.h" 
#include "nanny.h" 
#include "help.h" 

char *string_linedel( char *, int );
char *string_lineadd( char *, char *, int );
char *number_lines( char_data* ch, char * );

/*****************************************************************************
 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 )
{
    ch->println("-=======- Entering APPEND Mode -========-");
    ch->println("    Type .h on a new line for help");
    ch->println(" Terminate with a ~ or @ on a blank line.");
    ch->println("-=======================================-");

    if ( *pString == NULL )
    {
        *pString = str_dup( "" );
    }


	// Find dawnftp/mudftp connection if they have one
	bool using_dawnftp=false;
	if(TRUE_CH(ch)->pcdata && TRUE_CH(ch)->pcdata->preference_dawnftp==PREF_AUTOSENSE){
		for (connection_data *m = connection_list; m; m=m->next)  {
			if (m->connected_state == CON_FTP_COMMAND &&
				m->ftp.mode == FTP_PUSH &&
				!str_cmp(m->username, TRUE_CH(ch)->name))
			{
				using_dawnftp=true;
				break;
			}
		}
	}else if (TRUE_CH(ch)->pcdata->preference_dawnftp==PREF_ON){
		using_dawnftp=true; // perm on
	}

	if (using_dawnftp)
	{
        ch->desc->pString = pString;
        if (ftp_push(ch->desc)){ // ftp: PUSH mode 
            ch->println("Editing string via DawnFTP/mudFTP push connection.  Use ~ or @ to abort.");
			ch->desc->ftp.inuse=true;
			return;
		}
		
		{ // try PULL mode, since PUSH mode didnt' work and we have the connection forced on
            ch->printf("Sending DawnFTP/mudFTP request. If your client does not support DawnFTP/mudFTP, abort this\n"
                       "edit (type ~ or @ on a blank line), toggle dawnftp off, and try again.\n"
                       "\ntmp/%lu%c\n", *((unsigned long*) pString), 230);
			return;
        }
    }

	if (str_len(*pString)>MSL-4){
		char buf2[MSL*2];
		strncpy(buf2, *pString, MSL-4);
		buf2[MSL-4]='\0';

		ch->printf("The text you are trying to edit is longer "
			"than %d characters!\r\n", MSL-4);
		bug("string_append: Text too long! - trimmed version will be shown");
		log_string(buf2);
		ch->print(buf2);
		ch->printlnf("\r\n\r\n`RThe text you are trying to edit is longer "
			"than %d characters!`x", MSL-4);
		ch->wrapln("`YSorry, the text will have to be trimmed to a "
			"smaller size before it can be worked on.`x");
		ch->desc->pString = NULL;
	}else{
		ch->print( number_lines(ch, *pString));
		ch->print("`x");
		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 * newstr )
{
    char xbuf[HSL];
    int i;

    xbuf[0] = '\0';
    strcpy( xbuf, orig );
    if ( strstr( orig, old ) != NULL )
    {
        i = (int)(str_len( orig ) - str_len( strstr( orig, old ) ));
        xbuf[i] = '\0';
        strcat( xbuf, newstr );
        strcat( xbuf, &orig[i+str_len( old )] );
        free_string( orig );
    }

    return str_dup( xbuf );
}


/**************************************************************************/
// Kal
// assumes orig is an already str_duped string
char * string_replace_all( char * orig, char * old, char * newstr )
{
	if(!strstr( orig, old ) || !str_cmp(old, newstr)){
		return orig;
	}
	
	while(strstr( orig, old )){
		orig=string_replace(orig,old,newstr);
	}
	return orig;
}
/**************************************************************************/
// getline_by_number returns the line specified by the number, 
// caller needs to free it
char *getline_by_number( char *string, int linenum);
/**************************************************************************/

/*****************************************************************************
 Name:		string_add
 Purpose:	Interpreter for string editing.
 Called by:	game_loop_xxxx(comm.c).
 ****************************************************************************/
void save_bans(void);

void string_add( char_data *ch, char *argument )
{
    char buf[HSL];

    smash_tilde( argument );

    if(ch->desc && ch->desc->ftp.inuse && str_cmp(argument, "@"))
	{
        ch->println("Type @  to manually abort FTP mode.\r\n"
			"If DawnFTP/mudFTP is not supported by your client, abort this edit and turn dawnftp off.");
        return;
	}

    if ( *argument == '.' )
    {
        char arg1 [MIL];
        char arg2 [MIL];
        char arg3 [MIL];
        char tmparg3 [MIL];
        char text [MIL];

        argument = one_argument( argument, arg1 );
		sprintf(text, argument);
        argument = first_arg( argument, arg2, false );
		strcpy( tmparg3, argument );
        argument = first_arg( argument, arg3, false );

        if ( !str_cmp( arg1, ".a" ) )
        {
			do_answer(ch, text);
            return;
        }
        if ( !str_cmp( arg1, ".o" ) )
        {
			do_ooc(ch, text);
            return;
        }
		if ( !str_cmp( arg1, ".q" ) )
        {
			do_question(ch, text);
            return;
        }
		if ( !str_cmp( arg1, ".:" ))
        {
			interpret(ch, text);
            return;
        }
/*        if ( !str_cmp( arg1, ".:" )  )
        {
			do_immtalk(ch, text);
            return;
        }
*/
        if ( !str_cmp( arg1, ".c" ) )
        {
            ch->println("String cleared.");
			free_string(*ch->desc->pString);
			*ch->desc->pString = str_dup( "" );
            return;
        }

// .i <word> - spellcheck a single word
// .i        - spellcheck the whole string

#ifdef unix
		if ( !str_cmp( arg1, ".i" ) )
		{
			if (!IS_NULLSTR(arg2))
				do_ispell(ch, arg2);
			else
				ispell_string(ch);
			return;
		}
#endif

        if ( !str_cmp( arg1, ".s" ) )
        {
			// decide if to put line numbers on the string
			if (str_infix("`1",*ch->desc->pString)){
	            ch->println("String so far:");
				ch->print( number_lines(ch, *ch->desc->pString));
				ch->print("`x");
			}else{
	            ch->println("String so far: (has ``1 codes in it, use .b to see line numbering)");
				ch->print( *ch->desc->pString);
			}
            return;
        }

        if ( !str_cmp( arg1, ".b" ) )
        {
			ch->println( "String so far:" );
			ch->printbw( number_lines(ch, *ch->desc->pString));
			ch->print( "`x" );
            return;
        }

        if ( !str_cmp( arg1, ".r" ) )
        {
            if ( arg2[0] == '\0' )
            {
                ch->println("usage:  .r \"old string\" \"new string\"");
                return;
            }

			if (!str_infix(arg2 , *ch->desc->pString)){
				// check the lengths
				if ( str_len( *ch->desc->pString) + str_len( arg3 ) - str_len( arg2 )>= ( MSL - 50 ) )
				{
					ch->println("You can't replace that much, the resulting string would be too long.");
					return;
				}
				*ch->desc->pString =
					string_replace( *ch->desc->pString, arg2, arg3 );
                sprintf( buf, "'%s' replaced with '%s'.", arg2, arg3 );
				ch->printlnbw( buf );
			}else{
                sprintf( buf, "Couldn't find '%s' in string.", arg2);
				ch->printlnbw( buf );
			}
            return;
        }

        if ( IS_IMMORTAL(ch) && !str_cmp( arg1, ".z" ) )
        {
			int count=0;

			// hide all ``1 codes from the replace of `1 codes
			while(!str_infix("``1", *ch->desc->pString)){
				*ch->desc->pString =
					string_replace( *ch->desc->pString, "``1", "!@#$%%^%$#@!");
			}		
			while(!str_infix("`1", *ch->desc->pString)){
				*ch->desc->pString =
                    string_replace( *ch->desc->pString, "`1", "`+\n");
				count++;
			}
			// return all original ``1 codes 
            while(!str_infix("!@#$%%^%$#@!", *ch->desc->pString)){
				*ch->desc->pString =
					string_replace( *ch->desc->pString, "!@#$%%^%$#@!", "``1");
			}

			if(count==0){
                sprintf( buf, "Couldn't find any `1 code in string to mass replace.");
				ch->printlnbw( buf );
			}else{
                ch->printlnf("Replaced %d ``1 symbols with ``+\\n",count);
			}
            return;
        }


        if ( !str_cmp( arg1, ".n" ) || !str_cmp( arg1, ".f" ) )
        {
            *ch->desc->pString = note_format_string( *ch->desc->pString );
            ch->println("String noteformatted.");
            return;
        }
        
        if ( !str_cmp( arg1, ".w" ) )
        {
            *ch->desc->pString = format_string( *ch->desc->pString );
            ch->println("String formatted.");
            return;
        }

        if (!str_cmp(arg1,".-") || !str_cmp(arg1,".d"))
        {
            size_t len;
            bool found = false;  
    
            if (ch->desc->pString == NULL || *ch->desc->pString[0] == '\0')
            {
                ch->println("No lines left to remove.");
                return;
            }
    
            strcpy(buf,*ch->desc->pString);
    
            for (len = str_len(buf); len > 0; len--)
            {
                if (buf[len] == '\n')
                {
                    if (!found)  // back it up 
                    {
                        if (len > 0)
                            len--;
                        found = true;
                    }
                    else // found the second one 
                    {
                        buf[len + 1] = '\0';
                        free_string(*ch->desc->pString);
                        *ch->desc->pString = str_dup(buf);
                        ch->println("Bottom line removed.");
                        return;
                    }
                }
            }
            buf[0] = '\0';
            free_string(*ch->desc->pString);
            *ch->desc->pString = str_dup(buf);
            ch->println("Bottom line removed.");
            return;
        }

	if ( !str_cmp( arg1, ".ld" ) )
	{
		*ch->desc->pString = string_linedel( *ch->desc->pString, atoi(arg2) );
        ch->printlnf("Line %d deleted.", atoi(arg2) );
		return;
	}

	if ( !str_cmp( arg1, ".li" ) )
	{
		*ch->desc->pString = string_lineadd( *ch->desc->pString, tmparg3, atoi(arg2) );
        ch->printlnf("Line '%s' inserted above line %d.", tmparg3, atoi(arg2));
		return;
	}

	// line swap - Kal - Nov 99
	if ( !str_cmp( arg1, ".ls" ) )
	{
		char *first, *second;
		int line1=atoi(arg2);
		int line2=atoi(arg3);

		// validate the numbers
		if(line1<1 || line2<1){
			ch->printf("Line Swap (.ls) requires 2 numbers, both greater than 0.\r\n"
				"(e.g. '.ls 2 5' would swap lines 2 and 5.)\r\n");
			return;
		}
		first=getline_by_number( *ch->desc->pString, line1);
		if(IS_NULLSTR(first)){
			ch->printlnf("Line %d not found!", line2);		
			free_string(first);
			return;
		}
		second=getline_by_number( *ch->desc->pString, line2);
		if(IS_NULLSTR(second)){
			ch->printlnf("Line %d not found!", line2);		
			free_string(first);
			free_string(second);
			return;
		}

		*ch->desc->pString = string_linedel( *ch->desc->pString, line1 );
		*ch->desc->pString = string_lineadd( *ch->desc->pString, second, line1 );
		*ch->desc->pString = string_linedel( *ch->desc->pString, line2 );
		*ch->desc->pString = string_lineadd( *ch->desc->pString, first, line2 );
        ch->printlnf("Line %d swapped with line %d.", line1, line2);
		free_string(first);
		free_string(second);
		return;
	}


	if ( !str_cmp( arg1, ".lr" ) )
	{
		*ch->desc->pString = string_linedel( *ch->desc->pString, atoi(arg2) );
		*ch->desc->pString = string_lineadd( *ch->desc->pString, tmparg3, atoi(arg2) );
        ch->printlnf("Line %d replaced with '%s'.", atoi(arg2), tmparg3);
		return;
	}


        if ( !str_cmp( arg1, ".h" ) )
        {
            ch->println("`xSedit help (commands on blank line):   ");
            ch->println(".r 'old' 'new'      - replace a substring ");
            ch->println("                      (requires '', \"\") ");
			if ( IS_IMMORTAL(ch)){
				ch->println(".z                  - convert ``1 format to ``+ format");
			}
			ch->println(".h                  - get help (this info)");
            ch->println(".s                  - show string so far  ");
            ch->println(".b                  - show string bare with line numbers");
			ch->println("                      and with colour codes");
            ch->println(".n                  - noteformat string (wordwrap but");
			ch->println("                         dont put spaces after fullstops)");
			ch->println(".f                  - noteformat string");
            ch->println(".w                  - wordwrap string (changing spacing)");
            ch->println(".c                  - clear string so far ");
			ch->println(".i                  - spell check string so far ");
            ch->println(".d                  - delete the bottom line");
            ch->println(".-                  - delete the bottom line");
            ch->println(".ld <num>           - delete the line numbered <num>");
            ch->println(".li <num> <str>     - insert <str> before line <num>");
            ch->println(".lr <num> <str>     - replace line <num> with <str>");
            ch->println(".ls <num1> <num2>   - swap lines <num1> and <num2>");
            ch->println("@                   - end string          ");
            ch->println("-External commands-");
            ch->println(".a <text to reply>  - answer a question");
            ch->println(".o <text to ooc>    - ask a question");
            ch->println(".q <text to ask>    - ask a question");
			if (IS_IMMORTAL(ch))
			{
                ch->println(".: or : process external command");           
			}
            
            return;
        }         

        ch->println("SEdit:  Invalid dot command.");
        return;
    }

    if ( *argument == '~' || *argument == '@' )
    {
		// all changed flags system, enables some olc editor to mark
		// something as changed when they come out of the editor
		if(ch->desc->changed_flag){
			SET_BIT(*ch->desc->changed_flag,A); 
			ch->desc->changed_flag=NULL;
		}
		if ( ch->desc->editor == ED_BAN) // banedit 
		{
			save_bans();
		}else if ( ch->desc->editor == ED_HELP ){ // hedit
			help_data *pHelp;
			EDIT_HELP(ch, pHelp);
			get_widest_line_stats(pHelp->text, true, &pHelp->widest_line, &pHelp->widest_line_width);
			if(pHelp->widest_line_width>78){
				ch->printlnf("`RWARNING: Line text is %d characters wide.`x", pHelp->widest_line_width);
			}
		}else if ( ch->desc->editor == ED_MPCODE ){ // mob progs
			MOB_INDEX_DATA *mob;
			int hash;
			MPROG_LIST *mpl;
			MPROG_CODE *mpc;

			EDIT_MPCODE(ch, mpc);

			if ( mpc != NULL )
			{
				AREA_DATA *pArea;

				pArea=get_vnum_area( mpc->vnum);
				if (pArea)
				{
					SET_BIT( pArea->olc_flags, OLCAREA_CHANGED );
				}

				// display which mobs was affected by the updating of code 
				// - not really necessary but message is cool :)
				for ( hash = 0; hash < MAX_KEY_HASH; hash++ )
				{
					for ( mob = mob_index_hash[hash]; mob; mob = mob->next )
					{
						for ( mpl = mob->mprogs; mpl; mpl = mpl->next )
						{
							if ( mpl->prog == mpc)
							{
                                ch->printlnf("Updated mob %d %s(%s).", 
									mob->vnum, mob->short_descr, mob->player_name);
								continue;
							}
						}
					}
				}
			}
		}

        ch->desc->pString = NULL;
		ch->desc->ftp.inuse=false;
        return;
    }

	strcpy( buf, *ch->desc->pString );

    /*
     * Truncate strings to MSL.
     * --------------------------------------
     */
    if ( str_len( buf ) + str_len( argument ) >= ( MSL - 4 ) )
    {
        ch->println("String too long, last line skipped.");

	/* 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, "\r\n" );
    free_string( *ch->desc->pString );
    *ch->desc->pString = str_dup( buf );
    return;
}



/* Second half of format_string was rewritten by kalahn May 98, 
 * Now wordwraps correct with ` colorcode sequences.
 */
/*
 * 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[HSL];
    char xbuf2[HSL];
	char *rdesc;
	int i=0;
	bool cap=true;
	
	xbuf[0]=xbuf2[0]=0;
	
	i=0;
	
	for (rdesc = oldstring; *rdesc; rdesc++)
	{
		if (*rdesc=='\r')
		{
			if (xbuf[i-1] != ' ')
			{
				if(!(i>1 && xbuf[i-1]== '+' && xbuf[i-2]== '`')){
					xbuf[i]=' ';
					i++;
				}
			}
		}
		else if (*rdesc=='\n') ;
		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++;
		}
	}
	rdesc=xbuf2;
	xbuf[i]=0;
	strcpy(xbuf2,xbuf);
	
	xbuf[0]=0;
	
	
	/*
	*  Code above here removes all the lines and caps the correct words.
	*  below here puts in lines
	*/
	// code below here was written by Kalahn May 98
	// next wordwrap the string so no line is no greater 
	// than 77 visible characters
	
	// rdesc is a pointer to the start of the string
	// get the formatted version of rdesc into xbuf
	{
		int i=0; // index
		int vischars=0; // visible characters on the current line
		int last_space=0;
		char *point;
		int width=77;
		bool more_string= true;
		
		point=xbuf; // target buffer
		bool inside_tag=false;
		
		while(more_string)
		{
			while (vischars<width && *(rdesc+i)!='\0')
			{
				*point= *(rdesc+i); // copy the character

				{
					// this section of code enables note_format_string_width()
					// to wrap strings with embeded MXP tags correctly 
					// - Kal, Jan 04
					if (*(point)==MXP_BEGIN_TAG){
						inside_tag=true;					
					}
					if (*(point)==MXP_END_TAG){
						inside_tag=false;
						vischars--;
					}
					if(inside_tag){
						// when inside a tag, just keep copying till we reach the end
						i++;
						*(++point)='\0';
						continue;
					}
				}

				vischars++;
				
				// record the last space location.
				if (*(point)==' ')
				{
					last_space=i;
				}
				
				// calculate the effects of colour codes
				if (*(point)=='`')
				{
					i++;
					point++;
					*point= *(rdesc+i); // copy the next character
					switch (*point)
					{
					default: // most codes count as none
						vischars--;
						break;
						
					case '1': // newline system reset the counters
						vischars=0;
						rdesc+=i;
						i=-1;
						point--;
						last_space=-1;
						break;
						
					case '+': // inline paragraph system `+  
						point++;
						*point= '\n';
						vischars=0;
						rdesc+=i+1;
						i=-1;
						last_space=-1;
						break;

					case 'N': // N - counts as the length of the name
						vischars+=str_len(game_settings->gamename)-1;
						break;
					
					case '-': // creates ~ counts as one
					case '`': // creates ` counts as one
						break;
					}
					i++;
					point++;
					continue;
				}
				i++;
				*(++point)='\0';
			}
			
			// all tags should be terminated, otherwise someone has
			// a bug somewhere... if so close the tag anyway
			if(inside_tag){
				bugf("note_format_string_width(): mxp/html tag not closed!");
				*(point)=MXP_END_TAG;
				point++;
			}
			
			// end of string or adding a new line
			if (vischars<width) 
			{
				*point='\0';
				more_string=false;
			}
			else
			{
				if (last_space>-1)
				{
					last_space++;
					point-= (i-last_space);
                    *point++='\r';
                    *point++='\n';
					i=last_space;
				}
				else // line to long
				{
					logf("noteformatstring: line too long. '%s'", rdesc);
					*point++='-';
                    *point++='\r';
                    *point++='\n';
				}
				
				// setup for next time thru loop
				vischars=0;
				rdesc+=i;
				i=0;
				last_space=-1;
			}
		}
        *point++='\r';
        *point++='\n';
		*point='\0';
	}
	
	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 force_lowercase)
{
    char cEnd;

    while ( is_space(*argument) )
	argument++;

    cEnd = ' ';
    if ( *argument == '\'' || *argument == '"'
      || *argument == '%'  || *argument == '(' )
    {
        if ( *argument == '(' )
        {
            cEnd = ')';
            argument++;
        }
        else cEnd = *argument++;
    }

    while ( *argument != '\0' )
    {
        if( (is_space(cEnd) && is_space(*argument)) || *argument== cEnd ){
            argument++;
            break;
        }
		if ( force_lowercase) *arg_first = LOWER(*argument);
		else *arg_first = *argument;
		arg_first++;
		argument++;
    }
    *arg_first = '\0';

    while ( is_space(*argument) ){
		argument++;
	}

    return argument;
}


/**************************************************************************/
/*
 * 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;
}

/**************************************************************************/
char *string_linedel( char *string, int line )
{
	char *strtmp = string;
    char buf[HSL];
	int cnt = 1, tmp = 0;

	buf[0] = '\0';

	for ( ; *strtmp != '\0'; strtmp++ )
	{
		if ( cnt != line )
			buf[tmp++] = *strtmp;

		if ( *strtmp == '\n' )
		{
			if ( *(strtmp + 1) == '\r' )
			{
				if ( cnt != line )
					buf[tmp++] = *(++strtmp);
				else
					++strtmp;
			}

			cnt++;
		}
	}

	buf[tmp] = '\0';

	free_string(string);
	return str_dup(buf);
}

/**************************************************************************/
char *string_lineadd( char *string, char *newstr, int line )
{
	char *strtmp = string;
	int cnt = 1, tmp = 0;
	bool done = false;
    char buf[HSL];

	buf[0] = '\0';

	for ( ; *strtmp != '\0' || (!done && cnt == line); strtmp++ )
	{
		if ( cnt == line && !done )
		{
			strcat( buf, newstr );
			strcat( buf, "\r\n" );
			tmp += str_len(newstr) + 2;
			cnt++;
			done = true;
		}

		buf[tmp++] = *strtmp;

		if ( done && *strtmp == '\0' )
			break;

		if ( *strtmp == '\n' )
		{
			if ( *(strtmp + 1) == '\r' )
				buf[tmp++] = *(++strtmp);

			cnt++;
		}

		buf[tmp] = '\0';
	}

	free_string(string);
	return str_dup(buf);
}

/**************************************************************************/
// copy up till a \n from str into buf
// return the position of str after the line has been put into buf
char *getline( char *str, char *buf, int bufsize )
{
	int tmp = 0;
	bool found = false;
	bufsize--; 

	while ( *str && tmp<bufsize){
		if ( *str == '\n' )
		{
			found = true;
			break;
		}
		if(*(str)=='\r'){// dont want \r codes returned at all
			str++;
		}else{
			buf[tmp++] = *(str++);
		}
	}

	// buffer bounds check
	if(tmp>=bufsize){
		boundsbug("getline(): bufsize=%d - insufficient buffer space allocated.", bufsize);
	}

	if ( found )
	{
		// skip over line codes
		if ( *(str + 1) == '\r' ){
			str += 2;
		}else{
			str += 1;
		}
	} 

	buf[tmp] = '\0';

	return str;
}

/**************************************************************************/
// return the lines in a string numbered on the right hand side
// (.s in the string editor)
char *number_lines( char_data *ch, char *string )
{
	int cnt = 1;
	static char buf[MSL*4];
	char buf2[MSL*4], tmpb[MSL*4];
	char *string_start=string; 

	buf[0] = '\0';

	while ( *string )
	{
		string = getline( string, tmpb, MSL*4 );

		if(HAS_MXP(ch)){
			sprintf( buf2, "%s. %s\n", 
				mxp_create_send(ch,
					FORMATF("@|.ld %d\" hint=\"exit editor|delete line %d", cnt, cnt)
					,FORMATF("%2d", cnt)) // text seen on screen underlined
			, tmpb );
			cnt++;
		}else{
			sprintf( buf2, "%2d. %s\n", cnt++, tmpb );
		}

		// bounds check
		if(str_len(buf) + str_len(buf2)> MSL*4){
			boundsbug("number_lines(): insufficient buffer space allocated "
				"to display string '%200.200s'.", string_start);
		}
		strcat( buf, buf2 );
	}

	return buf;
}

/**************************************************************************/
// returns the line specified by the number, caller needs to free it
// Kal - Nov 99
char *getline_by_number( char *string, int linenum)
{
    char buf[HSL];

	int cnt = 0;
	if(linenum<1){
		return str_dup("");
	}

	buf[0] = '\0';

	while ( !IS_NULLSTR(string) )
	{
		string = getline( string, buf, HSL);
		if(++cnt==linenum){
			// return our string without \r and ~ codes (removed by fix_string)
			// note: getline doesn't returns up to the first \n 
			return str_dup(fix_string(buf)); 
			break;
		}
	}

	return str_dup("");
}
/**************************************************************************/
// add whitespace around the sides
// remove the name
// remove any double white spaces, and trim the sides
char *string_remove_name(char *str, char *name)
{
	name=lowercase(name);
	// pad with whitespace .. we only work in lowercase
	char *padded=str_dup(lowercase(FORMATF(" %s ", str)));
	free_string(str);

	padded=string_replace_all(padded, FORMATF(" %s ", name), " "); // remove the name
	padded=string_replace_all(padded, "  ", " "); // remove double spaces
	str=str_dup(trim_string(padded)); // trim the sides
	free_string(padded);
	return str;
}
/**************************************************************************/
/**************************************************************************/