Exercise 7-8. Write a program to print a set of files, starting each new file on a new page, with a title and a running page count for each file.
Print each file with a header line (filename + page number) at the top of every page. A page is LINES_PER_PAGE lines. When a page is full, emit a form-feed (\f) to advance to the next physical page, print the header, and continue. Starting a new file always forces a new page.
Solution
/* K&R Exercise 7-8 — print files with page headers and page count
* Compile: gcc -ansi -Wall ex7-8.c -o pr7
* Usage: ./pr7 file1.txt file2.txt ...
* Redirect to printer or use 'less' to see form-feeds */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define LINES_PER_PAGE 60
#define HEADER_LINES 2 /* header + blank line */
static void print_header(const char *filename, int pageno)
{
printf("\f"); /* form feed */
printf("-- %s (page %d) --\n\n", filename, pageno);
}
static void print_file(const char *filename)
{
FILE *fp;
char line[1024];
int lineno = 0;
int pageno = 1;
int body_lines = LINES_PER_PAGE - HEADER_LINES;
if ((fp = fopen(filename, "r")) == NULL) {
fprintf(stderr, "pr7: can't open %s\n", filename);
return;
}
print_header(filename, pageno);
while (fgets(line, sizeof line, fp)) {
if (lineno >= body_lines) {
pageno++;
lineno = 0;
print_header(filename, pageno);
}
fputs(line, stdout);
/* count embedded newlines */
if (line[strlen(line)-1] == '\n')
lineno++;
}
fclose(fp);
}
int main(int argc, char *argv[])
{
int i;
if (argc < 2) {
fprintf(stderr, "Usage: %s file ...\n", argv[0]);
return 1;
}
for (i = 1; i < argc; i++)
print_file(argv[i]);
return 0;
}
Compile and Test
gcc -ansi -Wall ex7-8.c -o pr7
# Generate a 75-line file (more than one page at 60 lines)
seq 1 75 | awk '{print "Line "$1": The quick brown fox jumps over the lazy dog."}' \
> /tmp/longfile.txt
./pr7 /tmp/longfile.txt | cat -A | grep -c "^\^L" # should print 2 form-feeds
./pr7 /tmp/longfile.txt | head -5 # show first page header
Expected Output (first 3 lines)
-- /tmp/longfile.txt (page 1) -- Line 1: The quick brown fox jumps over the lazy dog.
Design Notes
- Form feed first —
print_headeremits\fbefore the header text; this means the first page of the first file also gets a form feed, which is standard for printer output (clears the page before starting) body_lines = LINES_PER_PAGE - HEADER_LINES— the header itself consumes 2 lines (the header line and a blank line); this is subtracted from the page budget so text fits within the physical page- Multi-file usage — each call to
print_filestarts fresh withpageno = 1; pages are numbered per-file, not globally, matching the behaviour of traditional Unixpr
What This Exercise Teaches
- Form feed (
\f) — ASCII 12 advances a printer or terminal to the top of the next page;less -rand most pagers render it as a page break - Line counting vs character counting — counting calls to
fgetsis simpler but slightly wrong for lines that exceed the buffer size; here the buffer is 1024, larger than any reasonable line, so it is effectively correct - Configurable page size via
#define—LINES_PER_PAGEandHEADER_LINESas named constants make it trivial to adjust for different output media (80-line screens, 66-line paper, etc.)
Set Up Your C Environment
← Exercise 7-7 |
Chapter 7 Solutions |
Exercise 7-9 →
Book: The C Programming Language, 2nd Ed — Kernighan & Ritchie