STinC++ rewrites the programs in Software Tools in Pascal using C++
The previous program, concat
, was a simple example of taking an indeterminate number of inputs and generating one output. That your own machine undoubtedly has cat
or its moral equivalent installed shows the enduring utility of combining files, unformatted, into a single stream.
Similar styles of program, such as less
, take those multiple inputs and produce some kind of formatted output. Kernighan and Plauger’s next program, print
, continues the many-in/one-out pattern and adds just a smattering of formatting.
PROGRAM |
print print files with headings
|
USAGE |
print [ file … ]
|
FUNCTION |
print copies each of its argument files in turn to its output, inserting page headers and footer and filling the last page of each file to full length. A header consists of two blank lines, a line giving the filename and page number, and two more blank lines; a footer consists of two blank lines. Pages for each file are numbered starting at one. If no arguments are specified, print prints its standard input; the file name is null.
The text of each file is unmodified - no attempt is made to fold long lines or expands tabs to spaces.
|
EXAMPLE |
|
My implementation is probably much as you would imagine. Loop on each file, reading a line at a time, keeping count of lines as we go. If we’re at the top of a page, print the header. If we’re at the bottom, print the footer. When we run out of lines to read, pad with blanks until we hit the bottom of the page.
My initial implementation matched almost exactly with Kernighan and Plauger’s. The only real difference was that I calculated the number of lines in the header and footer from their implementations, rather than using manifest constants. But there it was, a loop, some input, some output, a bit of incrementing, some conditionals, just as Kernighan and Plauger had 40 years previously -
print.cpp - initial implementation
int print(
std::string const& filename,
std::istream& input,
std::ostream& output,
size_t path_length
) {
size_t page_count = 0;
size_t line_count = 0;
size_t const last_line = path_length - footer_lines;
while(input && !input.eof()) {
if (line_count == 0) {
header(filename, ++page_count, output);
line_count += header_lines;
}
std::string line = getline(input);
output << line << '\n';
++line_count;
if (line_count == last_line) {
footer(output);
line_count = 0;
}
}
if (line_count != 0) {
for ( ; line_count != last_line; ++line_count)
output << '\n';
footer(output);
}
return page_count;
} // print
Since I’ve already described what it does, you can quite easily see which bit does what and it’s, you know, fine. But it’s a bit of long, and it’s kind of boring.
So late on, after I’d started drafting what you’re reading now, I had another go at it, gathering up the bits of state into a little class, and now the file printing loop looks like this.
print.cpp
class page_printer {
// ...
size_t print() {
while(input_available()) {
if (at_page_start())
print_header();
print_line();
if (at_last_line())
print_footer();
} // while ...
pad_to_page_end();
return page_count_;
} // print
// ...
};