Exercise 1-22. Write a program to “fold” long 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 intelligent with very long lines, and if there are no blanks or tabs before the specified column.
Line folding is the most algorithmically demanding exercise in Chapter 1. The goal sounds straightforward — break long input lines at word boundaries before column 80 — but the implementation requires tracking three moving pieces simultaneously: how many characters are on the current output line (col), where the most recent space or tab appeared in the buffer (last_space), and how many characters are buffered so far (i). When column 80 arrives in the middle of a word, the program must reach back to the last space, replace it with a newline, print everything up to that point, slide the partial word to the front of the buffer, and continue. If no space has appeared at all, the exercise demands something intelligent — the right answer is a hard fold at the column limit.
Understanding the Three Variables
Before reading the code, lock down what each variable tracks:
col— how many characters have been written onto the current output line. Increments with every character stored. When it reachesFOLD, it is time to break.last_space— the index inline[]of the most recent space or tab. Initialised to-1, meaning no space seen yet on this line.i— the next free slot in the buffer. Equalscolas long as no fold has been triggered on the current logical input line.
The Three Cases in the Main Loop
Case 1 — newline character: The input line ended before hitting column 80. Store the newline, print the buffer, and reset col, i, and last_space to zero, zero, and -1.
Case 2 — at the fold column, space available (last_space >= 0): Replace line[last_space] with '\n' and print the buffer up to that point. Then shift the characters that follow the fold point to the front of the buffer — these are the characters of the word that was interrupted. Set col and i to the number of characters shifted. Append the character that triggered the fold and continue.
Case 3 — at the fold column, no space seen (last_space == -1): Hard fold. Append the current character, print the buffer with a trailing newline, and reset everything. This handles URLs, hex strings, or any token that has no natural break point within 80 columns.
Tracing a Word-Boundary Fold
This trace uses FOLD = 40 to keep the numbers short; the logic is identical at 80.
Input line:
Pack my box with five dozen liquor jugs and the rest of the sentence.
The program reads one character at a time, storing each in line[] and incrementing col. Every space updates last_space.
| After reading… | col | last_space (buffer index) |
|---|---|---|
'P' |
1 | -1 |
' ' (after “Pack”) |
5 | 4 |
' ' (after “my”) |
8 | 7 |
| … (each space updates last_space) … | … | … |
's' (last char of “jugs”), col = 39 |
39 | 34 (space before “jugs”) |
' ' (space after “jugs”), col = 40 |
40 | 39 |
Next character is 'a' (from “and”). Now col == 40 >= FOLD and last_space == 39. The soft-fold branch fires:
- Set
line[39] = '\n'— the space after “jugs” becomes a newline. - Print
"Pack my box with five dozen liquor jugs\n". - Shift characters from index 40 onward to the front — nothing to shift here since the space was the last character buffered (
i == 40andlast_space+1 == 40). - Set
i = 0,col = 0,last_space = -1. - Append
'a'normally; reading continues.
Result:
Pack my box with five dozen liquor jugs and the rest of the sentence.
Tracing a Hard Fold
Now suppose the input is a continuous string of 88 characters with no spaces (think of a raw hash value or a long URL fragment):
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz
After 80 characters are buffered, col == 80 and last_space == -1. The 81st character (which is 's') triggers the hard-fold branch:
- Append
's'to the buffer (now 81 characters). - Print all 81 characters followed by
'\n'. - Reset
i,col,last_space. - The remaining
"tuvwxyz"goes onto the next line.
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrs tuvwxyz
The hard-fold output line is one character wider than FOLD (81 vs 80). That is the correct behaviour: there was no earlier break point, and printing 80 characters then a newline would silently discard the character that triggered the fold. Including it and noting the overshoot is the intelligent choice the exercise asks for.
Solution
/* K&R Exercise 1-22: fold long lines at word boundaries
Compile: gcc -ansi -Wall exercise1-22.c -o exercise1-22 */
#include <stdio.h>
#define MAXLINE 1000
#define FOLD 80 /* fold at this column */
int main(void)
{
char line[MAXLINE];
int c, col, last_space, i, j, k;
col = 0;
last_space = -1;
i = 0;
while ((c = getchar()) != EOF) {
if (c == '\n') {
/* line ended before the fold column — print as-is */
line[i++] = '\n';
line[i] = '\0';
printf("%s", line);
i = 0;
col = 0;
last_space = -1;
} else if (col >= FOLD) {
if (last_space >= 0) {
/* soft fold: break at the last space seen */
line[last_space] = '\n';
line[i] = '\0';
printf("%s", line);
/* slide chars after the fold point to front of buffer */
j = 0;
k = last_space + 1;
while (k < i)
line[j++] = line[k++];
i = j;
col = j;
last_space = -1;
} else {
/* hard fold: no space found — print and break here */
line[i++] = c;
line[i] = '\0';
printf("%s\n", line);
i = 0;
col = 0;
last_space = -1;
continue;
}
/* fall through: append c after the soft fold */
line[i++] = c;
if (c == ' ' || c == '\t')
last_space = i - 1;
++col;
} else {
/* normal case: buffer the character */
line[i++] = c;
if (c == ' ' || c == '\t')
last_space = i - 1;
++col;
}
}
/* flush any remaining characters */
if (i > 0) {
line[i] = '\0';
printf("%s", line);
}
return 0;
}
Compile and Run
gcc -ansi -Wall exercise1-22.c -o exercise1-22
echo "This sentence is short." | ./exercise1-22
To fold a real file, pipe it through the program:
cat longfile.txt | ./exercise1-22
Or type directly and press Ctrl+D to signal end of input:
./exercise1-22
Sample Output
Input (one long line, 113 characters):
K&R is a slim book that manages to teach systems programming, style, and the C standard library all at once without wasted words.
Output with FOLD = 80:
K&R is a slim book that manages to teach systems programming, style, and the C standard library all at once without wasted words.
The fold happened at the space before “standard” — the last space before column 80. The second line picks up seamlessly from the next word.
What This Exercise Teaches
- Buffered line processing: unlike simple character-at-a-time exercises, this one requires holding an entire line in memory so you can reach back and edit a character you already stored.
- Tracking state across iterations:
last_spacemust survive from one loop iteration to the next; forgetting to reset it after each fold is a common source of bugs. - In-place buffer editing: replacing
line[last_space]with'\n'is a clean way to modify already-buffered content without extra storage. - Handling degenerate input: the hard-fold branch teaches the habit of asking “what if the normal case never occurs?” — a skill that matters for every real-world input parser.
Set Up Your C Environment
To compile and run this solution you need GCC installed. If you have not set up C on your machine yet:
- Complete C Development Environment Setup — recommended starting point
- Install GCC on Windows 11
- Install GCC on macOS
- Install GCC on Ubuntu/Linux
- VS Code for C Programming — recommended editor
← Exercise 1-21 |
Chapter 1 Solutions |
Exercise 1-23 →
Book:
The C Programming Language, 2nd Ed — Kernighan & Ritchie