[BACK]Return to roff_int.h CVS log [TXT][DIR] Up to [cvsweb.bsd.lv] / mandoc

File: [cvsweb.bsd.lv] / mandoc / roff_int.h (download)

Revision 1.19, Thu May 19 15:37:47 2022 UTC (22 months, 1 week ago) by schwarze
Branch: MAIN
Changes since 1.18: +3 -1 lines

Make roff_expand() parse left-to-right rather than right-to-left.
Some escape sequences have side effects on global state, implying
that the order of evaluation matters.  For example, this fixes the
long-standing bug that "\n+x\n+x\n+x" after ".nr x 0 1" used to
print "321"; now it correctly prints "123".

Right-to-left parsing was convenient because it implicitly handled
nested escape sequences.  With correct left-to-right parsing, nesting
now requires an explicit implementation, here solved as follows:
1. Handle nested expanding escape sequences iteratively.
When finding one, expand it, then retry parsing the enclosing escape
sequence from the beginning, which will ultimately succeed as soon
as it no longer contains any nested expanding escape sequences.
2. Handle nested non-expanding escape sequences recursively.
When finding one, the escape sequence parser calls itself to find
the end of the inner sequence, then continues parsing the outer
sequence after that point.

This requires the mandoc_escape() function to operate in two different
modes.  The roff(7) parser uses it in a mode where it generates
diagnostics and may return an expansion request instead of a parse
result.  All other callers, in particular the formatters, use it
in a simpler mode that never generates diagnostics and always returns
a definite parsing result, but that requires all expanding escape
sequences to already have been expanded earlier.  The bulk of the
code is the same for both modes.
Since this required a major rewrite of the function anyway, move
it into its own new file roff_escape.c and out of the file mandoc.c,
which was misnamed in the first place and lacks a clear focus.

As a side benefit, this also fixes a number of assertion failures
that tb@ found with afl(1), for example "\n\\\\*0", "\v\-\\*0",
and "\w\-\\\\\$0*0".

As another side benefit, it also resolves some code duplication
between mandoc_escape() and roff_expand() and centralizes all
handling of escape sequences (except for expansion) in roff_escape.c,
hopefully easing maintenance and feature improvements in the future.

While here, also move end-of-input handling out of the complicated
function roff_expand() and into the simpler function roff_parse_comment(),
making the logic easier to understand.

Since this is a major reorganization of a central component of
mandoc(1), stability of the program might slightly suffer for a few
weeks, but i believe that's not a problem at this point of the
release cycle.  The new code already satisfies the regression suite,
but more tweaking and regression testing to further improve the
handling of various escape sequences will likely follow in the near
future.

/* $OpenBSD: roff_int.h,v 1.16 2019/01/05 00:36:46 schwarze Exp $	*/
/*
 * Copyright (c) 2013-2015, 2017-2022 Ingo Schwarze <schwarze@openbsd.org>
 * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Parser internals shared by multiple parsers.
 */

struct	ohash;
struct	roff_node;
struct	roff_meta;
struct	roff;
struct	mdoc_arg;

enum	roff_next {
	ROFF_NEXT_SIBLING = 0,
	ROFF_NEXT_CHILD
};

struct	roff_man {
	struct roff_meta  meta;    /* Public parse results. */
	struct roff	 *roff;    /* Roff parser state data. */
	struct ohash	 *mdocmac; /* Mdoc macro lookup table. */
	struct ohash	 *manmac;  /* Man macro lookup table. */
	const char	 *os_s;    /* Default operating system. */
	char	 	 *os_r;    /* Operating system name at run time. */
	struct roff_node *last;    /* The last node parsed. */
	struct roff_node *last_es; /* The most recent Es node. */
	int		  quick;   /* Abort parse early. */
	int		  flags;   /* Parse flags. */
#define	ROFF_NOFILL	 (1 << 1)  /* Fill mode switched off. */
#define	MDOC_PBODY	 (1 << 2)  /* In the document body. */
#define	MDOC_NEWLINE	 (1 << 3)  /* First macro/text in a line. */
#define	MDOC_PHRASE	 (1 << 4)  /* In a Bl -column phrase. */
#define	MDOC_PHRASELIT	 (1 << 5)  /* Literal within a phrase. */
#define	MDOC_FREECOL	 (1 << 6)  /* `It' invocation should close. */
#define	MDOC_SYNOPSIS	 (1 << 7)  /* SYNOPSIS-style formatting. */
#define	MDOC_KEEP	 (1 << 8)  /* In a word keep. */
#define	MDOC_SMOFF	 (1 << 9)  /* Spacing is off. */
#define	MDOC_NODELIMC	 (1 << 10) /* Disable closing delimiter handling. */
#define	MAN_ELINE	 (1 << 11) /* Next-line element scope. */
#define	MAN_BLINE	 (1 << 12) /* Next-line block scope. */
#define	MDOC_PHRASEQF	 (1 << 13) /* Quote first word encountered. */
#define	MDOC_PHRASEQL	 (1 << 14) /* Quote last word of this phrase. */
#define	MDOC_PHRASEQN	 (1 << 15) /* Quote first word of the next phrase. */
#define	ROFF_NONOFILL	 (1 << 16) /* Temporarily suspend no-fill mode. */
#define	MAN_NEWLINE	  MDOC_NEWLINE
	enum roff_sec	  lastsec; /* Last section seen. */
	enum roff_sec	  lastnamed; /* Last standard section seen. */
	enum roff_next	  next;    /* Where to put the next node. */
	char		  filesec; /* Section digit in the file name. */
};


struct roff_node *roff_node_alloc(struct roff_man *, int, int,
			enum roff_type, int);
void		  roff_node_append(struct roff_man *, struct roff_node *);
void		  roff_word_alloc(struct roff_man *, int, int, const char *);
void		  roff_word_append(struct roff_man *, const char *);
void		  roff_elem_alloc(struct roff_man *, int, int, int);
struct roff_node *roff_block_alloc(struct roff_man *, int, int, int);
struct roff_node *roff_head_alloc(struct roff_man *, int, int, int);
struct roff_node *roff_body_alloc(struct roff_man *, int, int, int);
void		  roff_node_unlink(struct roff_man *, struct roff_node *);
void		  roff_node_relink(struct roff_man *, struct roff_node *);
void		  roff_node_free(struct roff_node *);
void		  roff_node_delete(struct roff_man *, struct roff_node *);

struct ohash	 *roffhash_alloc(enum roff_tok, enum roff_tok);
enum roff_tok	  roffhash_find(struct ohash *, const char *, size_t);
void		  roffhash_free(struct ohash *);

enum mandoc_esc	  roff_escape(const char *, const int, const int,
			int *, int *, int *, int *);
void		  roff_state_reset(struct roff_man *);
void		  roff_validate(struct roff_man *);

/*
 * Functions called from roff.c need to be declared here,
 * not in libmdoc.h or libman.h, even if they are specific
 * to either the mdoc(7) or the man(7) parser.
 */

void		  man_breakscope(struct roff_man *, int);
void		  mdoc_argv_free(struct mdoc_arg *);