Wrapping Static Text

When displaying a long string of text, it’s polite — and expected — for the programmer to wrap the text. Not wrapping the text would split words at the right margin, which irritates human readers to no end.

Last week’s Lesson discussed basic word-wrap philosophy. It’s not that difficult of a concept to grasp, but it isn’t the easiest thing to code.

As a review, here are the steps to wrap a word at the terminal’s right margin:

  1. If the character index (current column position) isn’t yet at the margin, keep reading characters. Otherwise:
  2. Backup to locate the nearest white space character.
  3. Replace that whitespace character with a newline.
  4. Start the next line with the text you backed over. (This is the tricky part.)
  5. Start over.

The following code demonstrates one way that these steps can be used to wrap static text, or text that’s already presented within the code.

#include <stdio.h>
#include <ctype.h>
#include <string.h>

#define COLUMN 40

int main()
{
    char preamble[] = "We the People of the United States, in Order to form a more perfect Union, establish Justice, insure domestic Tranquility, provide for the common defense, promote the general Welfare, and secure the Blessings of Liberty to ourselves and our Posterity, do ordain and establish this Constitution for the United States of America.";
    char *base,*right_margin;
    int length,width;

    length = strlen(preamble);
    base = preamble;
    width = COLUMN;

    while(*base)
    {
        if(length <= width)
        {
            puts(base);     /* display string */
            return(0);      /* and leave */
        }
        right_margin = base+width;
        while(!isspace(*right_margin))
        {
            right_margin--;
            if( right_margin == base)
            {
                right_margin += width;
                while(!isspace(*right_margin))
                {
                    if( *right_margin == '\0')
                        break;
                    right_margin++;
                }
            }
        }
        *right_margin = '\0';
        puts(base);
        length -= right_margin-base+1;      /* +1 for the space */
        base = right_margin+1;
    }

    return(0);
}

The while loop at Line 17 controls the word wrap process. It's actually a test: If the string represented by the base variable is empty, then nothing is wrapped.

Within the loop, a test takes place at Line 19: If the string's length (obtained at Line 13) is shorter than the right margin (set in the width variable), then a puts() function displays the string and the program terminates. Done!

Line 24 begins the wrapping process. At this point in the code, the text referenced by char pointer base is wider than the right column (the value stored in the width variable). The right margin position is referenced by this calculation:

right_margin = base+width;

So if the right margin is at 80 columns, the pointer right_margin is set 80 characters forward from the base pointer — effectively that's a line of text. But the line may terminate in the middle of a word, so the code attempts to find a white space character.

The while loop at Line 25 is tasked with finding that white space character. The isspace() function helps locate the wrapping point. The while loop continues to spin as long as the character represented by pointer right_margin isn't a space.

Line 27 backs up the pointer right_margin one character position, hunting for the wrapping point.

The if test at Line 28 (going all the way to Line 37) checks for the rare condition that no spaces are found for wrapping the text. If so, the right margin is recalculated (Line 30), and a non-white space character is searched for beyond the right margin, which happens with the while loop at Line 31.

At Line 39, the whitespace character is replaced by a null character, \0. Then the puts() function at Line 40 displays the string, which at this point easily fits within the margin specified.

Line 41 adjusts the value of the length variable, which is used at Line 19 to determine when the string no longer needs to be processed.

Line 42 calculates the starting point for the rest of the string, and the while loop continues.

Yes, this is some complex code! The basic wrapping function could be written in fewer lines, but this example handles two conditions that do arise: When the string is too short to wrap (Lines 19 through 23) and when a word is wider than the right margin (Lines 28 through 37).

Next week's Lesson demonstrates a way to wrap streaming text.

Leave a Reply