Exercise 1.22 - fold long lines

Question

Write a program to fold long input lines into two or more shorter lines after the last non-blank character that occurs before the n-th column of input. Make sure your program does something ntelligent with very long lines, and if there are no blanks or tabs before the specified column.

Solution

/* Exercise 1-22: Write a program to "fold" long input lines into two or more
                  shorter lines after the last non-blank character that occurs
                  before the n-th column of input. */

#include <stdio.h>

#define MAXCOL 35                           /* folded line length */
#define TABVAL 8                            /* standard tab length */
#define CURTAB(c) (TABVAL - ((c) % TABVAL)) /* current tab size */
#define NO_BLANK -1                         /* signifies no blank found */

int lastblank(const char arr[], int len);

/* folds long input lines into two or more shorter lines */
int main(void) {
    int c;                 /* character variable */
    int i, j;              /* indexing variable(s) */
    int pos;               /* current position in array */
    int col;               /* current column of output */
    int lbc;               /* last blank character position */
    char line[MAXCOL + 1]; /* fold array */

    pos = col = 0;
    while ((c = getchar()) != EOF) {
        /* process line array, keep track of line length by columns */
        line[pos++] = c;
        col += (c == '\t') ? CURTAB(col) : 1;

        /* create fold */
        if (col >= MAXCOL || c == '\n') {
            line[pos] = '\0';

            if ((lbc = lastblank(line, pos)) == NO_BLANK) {
                /* split word if no blank characters */
                for (i = 0; i < pos; ++i)
                    putchar(line[i]);
                /* reset column value and array position */
                col = pos = 0;
            } else {
                /* print array up until last blank character */
                for (i = 0; i < lbc; ++i)
                    putchar(line[i]);
                /* feed remaining characters into buffer */
                for (i = 0, j = lbc + 1, col = 0; j < pos; ++i, ++j) {
                    line[i] = line[j];
                    /* set new column value */
                    col += (c == '\t') ? CURTAB(col) : 1;
                }
                /* set array position after remaining characters */
                pos = i;
            }
            /* finish folded line with newline character */
            putchar('\n');
        }
    }

    return 0;
}

/* finds the last whitespace character in an array
   and returns the position */
int lastblank(const char arr[], int len) {
    int i, lbc;

    lbc = -1;
    for (i = 0; i < len; ++i)
        if (arr[i] == ' ' || arr[i] == '\t' || arr[i] == '\n')
            lbc = i;

    return lbc;
}

Explanation

  1. We determine the column to fold in the MAXCOL variable.

2. Since tab character can occur too and folding a tab character means folding it in mid-way, we also replace the tabs in the line with spaces.

3. Every character of the file is filled into a line[MAXCOL] array and that line is acted upon by the program.

4. We start at pos=0 and take each character and place it in line[pos] and then we analyze the character to act upon the condition.

6. If the character is \t. We go and expand the tab character in the line[pos] and get newposition. So, when line[t] at pos = 0, it will be now line[' ', ' ', ' ',' ',' ',' ',' ',' '] and pos = 8

7. If character is \n then we print the entire line contents reset the pos back to 0.

  1. otherwise, we get into our program.

When we are folding, we should not be folding in between the word. So we have to track the previous space occuring in a line. The logic follows.

1. When our position is greater than MAXCOL, then we look for previous blank space by using getblank and we get the position of that blank.

2. We then fold, getblank will return the pos which is not greater than MAXCOL. So, the print the characters we have in line and then print newline.

3. We determine the new position based the return value of getblank. If the return value of getblank was greater than MAXCOL, then our new position is 0, which is a newline. We will replace the contents of line starting from 0, mark this as i, and place the folded contents by the last for loop in the program and after placing the folded contents we return the new value of i, which is our updated pos.

With our new position we continue with the rest of the program.