K&R C Programs Exercise 7-8

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 firstprint_header emits \f before 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_file starts fresh with pageno = 1; pages are numbered per-file, not globally, matching the behaviour of traditional Unix pr

What This Exercise Teaches

  • Form feed (\f) — ASCII 12 advances a printer or terminal to the top of the next page; less -r and most pagers render it as a page break
  • Line counting vs character counting — counting calls to fgets is 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 #defineLINES_PER_PAGE and HEADER_LINES as 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

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>