/*___________________________________________________________________________* )()( DalekenMUD 1.12 (C) 2000, 2001, 2002 )()( `][' by Martin Thomson, Lee Brooks, `][' || Ken Herbert and David Jacques || || ----------------------------------------------------------------- || || Envy Diku Mud improvements copyright (C) 1994 by Michael Quan, || || David Love, Guilherme 'Willie' Arnold, and Mitchell Tse. || || Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael || || Chastain, Michael Quan, and Mitchell Tse. || || Original Diku Mud copyright (C) 1990, 1991 || || by Sebastian Hammer, Michael Seifert, Hans Henrik St{rfeldt, || || Tom Madsen, and Katja Nyboe. || || ----------------------------------------------------------------- || || Any use of this software must follow the licenses of the || || creators. 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. || || ----------------------------------------------------------------- || || string.c || || As distributed with OLC, based on The Isles 1.1 source code. || || The colour interpreter based on that distributed with CircleMUD. || *_/<>\_________________________________________________________________/<>\_*/ #include <stdarg.h> #if defined( unix ) # include <unistd.h> # include <sys/wait.h> #endif #include "mud.h" #include "regexp.h" #if defined( HAVE_VSNPRINTF ) int vsnprintf args( ( char *str, size_t n, const char *format, va_list ap ) ); #endif #if defined( HAVE_ISPELL ) void ispell_string args( ( CHAR_DATA *ch, const char *str ) ); #endif #define BUILDER_XP 17 /* 17 xp per 100 characters */ /* * Colour codes. */ int is_colour args( ( char code ) ); /* * Normal. */ #define CNRM "\x1B[0;0m" /* * Normal Colours. */ #define CBLK "\x1B[0;30m" #define CRED "\x1B[0;31m" #define CGRN "\x1B[0;32m" #define CYEL "\x1B[0;33m" #define CBLU "\x1B[0;34m" #define CMAG "\x1B[0;35m" #define CCYN "\x1B[0;36m" #define CWHT "\x1B[0;37m" #define CNUL "" /* * Bolds. */ #define BRED "\x1B[1;31m" #define BGRN "\x1B[1;32m" #define BYEL "\x1B[1;33m" #define BBLU "\x1B[1;34m" #define BMAG "\x1B[1;35m" #define BCYN "\x1B[1;36m" #define BWHT "\x1B[1;37m" #define BBLK "\x1B[1;30m" /* * Backgrounds */ #define BKRED "\x1B[41m" #define BKGRN "\x1B[42m" #define BKYEL "\x1B[43m" #define BKBLU "\x1B[44m" #define BKMAG "\x1B[45m" #define BKCYN "\x1B[46m" #define BKWHT "\x1B[47m" #define BKBLK "\x1B[40m" /* * Special codes. */ #define CUDL "\x1B[4m" /* Underline ANSI code */ #define CFSH "\x1B[5m" /* Flashing ANSI code. Change to "" if * you want to disable flashing colour codes */ #define CRVS "\x1B[7m" /* Reverse video ANSI code */ /* * Special characters. */ #define CAMP "&" #define CSLH "\\" #define CTIL "~" #define CCR "\n" #define BELL "\a" #define BARLN "|" #define MAX_COLOURS 34 const char *COLOURLIST[] = { CNRM, CRED, CGRN, CYEL, CBLU, CMAG, CCYN, CWHT, CBLK, BRED, BGRN, BYEL, BBLU, BMAG, BCYN, BWHT, BBLK, BKRED, BKGRN, BKYEL, BKBLU, BKMAG, BKCYN, BKWHT, BKBLK, CFSH, CRVS, CUDL, CAMP, CSLH, CTIL, CCR, BELL, BARLN }; /***************************************************************************** Name: string_edit Purpose: Puts player into append mode for given string. Called by: (many)olc_act.c ****************************************************************************/ void string_edit( CHAR_DATA *ch, char **pString ) { int len; send_to_char( " &w--<[====]>--<[ &bString Editor&w ]>--<[====]>--\n\r" " [ &g Type &r.h&g on a new line for help. &w ]\n\r" " [ &r@&g or &r~&g on a blank line to finish.&w ]\n\r" " --<[===================================]>--&n\n\r", ch ); if( *pString == NULL ) { *pString = str_dup( "" ); } send_to_char( *pString, ch ); len = strlen( *pString ); if( *( *pString + len - 1 ) != '\r' ) send_to_char( "\n\r", ch ); if( len > 0 && ch->class == CLASS_BUILDER && *pString != ch->description && ( !ch->pcdata->note || *pString != ch->pcdata->note->text ) ) { char buf[ 256 ]; sprintf( buf, "&YYou lose %d.%2.2d experience points.&n\n\r", ( BUILDER_XP * len ) / 100, ( BUILDER_XP * len ) % 100 ); send_to_char( buf, ch ); gain_exp( ch, -1 * BUILDER_XP * len ); } 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). ****************************************************************************/ int string_replace( char **orig, const char *old, const char *new, bool fAll ) { char xbuf[MAX_STRING_LENGTH * 3]; char temp, *substr, *start; int count = 0; xbuf[0] = '\0'; start = substr = *orig; if( fAll ) { while( ( substr = strstr( start, old ) ) ) { count++; temp = *substr; *substr = '\0'; if( strlen( xbuf ) + strlen( new ) + strlen( start ) >= MAX_STRING_LENGTH * 3 - 4 ) { count = -1; break; } strcat( xbuf, start ); strcat( xbuf, new ); *substr = temp; substr += strlen( old ); start = substr; } strcat( xbuf, start ); } else { if( ( substr = strstr( start, old ) ) ) { if( strlen( start ) + strlen( new ) + strlen( start ) >= MAX_STRING_LENGTH * 3 - 4 ) return -1; count++; temp = *substr; *substr = '\0'; strcat( xbuf, start ); strcat( xbuf, new ); strcat( xbuf, substr + strlen( old ) ); *substr = temp; } } if( count > 0 ) { free_string( *orig ); *orig = str_dup( xbuf ); } return count; } /***************************************************************************** Name: regex_replace Purpose: Substitutes one string for another. Called by: string_add (string.c) aedit_builder (olc_act.c). ****************************************************************************/ int regex_replace( char **orig, char *pat, char *new, bool fAll ) { int count = 0; regexp *preg; int len; char *origpt = *orig; char frombuf[MAX_STRING_LENGTH]; char *frompt; char tobuf[MAX_STRING_LENGTH]; char *topt = &tobuf[0]; int space = MAX_STRING_LENGTH; if( strlen( *orig ) > MAX_STRING_LENGTH - 4 ) { regerror( "String too long" ); return -1; } if( !( preg = regcomp( pat, 1 ) ) ) return -1; for( ;; ) { len = strchr( origpt, '\n' ) - origpt; if( len < 0 ) break; strncpy( frombuf, origpt, len ); frombuf[len] = '\0'; origpt += len + 2; /* to the next line. */ frompt = &frombuf[0]; if( regexec( preg, frompt ) ) do { int diff; char *old; count++; /* * Copy leading text. */ diff = preg->startp[0] - frompt; if( ( space -= diff ) < 0 ) return -1; strncpy( topt, frompt, diff ); topt += diff; /* Substitution */ old = topt; topt = regsub( preg, new, topt, space ); if( !topt || ( space -= topt - old ) < 0 ) /* amylaar */ return -1; if( frompt == preg->endp[0]) { /* amylaar : prevent infinite loop */ if( !*frompt ) break; if( --space < 0 ) { regerror( "Ran out of space" ); return -1; } *topt++ = *frompt++; } else frompt = preg->endp[0]; } while( fAll && !preg->reganch && regexec( preg, frompt ) ); /* * Add the end of the line. */ if( ( space -= strlen( frompt ) + 1 ) < 0 ) { regerror( "Ran out of space" ); return -1; } strcpy( topt, frompt ); topt += strlen( frompt ); *topt++ = '\n'; *topt++ = '\r'; } if( count ) { free_string( *orig ); *orig = str_dup( tobuf ); } free( preg ); return count; } /* OLC 1.1b */ /***************************************************************************** Name: string_add Purpose: Interpreter for string editing. Called by: game_loop_xxxx (comm.c) . ****************************************************************************/ void string_add( CHAR_DATA *ch, const char *argument ) { char buf[MAX_STRING_LENGTH]; if( *argument == '@' || *argument == '~' ) { if( ch->class == CLASS_BUILDER && *ch->desc->pString != ch->description && ( !ch->pcdata->note || *ch->desc->pString != ch->pcdata->note->text ) ) { sprintf( buf, "&YYou receive %d.%2.2d experience points.&n\n\r", ( BUILDER_XP * strlen( *ch->desc->pString ) ) / 100, ( BUILDER_XP * strlen( *ch->desc->pString ) ) % 100 ); send_to_char( buf, ch ); gain_exp( ch, BUILDER_XP * strlen( *ch->desc->pString ) ); } ch->desc->pString = NULL; return; } if( *argument == '.' ) { char arg1[MAX_INPUT_LENGTH]; argument = first_arg( argument, arg1, FALSE ); switch( arg1[1] ) { case 'c': case 'C': send_to_char( "String cleared.\n\r", ch ); free_string( *ch->desc->pString ); *ch->desc->pString = str_dup( "" ); return; case 'e': case 'E': send_to_char( "External Command:\n\r", ch ); interpret( ch, argument ); return; case 'f': case 'F': *ch->desc->pString = format_string( *ch->desc->pString ); send_to_char( "String formatted.\n\r", ch ); return; case 'h': case 'H': send_to_char( "StrEdit help (commands on blank line):\n\r" ".c - clear string so far \n\r" ".d<xnum> - delete line number <xnum>\n\r" ".e <command> - external command\n\r" ".f - format (word wrap) string\n\r" ".h - get help (this info)\n\r" ".i<num> <str> - insert string at line <num>\n\r" ".r 'old' 'new' - replace a substring\n\r" ".ra 'old' 'new' - replace ALL substrings\n\r" ".rr 'pat' 'str' - replace using regular expressions\n\r" " (requires '' or \"\",\n\r" " use $^ for end of line)\n\r" ".s[n] - show string so far\n\r" ".l[n] - show with line numbers\n\r" " (.sn or .ln shows raw colour codes)\n\r" #if defined( HAVE_ISPELL ) ".p - ispell\n\r" #endif "@ - end string\n\r", ch ); return; case 's': case 'S': { char buf[MAX_STRING_LENGTH]; char *p; int i; if( arg1[2] != 'n' && arg1[2] != 'N' ) { send_to_char( "String so far:\n\r", ch ); send_to_char( *ch->desc->pString, ch ); return; } i = 0; p = *ch->desc->pString; while( *p && i < MAX_STRING_LENGTH ) { if( *p == '&' ) buf[i++] = '&'; buf[i++] = *p++; } buf[i] = '\0'; send_to_char( buf, ch ); return; } case 'i': case 'I': { int num; char *p1, chsave; char buf[MAX_STRING_LENGTH]; char buf1[MAX_STRING_LENGTH]; if( !is_number( &arg1[2] ) ) { send_to_char( "Usage: .i<linenum> <string>\n\r", ch ); return; } num = atoi( &arg1[2] ); strcpy( buf, *ch->desc->pString ); for( p1 = &buf[0]; p1 && *p1 && --num > 0; ) p1 = strchr( p1 + 1, '\n' ); if( !p1 || !*p1 ) { send_to_char( "The string isn't that long.\n\r", ch ); return; } if( p1 != &buf[0] ) { if( *p1 == '\n' ) p1++; if( *p1 == '\r' ) p1++; } chsave = *p1; *p1 = '\0'; strcpy( buf1, buf ); *p1 = chsave; strcat( buf1, argument ); strcat( buf1, "\n\r" ); strcat( buf1, p1 ); free_string( *ch->desc->pString ); *ch->desc->pString = str_dup( buf1 ); send_to_char( "Line Inserted.\n\r", ch ); return; } case 'd': case 'D': { int num; char *p1, *p2; char buf[MAX_STRING_LENGTH * 2]; if( !is_number( &arg1[2] ) ) { send_to_char( "Usage: .d<xnumber>\n\r", ch ); return; } num = atoi( &arg1[2] ); if( num < 0 ) { send_to_char( ".d<xnumber> : Try using a positive number.\n\r", ch ); return; } strcpy( buf, *ch->desc->pString ); for( p1 = &buf[0]; p1 && *p1 && --num > 0; ) p1 = strchr( p1 + 1, '\n' ); if( !p1 || !*p1 ) { send_to_char( "The string isn't that long.\n\r", ch ); return; } if( p1 != &buf[0] ) { if( *p1 == '\n' ) p1++; if( *p1 == '\r' ) p1++; } p2 = strchr( p1, '\n' ); if( !p2 ) p2 = strchr( buf, '\0' ); if( *p2 == '\n' ) p2++; if( *p2 == '\r' ) p2++; strcpy( p1, p2 ); free_string( *ch->desc->pString ); *ch->desc->pString = str_dup( buf ); send_to_char( "Line deleted.\n\r", ch ); return; } case 'l': case 'L': { char buf1[MAX_STRING_LENGTH * 2]; char *p; char *i; int line = 1; send_to_char( "String so far:\n\r", ch ); strcpy( buf1, "1 : " ); i = strchr( buf1, '\0' ); for( p = *ch->desc->pString; p && *p; p++ ) { if( *p == '\r' ) { /* ignore */ } else if( *p == '\n' ) { *i++ = '\n'; *i++ = '\r'; line++; sprintf( i, "%-3d: ", line ); i = strchr( i, '\0' ); } else if( *p == '&' && ( arg1[2] == 'n' || arg1[2] == 'N' ) ) { *i++ = '&'; *i++ = '&'; } else *i++ = *p; } i = strrchr( buf1, '\r' ); if( i == NULL ) strcpy( buf1, "\n\r" ); else strcpy( i, "\r&n" ); send_to_char( buf1, ch ); return; } case 'r': case 'R': { bool fAll = FALSE; bool fRegex = FALSE; int replaced; int i; char *str; char arg2[MAX_INPUT_LENGTH]; char arg3[MAX_INPUT_LENGTH]; argument = first_arg( argument, arg2, FALSE ); first_arg( argument, arg3, FALSE ); if( arg2[0] == '\0' ) { send_to_char( "usage: .r[a][r] \"old string\" \"new string\"\n\r", ch ); return; } for( i = 2; i < 5 && arg1[i] != '\0'; ++i ) { if( UPPER( arg1[i] ) == 'A' ) fAll = TRUE; else if( UPPER( arg1[i] ) == 'R' ) fRegex = TRUE; } str = arg2; while( ( str = strstr( str, "$^" ) ) ) { *str++ = '\n'; *str++ = '\r'; } str = arg3; while( ( str = strstr( str, "$^" ) ) ) { *str++ = '\n'; *str++ = '\r'; } if( fRegex ) replaced = regex_replace( ch->desc->pString, arg2, arg3, fAll ); else replaced = string_replace( ch->desc->pString, arg2, arg3, fAll ); if( replaced > 0 ) sprintf( buf, "Replaced %d lot%s of '&g%s&n' with '&y%s&n'.\n\r", replaced, ( replaced > 1 ) ? "s" : "", arg2, arg3 ); else if( replaced == 0 ) strcpy( buf, "No pattern matching that was found.\n\r" ); else if( fRegex && regexp_error ) { sprintf( buf, "REGEX error: %s.\n\r", regexp_error ); regexp_error = NULL; } else strcpy( buf, "The string became too long.\n\r" ); send_to_char( buf, ch ); return; } case 'p': case 'P': #if defined( HAVE_ISPELL ) ispell_string( ch, *ch->desc->pString ); #else send_to_char( "ISpell has been disabled on this server.\n\r", ch ); #endif return; } } /* * 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; } strcpy( buf, *ch->desc->pString ); strcat( buf, argument ); strcat( buf, "\n\r" ); free_string( *ch->desc->pString ); *ch->desc->pString = str_dup( buf ); return; } /***************************************************************************** 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 */ ) { const int MAX_LINE_LENGTH = 77; char xbuf[MAX_STRING_LENGTH]; char *wptr = &xbuf[0]; char *lastwordstart = NULL; int linelength = 0; bool newsentence = TRUE; int endoflines = 0; while( *oldstring ) { if( *oldstring == '&' ) { *wptr++ = *oldstring++; if( isprint( COLOURLIST[is_colour( *oldstring )][0] ) ) { linelength++; } *wptr++ = *oldstring; endoflines = 0; } else if( isspace( *oldstring ) ) { if( *oldstring == '\r' ) ; else if( *oldstring == '\n' && endoflines ) { strncpy( wptr, "\n\r\n\r", endoflines * 2 ); wptr += endoflines * 2; endoflines = 1; linelength = 0; newsentence = TRUE; } else { /* Only set this for the first '\n' so multiple '\n's don't * cause extra end-of-line sequences to be added. */ if( *oldstring == '\n' ) endoflines = 2; if( wptr == &xbuf[0] || !isspace( *(wptr - 1) ) ) { if( newsentence ) { *wptr++ = ' '; linelength++; } *wptr++ = ' '; linelength++; } } lastwordstart = oldstring + 1; } else { if( newsentence && isalpha( *oldstring ) ) { *wptr++ = UPPER( *oldstring ); newsentence = FALSE; } else { *wptr++ = *oldstring; } linelength++; if( *oldstring == '.' || *oldstring == '?' || *oldstring == '!' ) newsentence = TRUE; endoflines = 0; } if( linelength >= MAX_LINE_LENGTH ) { if( lastwordstart ) { oldstring = lastwordstart; do { --wptr; } while( !isspace( *wptr ) ); if( wptr > &xbuf[0] && isspace( *(wptr - 1) ) ) { --wptr; } } else { oldstring -= 2; wptr -= 2; *wptr++ = '-'; } *wptr++ = '\n'; *wptr++ = '\r'; linelength = 0; endoflines = 1; lastwordstart = NULL; } else oldstring++; } while( wptr > &xbuf[0] && isspace( *(wptr - 1) ) ) wptr--; *wptr++ = '\n'; *wptr++ = '\r'; *wptr = '\0'; free_string( oldstring ); return ( str_dup( xbuf ) ); } #if defined( HAVE_ISPELL ) /* Ispell, based on code by Erwin S. Andreasen, mostly just a straight copy. */ static FILE *ispell_out; static int ispell_pid = -1; static int to[2], from[2]; #define ISPELL_BUF_SIZE 1024 void ispell_init() { char ignore_buf[1024]; if( !IS_SET( SysInfo->flags, SYSINFO_ISPELL ) ) { ispell_pid = -1; return; } pipe( to ); pipe( from ); ispell_pid = fork(); if( ispell_pid < 0 ) { bug( "ispell_init: fork." ); } else if( ispell_pid == 0 ) /* child */ { int i; dup2( to[0], 0 ); /* this is where we read commands from - (stdin) */ close( to[0] ); close( to[1] ); dup2( from[1], 1 ); /* this is where we write stuff to - (stdout) */ close( from[0] ); close( from[1] ); /* Close all the other files */ for( i = 2; i < 255; i++ ) close( i ); if( SysInfo->dictionary && SysInfo->dictionary[0] ) { sprintf( ignore_buf, "-p%s", SysInfo->dictionary ); execlp( "ispell", "ispell", "-a", ignore_buf, (char*) NULL ); } else execlp( "ispell", "ispell", "-a", (char*) NULL ); exit( 1 ); } else /* ok !*/ { close( to[0] ); close( from[1] ); ispell_out = fdopen( to[1], "w" ); setbuf( ispell_out, NULL ); fprintf( ispell_out, "+\n" ); /* terse output, less to worry about */ #if !defined( __sun ) /* that ispell on sun gives no (c) msg */ read( from[0], ignore_buf, 1024 ); #endif } } void ispell_done() { if( ispell_pid != -1 ) { fprintf( ispell_out, "#\n" ); fclose( ispell_out ); close( from[0] ); waitpid( ispell_pid, NULL, 0 ); ispell_pid = -1; } } char *get_ispell_line( char *buf, const char *word ) { char buf2[MAX_INPUT_LENGTH + 10]; int len; if( ispell_pid == -1 ) return NULL; if( word ) { fprintf( ispell_out, "^%s\n", word ); fflush( ispell_out ); } len = read (from[0], buf2, ISPELL_BUF_SIZE); buf2[len] = '\0'; /* Read up to max 1024 characters here */ if( sscanf( buf2, "%1024[^\n]\n\n", buf ) != 1 ) return NULL; return buf; } void do_ispell( CHAR_DATA *ch, const char *argument ) { const char *pc; char buf[MAX_STRING_LENGTH]; if( ispell_pid <= 0 ) { send_to_char( "ispell is not running.\n\r", ch ); return; } if( !argument[0] || strchr( argument, ' ' ) ) { send_to_char( "Invalid input.\n\r", ch ); return; } if( argument[0] == '+' ) { if( !authorized( get_char( ch ), "ispell" ) ) return; for( pc = argument + 1; *pc; pc++ ) if( !isalpha( *pc ) ) { charprintf( ch, "'%c' is not a letter.\n\r", *pc ); return; } fprintf( ispell_out, "*%s\n", argument + 1 ); fflush( ispell_out ); charprintf( ch, "Word '%s' added to system dictionary.\n\r", argument + 1 ); send_to_char( "If you didn't mean to add that, please inform admin.\n\r", ch ); log_string( "%s added the word '%s' to the dictionary.", get_char( ch )->name, argument + 1 ); return; /* we assume everything is OK.. better be so! */ } pc = get_ispell_line( buf, argument ); if( !pc ) { send_to_char( "ispell: failed.\n\r", ch ); return; } switch( pc[0] ) { case '*': case '+': /* root */ case '-': /* compound */ send_to_char( "Correct.\n\r", ch ); break; case '&': /* miss */ case '?': /* guess */ charprintf( ch, "Not found. Possible words: %s\n\r", strchr( pc, ':' ) + 1 ); break; case '#': /* none */ send_to_char( "Unable to find anything that matches.\n\r", ch ); break; default: charprintf( ch, "Weird output from ispell: %s\n\r", pc ); } } void ispell_string( CHAR_DATA *ch, const char *str ) { char buf[MAX_STRING_LENGTH]; char word[MAX_INPUT_LENGTH]; char *wordp; const char *p, *q; int len, i; if( ispell_pid <= 0 ) { send_to_char( "ispell isn't running.\n\r", ch ); return; } for( q = str; ( p = strchr( q, '\n' ) ); q = p + 2 ) { strncpy( buf, q, p - q ); buf[p - q] = '\0'; fprintf( ispell_out, "^%s\n", buf ); } fflush( ispell_out ); len = read( from[0], buf, MAX_STRING_LENGTH - 2 ); buf[len] = '\0'; for( i = 0; i < len; ++i ) { if( buf[i] == '\n' ) buf[i] = '\0'; } for( i = 0; i < len; ++i ) { switch( buf[i] ) { case '*': /* correct */ case '+': /* root */ case '-': /* compound */ case '\0': break; case '&': /* miss */ case '?': /* guess */ i += 2; wordp = &word[0]; while( !isspace( buf[i] ) ) *wordp++ = buf[i++]; *wordp = '\0'; while( buf[i] != ':' ) ++i; i += 2; charprintf( ch, "'%s': %s.\n\r", word, &buf[i] ); break; case '#': /* none */ i += 2; wordp = &word[0]; while( !isspace( buf[i] ) ) *wordp++ = buf[i++]; *wordp = '\0'; charprintf( ch, "'%s': No suggestions.\n\r", word ); break; default: charprintf( ch, "Weird output from ispell: %s\n\r", &buf[i] ); break; } while( buf[i] != '\0' ) ++i; } return; } #else /* !defined( HAVE_ISPELL ) */ void do_ispell( CHAR_DATA *ch, const char *argument ) { send_to_char( "ISpell has been disabled on this system.\n\r", ch ); return; } #endif /* * 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 quotes, parenthesis (barring) ('s) and percentages. Called by: string_add( string.c ) ****************************************************************************/ const char *first_arg( const char *argument, char *arg_first, bool fCase ) { char cEnd; while( *argument == ' ' ) argument++; cEnd = ' '; if( strchr( "'\"%([{<", *argument ) ) { switch( *argument ) { /* Careful here, we could run off the end by accident. */ case '\0': cEnd = ' '; argument--; break; case '(': cEnd = ')'; break; case '[': cEnd = ']'; break; case '{': cEnd = '}'; break; case '<': cEnd = '>'; break; default: cEnd = *argument; break; } argument++; } while( *argument != '\0' ) { if( *argument == cEnd || ( cEnd == ' ' && isspace( *argument ) ) ) { argument++; break; } if( fCase ) *arg_first = LOWER( *argument ); else *arg_first = *argument; arg_first++; argument++; } *arg_first = '\0'; while( isspace( *argument ) ) argument++; return argument; } /* * Used in olc_act.c for aedit_builders. */ char *string_unpad( char *argument ) { char buf[MAX_STRING_LENGTH]; char *s; s = argument; while( *s == ' ' ) s++; strcpy( buf, s ); s = buf; if( *s != '\0' ) { while( *s != '\0' ) s++; s--; while( *s == ' ' ) s--; s++; *s = '\0'; } free_string( argument ); return str_dup( buf ); } /* * 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; } /* * Returns an all-caps string. OLC 1.1b * Used for saving helps. */ char *all_capitalize( char *strcap, const char *str ) { int i; for( i = 0; str[i] != '\0'; i++ ) strcap[i] = UPPER( str[i] ); strcap[i] = '\0'; return strcap; } /* * Takes a string of one line and splits it if it is too long */ char *multi_line( char *buf, const char *str, int length, int indent ) { int i, j; const char *p; char *q; p = str; j = 0; while( p < strchr( str, '\0' ) ) { i = length; if( p > str ) i -= indent; while( i > 0 ) { if( isspace( p[i] ) ) break; i--; if( p[i] == '-' ) break; } if( indent && p > str ) { char *sp; int k; sp = strchr( buf, '\0' ); for( k = 0; k < indent; k++ ) { *sp++ = ' '; } j += indent; } strncpy( &buf[j], p, i ); if( str[i] == '-' ) { strcpy( &buf[i + j], "-\n\r" ); j++; } else strcpy( &buf[i + j], "\n\r" ); p += i + 1; j += i + 2; } q = strchr( buf, '\0' ) - 1; while( isspace( *q ) ) { *q-- = '\0'; } ++q; *q++ = '\n'; *q = '\r'; return buf; } /* Implementation of a dynamically expanding buffer. Inspired by Russ Taylor's <rtaylor@efn.org> buffers in ROM 2.4b2. The buffer is primarily used for null-terminated character strings. A buffer is allocated with buffer_new, written to using buffer_strcat, cleared (if needed) using buffer_clear and free'ed using buffer_free. Erwin S. Andreasen <erwin@pip.dknet.dk> */ #define EMEM_SIZE -1 /* find_mem_size returns this when block is too large */ #define NUL '\0' #define MAX_MEM_LIST 14 /* from db.c */ extern const int rgSizeList [MAX_MEM_LIST]; /* Find in rgSizeList a memory size at least this long */ int find_mem_size( int min_size ) { int i; for( i = 0; i < MAX_MEM_LIST; i++ ) if( rgSizeList[i] >= min_size ) return rgSizeList[ i ]; /* min_size is bigger than biggest allowable size! */ return EMEM_SIZE; } /* Create a new buffer, of at least size bytes */ BUFFER *buffer_new( int min_size ) { int size; BUFFER *buffer; char buf[200]; /* for the bug line */ size = find_mem_size( min_size ); if( size == EMEM_SIZE ) { bug( buf ); abort( ); } buffer = alloc_mem( sizeof( BUFFER ) ); buffer->size = size; buffer->data = alloc_mem( size ); buffer->data[0] = '\0'; buffer->overflowed = FALSE; buffer->len = 0; return buffer; } void buffer_strcat( BUFFER *buffer, const char *text ) { int new_size; int text_len; char *new_data; char buf[200]; if( buffer->overflowed ) /* Do not attempt to add anymore if buffer is already overflowed */ return; if( !text ) /* Adding NULL string ? */ return; text_len = strlen( text ); if( text_len == 0 ) /* Adding empty string ? */ return; /* Will the combined len of the added text and the current text exceed our buffer? */ if( ( text_len + buffer->len + 1 ) > buffer->size ) /* expand? */ { new_size = find_mem_size( buffer->size + text_len + 1 ); if( new_size == EMEM_SIZE ) /* New size too big ? */ { bug( buf ); buffer->overflowed = TRUE; return; } /* Allocate the new buffer */ new_data = alloc_mem( new_size ); /* Copy the current buffer to the new buffer */ memcpy( new_data, buffer->data, buffer->len ); free_mem( buffer->data, buffer->size ); buffer->data = new_data; buffer->size = new_size; } memcpy( buffer->data + buffer->len, text, text_len ); /* Start copying */ buffer->len += text_len; /* Adjust length */ buffer->data[ buffer->len ] = NUL; /* Null-terminate at new end */ } /* Free a buffer */ void buffer_free( BUFFER *buffer ) { free_mem( buffer->data, buffer->size ); free_mem( buffer, sizeof( BUFFER ) ); } /* Clear a buffer's contents, but do not deallocate anything */ void buffer_clear( BUFFER *buffer ) { buffer->overflowed = FALSE; buffer->len = 0; } /* print stuff, append to buffer. safe-ish, a bit of a hack. */ #if !defined( HAVE_VSNPRINTF ) int bprintf( BUFFER *buffer, const char *fmt, ... ) { /* we have to allow for overflow, this is a bit of a hack */ char buf[MAX_STRING_LENGTH + MAX_INPUT_LENGTH]; va_list va; int res; va_start( va, fmt ); vsprintf( buf, fmt, va ); va_end( va ); res = strlen( buf ); if( res >= MAX_STRING_LENGTH - 1 ) { buf[0] = NUL; bug( "Overflow when printing string %s", fmt ); } else buffer_strcat( buffer, buf ); return res; } #else int bprintf( BUFFER *buffer, const char *fmt, ... ) { char buf[MAX_STRING_LENGTH]; va_list va; int res; va_start( va, fmt ); res = vsnprintf( buf, MAX_STRING_LENGTH, fmt, va ); va_end( va ); if( res >= MAX_STRING_LENGTH - 1 ) { buf[0] = NUL; bug( "Overflow when printing string %s", fmt ); } else buffer_strcat( buffer, buf ); return res; } #endif int is_colour( char code ) { switch( code ) { /* Normal */ case 'n': case 'x': return 0; break; /* Normal colours */ case 'r': return 1; break; /* Red */ case 'g': return 2; break; /* Green */ case 'y': return 3; break; /* Yellow */ case 'b': return 4; break; /* Blue */ case 'm': return 5; break; /* Magenta */ case 'c': return 6; break; /* Cyan */ case 'w': return 7; break; /* White */ case 'k': return 8; break; /* Black */ /* Bold colours */ case 'R': return 9; break; /* Bold red */ case 'G': return 10; break; /* Bold green */ case 'Y': return 11; break; /* Bold yellow */ case 'B': return 12; break; /* Bold blue */ case 'M': return 13; break; /* Bold magenta */ case 'C': return 14; break; /* Bold cyan */ case 'W': return 15; break; /* Bold white */ case 'K': return 16; break; /* Bold black */ /* Background colours */ case '1': return 17; break; /* Red background */ case '2': return 18; break; /* Green background */ case '3': return 19; break; /* Yellow background */ case '4': return 20; break; /* Blue background */ case '5': return 21; break; /* Magenta background */ case '6': return 22; break; /* Cyan background */ case '7': return 23; break; /* White background */ case '0': return 24; break; /* Black background */ /* Special codes */ case 'f': return 25; break; /* Flash */ case 'v': return 26; break; /* Reverse video */ case 'u': return 27; break; /* Underline (Only for mono screens) */ /* Misc characters */ case '&': return 28; break; /* The & character */ case '\\': return 29; break; /* The \ character */ case '-': return 30; break; /* tilde ~ */ case '/': return 31; break; /* carriage return \n */ case '*': return 32; break; /* bell */ case '!': return 33; break; /* bar | */ } return 0; } void proc_colour( char *outbuf, const char *inbuf, bool colour ) { register int j = 0, p = 0; int k, max, c = 0; if( inbuf[0] == '\0' ) return; while( inbuf[j] != '\0' && p < MAX_STRING_LENGTH * 4 - 3 ) { if( ( inbuf[j] == '&' ) ) { c = is_colour( inbuf[j + 1] ); j += 2; } else { outbuf[p] = inbuf[j]; j++; p++; continue; } max = strlen( COLOURLIST[c] ); if( colour || max == 1 ) { for( k = 0; k < max; k++ ) { outbuf[p] = COLOURLIST[c][k]; p++; } } } if( p >= MAX_STRING_LENGTH * 4 - 3 ) { outbuf[p - 30] = '\0'; /* 30 back just to be safe */ strcat( outbuf, "\n\r\n\r***Output Overflow***\n\r" ); } else { outbuf[p] = '\0'; } return; } /* * Length of the string as seen minus colour codes. */ int colour_strlen( const char *str ) { int i, j; j = 0; for( i = 0; str[i]; i++ ) { if( str[i] == '&' ) { if( !isprint( COLOURLIST[is_colour( str[i + 1] )][0] ) ) j--; } else j++; } return j; } /* * Makes the string the correct length for format with colour. */ char *colour_strpad( char *outstr, const char *str, const int length ) { int i, j; j = 0; for( i = 0; str[i] && j < length; i++ ) { outstr[i] = str[i]; if( str[i] == '&' ) { if( !isprint( COLOURLIST[is_colour( str[i + 1] )][0] ) ) j--; } else j++; } outstr[i++] = '&'; outstr[i++] = 'n'; while( j < length ) { outstr[i++] = ' '; j++; } outstr[i] = '\0'; return outstr; } /* * Centres the string on a certain length line. */ char *colour_strcentre( char *outstr, const char *str, const int length ) { char *p = outstr; int pad, i; pad = length - colour_strlen( str ); if( pad <= 0 ) return colour_strpad( outstr, str, length ); for( i = 0; i < pad / 2; ++i ) *p++ = ' '; for( i = 0; str[i] != '\0'; ++i ) *p++ = str[i]; for( i = pad / 2; i < pad; ++i ) *p++ = ' '; *p = '\0'; return outstr; } /* * Limits the length of a colourised string. * Very similar to the above, except destructive to the string. */ void str_limit( char *str, const int length ) { int i, j; j = 0; for( i = 0; str[i] && j < length; i++ ) { if( str[i] == '&' ) { if( !isprint( COLOURLIST[is_colour( str[i + 1] )][0] ) ) j--; } else j++; } str[i++] = '&'; str[i++] = 'n'; str[i] = '\0'; } /* * Remove as much of the colour code as possible. */ char *kill_colour( char *outstr, const char *str ) { int i, j; j = 0; for( i = 0; str[i]; i++ ) { if( str[i] == '&' ) { if( isprint( COLOURLIST[is_colour( str[++i] )][0] ) ) outstr[j++] = COLOURLIST[is_colour( str[i] )][0]; } else outstr[j++] = str[i]; } outstr[j] = '\0'; return outstr; }