After a few days of tidying up code its time to get back to the job of improving things. Today we’ll tackle a nice simple job, pstack_mid.

To begin with we’ll create a variable argument version of that function, mainly because it annoys me there isn’t one:

void PSTACK_MID(const char *format, ...)
{
char *temp;
va_list argum;va_start(argum, format);
vsprintf(stack, format, argum);
va_end(argum);temp = strdup(stack);
pstack_mid(temp);
if (temp)
FREE(temp);
}

So a pretty standard function, with the only talking point being that I don’t check strdup returned a sane value until its time to tidy up.

The current pstack_mid is a little dumb when it comes to handling strings with colour codes, so we’ll fix that, and give it a slight performance boost as well.


extern int valid_char_col(char);void pstack_mid(char *str)
{
int width, half, len;
char *line_pos, *str_pos, cur_col[1], last_col[1];/* if str is empty or non existant, just print a line */
if (!str || !*str)
{
stack = stpcpy(stack, LINE "\n");
return;
}

Thats why we didn’t handle strdup in PSTACK_MID, we’ll handle it in the real function and save duplicating some code.


/* we want the length of str without counting the colour codes */
str_pos = str;
len = 0;
*last_col = 'N';
while (*str_pos)
{
/* check if we have a colour code */
if (*str_pos == '^')
{
if (*(str_pos + 1) == 'N' || *(str_pos + 1) == 'n' || valid_char_col(*(str_pos + 1)))
{
*last_col = *(str_pos + 1);
str_pos += 2;
continue;
}
/* ^^ renders as ^ so check for that case */
else if (*(str_pos + 1) == '^')
str_pos++;
}
/* if we're here count the character */
len++;
str_pos++;
}

The new pstack_mid is really just 3 more copy and pastes of that loop.


/* try and get the current line width */
if (current_player)
width = current_player->term_width;
else
width = 76;
/* ensure width is always long enough to hold str. We'll let process_output
handle getting the line wrap right */
width = width * ((len + 2) / width) + 1);

The original code has a slight flaw. If the length of the string being centered is the same as the line width, it’ll crash the talker. the original code was a little silly when str was larger than width, so we solve both those problems by figuring out the number of lines we need to hold the str and sizing width accordingly.


/* copy the prefix */
half = (width - (len + 2)) / 2;
*cur_col = 'N';
line_pos = LINE;
while (half)
{
/* check to see if we're on a colour code */
if (*line_pos == '^')
{
if (*(line_pos + 1) == 'N' || *(line_pos + 1) == 'n' || valid_char_col(*(line_pos + 1)))
{
*cur_col = *(line_pos + 1);
line_pos += 2;
*stack++ = '^';
*stack++ = *cur_col;
if (!*line_pos)
line_pos = LINE;
}
/* ^^ renders as ^ so check for that case */
else if (*(line_pos + 1) == '^')
line_pos++;
}
*stack++ = *line_pos++;
half--;
if (!*line_pos)
line_pos = LINE;
}

Very similar to our string counting loop. Only real important thing to note is that we don’t use an external function to keep track of where we are in the LINE constant.


/* now to copy the str... */
*stack++ = ' ';
stack = stpcpy(stack, str);

We could have used the same loop to copy str into place, but this way is potentially faster.


/* we need to sync our positions so a quick loop coming up */
half = len + 2;
while (half)
{
/* check to see if we're on a colour code */
if (*line_pos == '^')
{
if (*(line_pos + 1) == 'N' || *(line_pos + 1) == 'n' || valid_char_col(*(line_pos + 1)))
{
*cur_col = *(line_pos + 1);
line_pos += 2;
if (!*line_pos)
line_pos = LINE;
}
/* ^^ renders as ^ so check for that case */
else if (*(line_pos + 1) == '^')
line_pos++;
}
half--;
line_pos++;
if (!*line_pos)
line_pos = LINE;
}

Although we still need that loop to ensure we’re in the right place for when we recopy the line, and have the right colour code for the next part.


/* if your paying attention as opposed to blindly copying and pasting
you'll note I've kept track of the colour code in effect. So now
we can restore the line colour if needed. */
if (*cur_col != *last_col)
{
stack = stpcpy(stack, "^N^");
*stack++ = *cur_col;
}
*stack++ = ' ';

TrueFriends uses a similar system to ensure its cname doesn’t mess up the screen. Well similar in the same way that oranges and lemons are both citrus fruits.


/* now we copy the postfix */
half = (width - (len + 2)) / 2;
/* if width - (len + 2) is odd, then the line is short by 1 character
so lets fix that */
if (width - (len + 2) % 2)
half++;

I’m sorry there just isn’t an intuitive way to test if a number is odd.


while (half)
{
/* check to see if we're on a colour code */
if (*line_pos == '^')
{
if (*(line_pos + 1) == 'N' || *(line_pos + 1) == 'n' || valid_char_col(*(line_pos + 1)))
{
*cur_col = *(line_pos + 1);
line_pos += 2;
*stack++ = '^';
*stack++ = *cur_col;
if (!*line_pos)
line_pos = LINE;
}
/* ^^ renders as ^ so check for that case */
else if (*(line_pos + 1) == '^')
line_pos++;
}
*stack++ = *line_pos++;
half--;
if (!*line_pos)
line_pos = LINE;
}

The final cut and paste of that loop to finish off the line.


/* Now we tidy everything up */
if (*cur_col != 'N')
{
*stack++ = '^';
*stack++ = 'N';
}
*stack++ = '\n';
*stack = '';
}

If colour is on the line, then we end the colour.

And thats a colour aware pstack_mid. Its probably a good idea to quickly scan the code and see where its sane to use the new variable argument version in place of the single argument version.

Sorry, for WordPress being dumb and stripping out the code formatting, I’ll try and find a way to make it saner for the  more complicated code thats coming.

Advertisements