groff
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Groff] PDFPIC macro


From: Ralph Corderoy
Subject: Re: [Groff] PDFPIC macro
Date: Tue, 30 Sep 2014 12:23:00 +0100

Hi Keith,

First off, thanks for putting the effort in;  you're one of the few
who's doing that and code wins over codeless argument.  That said, I've
some complaints that may sway your style.  :-)

In general, I dislike the verbose comments, both in number and
wordiness.  Also, vertical space is precious as it allows me to glance
up and down without the distraction of scrolling;  the many empty `//'
comments waste this and pull the comment from its subject line.

Yes, comments are useful, as Werner said, but we've better control-flow
structures than early Fortran, break and continue are welcome, a
function need not clutter its control flow by steering towards a single
return, goto is even useful in common idioms.  And we're not limited to
two-letter identifiers like troff, so comments are not as much needed as
they were.  Especially with well-chosen identifiers;  I find
context_args() non-obvious.

Taking a bit at random...

>     // psbb_locator class constructor.
>     //

Not needed.

>     psbb_locator::psbb_locator(const char *fname):
>     filename(fname), llx(0), lly(0), urx(0), ury(0), lastc(EOF)
>     {
>       // PS files might contain non-printable characters, such as ^Z
>       // and CRs not followed by an LF, so open them in binary mode.
>       //
>       fp = include_search_path.open_file_cautious(filename, 0, FOPEN_RB);

We know it's a PostScript file.  More succinctly,

        // Binary mode as, for example, ^Z and CR with LF are valid.
        fp = include_search_path.open_file_cautious(filename, 0, FOPEN_RB);

>       if (fp) {
>         // After successfully opening the file, acquire the first
>         // line, whence we may determine the file format...
>         //
>         if (get_line(DSC_LINE_MAX_ENFORCE) == 0)

This is describing straightforward control flow.  `if fp' means we
successfully opened the file;  the reader is expect to know that.  He
should also know that what happens in the then-block is what happens
next, `After' not needed.  And what happens next it to get_line(), which
is to get a line;  it's not surprising that it's the first line as we've
just opened the file.

>           //
>           // ...except in the case of an empty file, which we are
>           // unable to process further.
>           //
>           error("`%1' is empty", filename);

The textual error message tells me what the problem is.  I don't need
that comment;  or if I do then the error message is unclear to the
reader of the code and the user that experiences it.  :-) Control flow
shows it's not processed further, *if* I follow through all the rest of
the function.

BTW, I dislike having to mentally stack many if-conditions simply so all
the elses do a simple thing, like error().  Especially when those elses
are off-screen.  I have to stack them to see if an else is present each
time and what it's the else of.  And that means moving to the matching
brace, which is off-screen because too much vertical space is wasted.  I
think I mentioned that.  :-)

Here's a non-compiled attempt at that function.  I don't suppose you'll
favour it any more than I'm tempted to write in your style, but I hope
it gets my points across.

    psbb_locator::psbb_locator(const char *fname):
    filename(fname), llx(0), lly(0), urx(0), ury(0), lastc(EOF)
    {
        // Binary mode as, for example, ^Z and CR with LF are valid.
        fp = include_search_path.open_file_cautious(filename, 0, FOPEN_RB);
        if (!fp) {
            error("can't open `%1': %2", filename, strerror(errno));
            goto done;
        }

        if (!get_line(DSC_LINE_MAX_ENFORCE)) {
            error("`%1' is empty", filename);
            goto done;
        }

        if (!expect("%!PS-Adobe-")) {
            error("`%1' does not conform to the Document Structuring 
Conventions",
                  filename);
            goto done;
        }

        const char *s = NULL;
        while (get_header_comment() && !(s = match("%%BoundingBox:")))
            ;
        if (!s) {
            error("%%%%BoundingBox comment not found in headers: `%1'", 
filename);
            goto done;
        }

        int status = parse_bounding_box(s);
        if (status == PSBB_RANGE_IS_SET)
            goto done;
        if (status == PSBB_RANGE_IS_BAD)
            goto bad_range;

        // status is PSBB_RANGE_AT_END, retrieve and check that one.
        if (!skip_to_trailer()) {
            error("%%%%BoundingBox at end, but no %%%%Trailer: `%1'", filename);
            goto done;
        }

        s = NULL;
        while (get_line(DSC_LINE_MAX_ENFORCE) && !(s = match("%%BoundingBox:")))
            ;
        if (!s) {
            error("%%%%BoundingBox comment not found at end: `%1'", filename);
            goto done;
        }

        status = parse_bounding_box(s);
        if (status == PSBB_RANGE_IS_SET)
            goto done;
        if (status == PSBB_RANGE_AT_END) {
            error("%%%%BoundingBox in %%%%Trailer also says at end: `%1'", 
filename);
            goto done;
        }
        // status must be...

    bad_range:
        error("invalid arguments to %%%%BoundingBox in `%1': %2", filename, s);

    done:
        if (fp)
            fclose(fp);
        assign_registers();   // Use defaults if no valid %%BoundingBox.
    }

Yes, I have to scroll down almost immediately to find `done', but it's
short, I can quickly glean it's purpose, and all the other times I come
across it, I know it's clean-up on function exit without having to
look again;  it's not much to remember compared to all those if-elses.

> +inline int psbb_locator::skip_to_trailer(void)
> +{
> +  // Begin by considering a chunk of the input file starting 512 bytes
> +  // before its end, and search it for a "%%Trailer" comment; if none is
> +  // found, incrementally double the chunk size while it remains within
> +  // a 32768L byte range, and search again...
> +  //
> +  for (ssize_t offset = 512L; offset > 0L; offset <<= 1) {

This scans the last 512, 1KiB, 2KiB, 4KiB, 8KiB, 16KiB, and 32KiB of the
file before giving up, after 63KiB of work, and scanning the whole file.
Rather than keep scanning over old ground, could it not just start 32KiB
from the end?

Cheers, Ralph.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]