Skip to content

Commit

Permalink
Improve Mac OS X buggy SH handling.
Browse files Browse the repository at this point in the history
This allows any content under an SH that is either normal text
or entities to be serialised on the same line as SH instead of
the subsequent line.

Properly call this "headerhack" to point out that this is not
because of lowdown or man(7), but other buggy software.

References #138
  • Loading branch information
kristapsdz committed Sep 15, 2024
1 parent fdef5af commit 9906f8c
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 20 deletions.
51 changes: 31 additions & 20 deletions nroff.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ struct bnode {
char *args; /* (unsafe) 2nd args */
int close; /* BNODE_COLOUR/FONT */
int tblhack; /* BSCOPE_SPAN */
int headerhack; /* BSCOPE_BLOCK */
enum bscope scope; /* scope */
unsigned int font; /* if BNODE_FONT */
#define BFONT_ITALIC 0x01
Expand All @@ -84,7 +85,6 @@ struct bnode {
unsigned int colour; /* if BNODE_COLOUR */
#define BFONT_BLUE 0x01
#define BFONT_RED 0x02
int trailingspace; /* BSCOPE_BLOCK .SH */
TAILQ_ENTRY(bnode) entries;
};

Expand Down Expand Up @@ -517,8 +517,18 @@ bqueue_flush(const struct nroff *st, struct lowdown_buf *ob,
return 0;
}

/* Finally, trailing newline/space. */
trailingchar = bn->trailingspace ? ' ' : '\n';
/*
* The "headerhack" is used by SH block macros to
* indicate that their children are all normal text or
* entities, and so to output the content on the same
* line as the SH macro. This is a hack because man(7)
* specifically allows for next-line content; however,
* buggy software (e.g., Mac OS X's makewhatis) don't
* correctly parse empty SH properly.
*/

trailingchar = bn->headerhack ? ' ' : '\n';

if (nextblk && ob->size > 0 &&
ob->data[ob->size - 1] != trailingchar &&
!hbuf_putc(ob, trailingchar))
Expand Down Expand Up @@ -842,7 +852,7 @@ rndr_header(struct nroff *st, struct bnodeq *obq,
struct lowdown_buf *buf = NULL;
const struct lowdown_buf *nbuf;
int rc = 0;
const struct lowdown_node *child = NULL;
const struct lowdown_node *child;

level = (ssize_t)n->rndr_header.level + st->headers_offs;
if (level < 1)
Expand All @@ -861,23 +871,24 @@ rndr_header(struct nroff *st, struct bnodeq *obq,
if (bn == NULL)
return 0;

if (level == 1
&& (child = TAILQ_FIRST(&n->children))
&& !TAILQ_NEXT(child, entries)) {
/* This is a first-level header and there is exactly one child.
*
* If the child is a `LOWDOWN_NORMAL_TEXT`, put
* a single space between the `.SH` and the
* heading instead of a newline.
*
* This renders headers as (e.g.) `.SH
* SYNOPSIS` instead of `.SH\nSYNOPSIS`, which
* is important for macOS `makewhatis`.
*
* See: https://github.com/kristapsdz/lowdown/pull/138
*/
bn->trailingspace = child->type == LOWDOWN_NORMAL_TEXT;
/*
* Do a scan of the contents of the SH. If there's only
* normal text (or entities), then record this fact. It
* will be used in bqueue_flush() for how the macro is
* serialised.
*/

if (level == 1) {
bn->headerhack = 1;
TAILQ_FOREACH(child, &n->children, entries) {
if (child->type == LOWDOWN_ENTITY ||
child->type == LOWDOWN_NORMAL_TEXT)
continue;
bn->headerhack = 0;
break;
}
}

TAILQ_CONCAT(obq, bq, entries);
st->post_para = 1;
return 1;
Expand Down
22 changes: 22 additions & 0 deletions regress/header-buggy-makewhatis.man
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.SH Same line as SH.
.LP
a
.SS
Not same line as SS.
.LP
b
.SH Same \(lqline\(rq.
.LP
c
.SH
Not same \fBline\fR <\fIfoo.com\fR>
.LP
d
.SH
Not same \fIline\fR.
.LP
e
.SH
Not same \fBline\fR.
.LP
e
12 changes: 12 additions & 0 deletions regress/header-buggy-makewhatis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Same line as SH.
a
## Not same line as SS.
b
# Same "line".
c
# Not same [line](foo.com)
d
# Not same *line*.
e
# Not same **line**.
e

0 comments on commit 9906f8c

Please sign in to comment.