//
// aegis - project change supervisor
// Copyright (C) 1999-2006, 2008, 2012 Peter Miller
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see
// .
//
#include
#include // for sprintf
#include
#include
#include
#include
#include
#include // for DEFAULT_PRINTER_LENGTH
#include
#include
#include
#include
#include
//
// This is how you figure out whether you are talking to a printer,
// or to a terminal.
//
#define PRINTER_THRESHOLD (DEFAULT_PRINTER_LENGTH / 2)
// assert(PRINTER_THRESHOLD > DEFAULT_PAGE_LENGTH);
wide_output_header::~wide_output_header()
{
trace(("wide_output_header::~wide_output_header(this = %p)\n{\n",
this));
flush();
trace(("}\n"));
}
wide_output_header::wide_output_header(const wide_output::pointer &a_deeper) :
deeper(a_deeper),
line_number(0),
length(deeper->page_length()),
width(deeper->page_width()),
is_a_printer(length > PRINTER_THRESHOLD),
column(0),
page_number(0),
page_time(now()),
already_top_diverted(false)
{
if (is_a_printer)
{
//
// Three blank lines at top (avoid perforation)
// two title lines,
// one blank line before columns.
// Plus, 3 blank lines at bottom (avoid perforation).
//
length -= 9;
}
else if (length > 5)
{
//
// One blank line at top,
// two title lines,
// one blank line before columns.
// Leave the last line for the pager
//
length -= 5;
}
else
length = 1;
trace(("}\n"));
}
wide_output_header::hpointer
wide_output_header::open(const wide_output::pointer &a_deeper)
{
trace(("wide_output_header::open(deeper = %p)\n", a_deeper.get()));
return hpointer(new wide_output_header(a_deeper));
}
nstring
wide_output_header::filename()
{
return deeper->filename();
}
static wstring
wstr_subset_of_max_width(const wstring &wis, int len)
{
const wchar_t *begin = wis.c_str();
const wchar_t *s = begin;
int s_wid = 0;
while (*s)
{
int c_wid = s_wid + wcwidth(*s);
if (c_wid > len)
break;
++s;
s_wid = c_wid;
}
return wstring(begin, s - begin);
}
static wstring
wstr_spaces(int n)
{
trace(("mark\n"));
static wstring_accumulator buffer;
buffer.clear();
while (n > 0)
{
buffer.push_back(L' ');
--n;
}
return buffer.mkstr();
}
void
wide_output_header::left_and_right(const wstring &lhs, const char *rhs)
{
trace(("wide_output_header::left_and_right()\n{\n"));
wstring tmp3(rhs);
trace(("mark\n"));
int w3 = tmp3.column_width();
int frac = width - 2 - w3;
trace(("mark\n"));
wstring tmp1 = wstr_subset_of_max_width(lhs, frac);
trace(("mark\n"));
frac = width - tmp1.column_width() - w3;
trace(("frac = %d\n", frac));
wstring tmp2 = wstr_spaces(frac);
trace(("mark\n"));
deeper->write(tmp1);
deeper->write(tmp2);
deeper->write(tmp3);
deeper->put_wc(L'\n');
trace(("}\n"));
}
void
wide_output_header::top_of_page_processing()
{
//
// advance the page number
//
if (already_top_diverted)
return;
already_top_diverted = true;
trace(("wide_output_header::top_of_page(this = %p)\n{\n", this));
++page_number;
//
// Blank line(s) before the title
//
if (is_a_printer)
{
if (page_number > 1)
deeper->put_wc(L'\f');
deeper->put_wc(L'\n');
deeper->put_wc(L'\n');
}
deeper->put_wc(L'\n');
//
// first line of titles
//
trace(("mark\n"));
char tmp1[30];
snprintf(tmp1, sizeof(tmp1), "Page %d", page_number);
left_and_right(title1, tmp1);
//
// second line of titles
//
trace(("mark\n"));
snprintf(tmp1, sizeof(tmp1), "%.24s", ctime(&page_time));
left_and_right(title2, tmp1);
//
// blank line between titles and columns
//
trace(("mark\n"));
deeper->put_wc(L'\n');
already_top_diverted = false;
trace(("}\n"));
}
void
wide_output_header::bottom_of_page_processing()
{
trace(("bottom of page\n"));
//
// Flush the output here. This means that the last page will be
// available on the output immediately. This can be important
// for reports and lists which take a long to to generate.
//
deeper->flush();
}
void
wide_output_header::write_inner(const wchar_t *data, size_t len)
{
//
// If we see a form-feed, advance to the next page.
// Do this by inserting the right number of newlines.
// By doing it first, by recursion, the rest of the unexceptional
// processing simply falls out cleanly.
//
trace(("wide_output_header::write_inner(this = %p, data = %p, "
"len = %ld)\n{\n", this, data, (long)len));
while (len > 0)
{
wchar_t wc = *data++;
--len;
if (wc == L'\f')
{
//
// If we are at the top of the page,
// emit the page header.
//
if
(
line_number == 0
&&
column == 0
&&
!already_top_diverted
)
top_of_page_processing();
for (;;)
{
//
// Forward the character to the output.
// Keep track of the column for eoln()
// purposes; it doesn't need to be exact,
// so we aren't using wcwidth.
//
deeper->put_wc(L'\n');
//
// Keep track of the line number so
// that we can work out when we get to
// the end of the page (and implicitly,
// the top of the next page).
//
column = 0;
++line_number;
trace(("linum = %d;\n", line_number));
if (line_number >= length)
{
bottom_of_page_processing();
line_number = 0;
break;
}
}
continue;
}
//
// If we are at the top of the page,
// emit the page header.
//
if (line_number == 0 && column == 0 && !already_top_diverted)
top_of_page_processing();
//
// Forward the character to the output.
// Keep track of the column for eoln() purposes;
// it doesn't need to be exact, so we aren't using wcwidth.
//
deeper->put_wc(wc);
++column;
//
// If the character was a newline, keep track of the line number
// so that we can work out when we get to the end of the page
// (and implicitly, the top of the next page).
//
if (wc == L'\n')
{
column = 0;
++line_number;
trace(("line_number = %d;\n", line_number));
if (line_number >= length)
{
bottom_of_page_processing();
line_number = 0;
}
}
}
trace(("}\n"));
}
int
wide_output_header::page_width()
{
trace(("wide_output_header::page_width(this = %p)\n", this));
return width;
}
void
wide_output_header::flush_inner()
{
trace(("wide_output_header::flush_inner(this = %p)\n{\n", this));
deeper->flush();
trace(("}\n"));
}
int
wide_output_header::page_length()
{
trace(("wide_output_header::page_length(this = %p)\n", this));
return length;
}
void
wide_output_header::end_of_line_inner()
{
trace(("wide_output_header::eoln_inner(this = %p)\n{\n", this));
if (column > 0)
put_wc(L'\n');
trace(("}\n"));
}
static wstring
censor(const nstring &s)
{
//
// convert to a wide string
//
wstring wis(s);
//
// make sure it doesn't have any unreasonable characters
//
language_human();
size_t j = 0;
for (; j < wis.size(); ++j)
if (!iswprint(wis.c_str()[j]))
break;
language_C();
//
// Truncate at the first unprintable character,
// if any are present
//
if (j < wis.size())
{
return wstring(wis.c_str(), j);
}
//
// return result
//
return wis;
}
void
wide_output_header::title(const nstring &t1, const nstring &t2)
{
trace(("wide_output_header::title(this = %p, %s, %s)\n{\n",
this, t1.quote_c().c_str(), t2.quote_c().c_str()));
title1 = censor(t1);
title2 = censor(t2);
trace(("}\n"));
}
void
wide_output_header::need(int nlines)
{
if (nlines <= 0)
return;
trace(("wide_output_header::need(this = %p, nlines = %d)\n{\n",
this, nlines));
flush();
if (line_number > 0 && line_number + nlines > length)
put_wc(L'\f');
trace(("}\n"));
}
void
wide_output_header::need1(int nlines)
{
if (nlines <= 0)
return;
trace(("wide_output_header::need1(this = %p, nlines = %d)\n{\n",
this, nlines));
flush();
if (line_number > 0)
{
if (line_number + nlines > length)
put_wc(L'\f');
else
put_wc(L'\n');
}
trace(("}\n"));
}
void
wide_output_header::eject()
{
trace(("wide_output_header::eject(this = %p)\n{\n", this));
if (column > 0)
put_wc(L'\n');
if (line_number > 0)
put_wc(L'\f');
trace(("}\n"));
}
bool
wide_output_header::is_at_top_of_page()
{
flush();
return (line_number == 0 && column == 0);
}
const char *
wide_output_header::type_name()
const
{
return "wide_output_header";
}
// vim: set ts=8 sw=4 et :