Colour tokens causing misalignment with string formatting (e.g. %-40s)

Posted by Marowi on Sun 13 Jul 2014 02:12 AM — 7 posts, 27,647 views.

#0
Hey folks,

I've been digging into a problem with aligning coloured text, and am running short on ideas. I'm working with a modified SMAUG/SWR codebase, but I believe this is primarily related to standard functions.

The problem : using '%-40s' (or any string format/length token) will consider the colour tokens as part of the '40', and the output will consequently be 2 shorter than I want.

The code:

	ch_printf( ch, "&zName: &c%-40s &zVnum: &c%d\n\r", (location->name), location->vnum );
	ch_printf( ch, "&zName: &c%-40s &zVnum: &c%d\n\r", showclr(location->name), location->vnum );
	ch_printf( ch, "&zName: &c%-40s &zVnum: &c%d\n\r", stripclr(location->name), location->vnum );
	ch_printf( ch, "&zName: &c%-40s | strlen: %d | strlen_color: %d\n\r", location->name, strlen(location->name), strlen_color(location->name));


The output:

Name: The Kuragame Waterfall                 Vnum: 4102
Name: &pThe Kuragame Waterfall                Vnum: 4102
Name: The Kuragame Waterfall                   Vnum: 4102
Name: The Kuragame Waterfall                 | strlen: 24 | strlen_colour: 22


Technically, there are 24 characters in the string provided, so it's not entirely incorrect to be adding only 16 spaces to make it to the 40 requested. However, I would like it to disregard the colour tags when counting characters.

My best guess is that it's because ch_printf() has no idea that I'm going to swap colour tokens for ANSI code, so when it calculates string length, it's doing the best it can without that information.

ch_printf:

void ch_printf( CHAR_DATA *ch, char *fmt, ... )
{
   char buf[MAX_STRING_LENGTH*2];
   va_list args;

   va_start( args, fmt );
   vsnprintf( buf, MAX_STRING_LENGTH*2, fmt, args );
   va_end( args );

   send_to_char_color( buf, ch );
}


I'm at a bit of a loss as to how other MUDs handle this; perhaps I should have opened with the question - do they?

Any advice is appreciated!

-Marowi
USA #1
Honestly, every mud I've worked on has resorted to either coding in the colors elsewhere, adding in extra whitespace to account for the color codes in that string or saying screw it and still does it oldschool. There's also the option of checking the string and subtracting 2 from strlen for every color code in the provided text. Of more concern to me though is the fact that your sample text offsets aren't correct.

2nd should be offset 2 from the first and isn't after the whitespace.
#2
Trying to determine in which cases to add the whitespace is the difficult part. I'd only want it for particular strings, where the formatting means misalignment (who list, etc).

I have a strlen_color function that doesn't count the colour codes, but it seems that the string length is already determined before it gets to any sort of color conversion code.

The sample text offsets are correct. With this codebase, 'showclr' doubles up any colour codes in the string, so they display in the output.
e.g. "&BOcean" + showclr = "&&BOcean"

I guess I could not bother with alignment for any strings that could have colours, and strip out the colours for any strings I definitely want aligned.

Thanks for your reply.
Australia Forum Administrator #3
In the source (color.c) I found this, doesn't that do what you want?


/*
 * Quixadhal - I rewrote this too, so that it uses colorcode.  It may not
 * be as efficient as just walking over the string and counting, but it
 * keeps us from duplicating the code several times.
 *
 * This function returns the intended screen length of a string which has
 * color codes embedded in it.  It does this by stripping the codes out
 * entirely (A NULL descriptor means ANSI will be FALSE).
 */
int color_strlen( const char *src )
...


Using that you can find the length of the string without colour codes, then just work out how many spaces to send afterwards.
#4
I wrote a strlen_color, which presumably does the same thing. In the example, strlen(location->name) returns 24, while strlen_color(location->name) returns 22.

The problem I'm having is that the %-40s has no concept of anything except the raw text passed to it, which it correctly determines as 24 characters in length, and adds another 16 spaces.

Your suggestion did give me the idea that I add another %s after the %-40s, and instead count the colour codes inside (location->name) and add that many spaces - it's messy, but if absolutely necessary, it could work.
Australia Forum Administrator #5
Yes that was what I meant. In fact you can probably print "" (an empty string) with a dynamic length, eg.


%-40s%-*s


Then supply the number of spaces you want as an integer before the blank string (or is it after?) Like that, anyway.
#6
Ah! Fantastic. I hadn't known it was possible to insert a dynamic length token like that; a quick search revealed that a lot of people have the same reaction.

Thanks again for all the responses - got this sorted for now!