aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorIngo Schwarze <schwarze@openbsd.org>2018-04-09 02:31:42 +0000
committerIngo Schwarze <schwarze@openbsd.org>2018-04-09 02:31:42 +0000
commit5aacba9ce0d0de6d9276777e5f2b193b60604d5f (patch)
tree3381e307653d2e733dd5fb397eb9d1f67c13244f
parent075c90103abe758895ec66087c805141bc329824 (diff)
downloadmandoc-5aacba9ce0d0de6d9276777e5f2b193b60604d5f.tar.gz
mandoc-5aacba9ce0d0de6d9276777e5f2b193b60604d5f.tar.zst
mandoc-5aacba9ce0d0de6d9276777e5f2b193b60604d5f.zip
Using an undefined string or macro will cause it to be defined as empty.
Observed by Werner Lemberg on Nov 14, 2011 and rotting on my TODO list ever since.
-rw-r--r--TODO6
-rw-r--r--regress/roff/string/Makefile4
-rw-r--r--regress/roff/string/undef.in69
-rw-r--r--regress/roff/string/undef.out_ascii37
-rw-r--r--regress/roff/string/undef.out_lint2
-rw-r--r--roff.c125
6 files changed, 193 insertions, 50 deletions
diff --git a/TODO b/TODO
index 0128fc3d..4d74fd06 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,6 @@
************************************************************************
* Official mandoc TODO.
-* $Id: TODO,v 1.248 2017/10/28 16:20:24 schwarze Exp $
+* $Id: TODO,v 1.249 2018/04/09 02:31:42 schwarze Exp $
************************************************************************
Many issues are annotated for difficulty as follows:
@@ -87,10 +87,6 @@ are mere guesses, and some may be wrong.
Found by naddy@ in devel/cutils cobfusc(1) Mon, 16 Feb 2015 19:10:52 +0100
loc *** exist *** algo *** size ** imp *
-- using undefined strings or macros defines them to be empty
- wl@ Mon, 14 Nov 2011 14:37:01 +0000
- loc * exist * algo * size * imp *
-
--- missing mdoc features ----------------------------------------------
- .Bl -column .Xo support is missing
diff --git a/regress/roff/string/Makefile b/regress/roff/string/Makefile
index 354a7a75..eda8c8ad 100644
--- a/regress/roff/string/Makefile
+++ b/regress/roff/string/Makefile
@@ -1,7 +1,7 @@
# $OpenBSD: Makefile,v 1.6 2014/07/06 19:08:57 schwarze Exp $
-REGRESS_TARGETS = escape infinite name std zerolength
-LINT_TARGETS = name std
+REGRESS_TARGETS = escape infinite name std undef zerolength
+LINT_TARGETS = name std undef
# The infinite test fails badly with groff-1.20.1:
# It fails to print the following text.
diff --git a/regress/roff/string/undef.in b/regress/roff/string/undef.in
new file mode 100644
index 00000000..d84a67c6
--- /dev/null
+++ b/regress/roff/string/undef.in
@@ -0,0 +1,69 @@
+.\" $OpenBSD: std.in,v 1.2 2017/07/04 14:53:27 schwarze Exp $
+.TH STRING-UNDEF 1 "April 9, 2018"
+.SH NAME
+string-undef - expanding undefined strings
+.SH DESCRIPTION
+.SS User defined string
+The sting "mys" is
+.ie dmys defined
+.el undefined
+and remains
+.ie dmys defined.
+.el undefined.
+.PP
+Its value is "\*[mys]", and now it is
+.ie dmys defined,
+.el undefined,
+and its value is still "\*[mys]".
+.PP
+.ds mys newval
+After redefining it to "\*[mys]", it is of course still
+.ie dmys defined.
+.el undefined.
+.PP
+.rm mys
+After removing the definition, it is now
+.ie dmys defined.
+.el undefined.
+.SS User defined macro
+The macro "mym" is
+.ie dmym defined.
+.el undefined.
+.PP
+It has no effect:
+.mym
+But now it is
+.ie dmym defined.
+.el undefined.
+.PP
+.de mym
+neweffect
+..
+After defining it as:
+.mym
+it is of course still
+.ie dmym defined.
+.el undefined.
+.PP
+.rm mym
+After removing the definition, it is now
+.ie dmym defined.
+.el undefined.
+.SS Renamed macro
+The standard .BR macro is
+.ie dBR defined,
+.el undefined,
+and it
+.BR works .
+.PP
+.rn BR newBR
+After renaming it, the new name is
+.ie dnewBR defined,
+.el undefined,
+and
+.newBR works .
+.SS Predefined string
+A predefined string is
+.ie dR defined
+.el undefined
+and has the value "\*R".
diff --git a/regress/roff/string/undef.out_ascii b/regress/roff/string/undef.out_ascii
new file mode 100644
index 00000000..6e7bd01f
--- /dev/null
+++ b/regress/roff/string/undef.out_ascii
@@ -0,0 +1,37 @@
+STRING-UNDEF(1) General Commands Manual STRING-UNDEF(1)
+
+
+
+NNAAMMEE
+ string-undef - expanding undefined strings
+
+DDEESSCCRRIIPPTTIIOONN
+ UUsseerr ddeeffiinneedd ssttrriinngg
+ The sting "mys" is undefined and remains undefined.
+
+ Its value is "", and now it is defined, and its value is still "".
+
+ After redefining it to "newval", it is of course still defined.
+
+ After removing the definition, it is now undefined.
+
+ UUsseerr ddeeffiinneedd mmaaccrroo
+ The macro "mym" is undefined.
+
+ It has no effect: But now it is defined.
+
+ After defining it as: neweffect it is of course still defined.
+
+ After removing the definition, it is now undefined.
+
+ RReennaammeedd mmaaccrroo
+ The standard .BR macro is defined, and it wwoorrkkss.
+
+ After renaming it, the new name is defined, and wwoorrkkss.
+
+ PPrreeddeeffiinneedd ssttrriinngg
+ A predefined string is defined and has the value "(R)".
+
+
+
+OpenBSD April 9, 2018 STRING-UNDEF(1)
diff --git a/regress/roff/string/undef.out_lint b/regress/roff/string/undef.out_lint
new file mode 100644
index 00000000..320ec604
--- /dev/null
+++ b/regress/roff/string/undef.out_lint
@@ -0,0 +1,2 @@
+mandoc: undef.in:14:15: WARNING: undefined string, using "": mys
+mandoc: undef.in:34:2: ERROR: skipping unknown macro: .mym
diff --git a/roff.c b/roff.c
index e2e498da..2a609eaf 100644
--- a/roff.c
+++ b/roff.c
@@ -1,7 +1,7 @@
-/* $Id: roff.c,v 1.324 2017/07/14 17:16:16 schwarze Exp $ */
+/* $Id: roff.c,v 1.325 2018/04/09 02:31:42 schwarze Exp $ */
/*
* Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2010-2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010-2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -46,6 +46,7 @@
#define ROFFDEF_STD (1 << 4) /* mdoc(7) or man(7) macro. */
#define ROFFDEF_ANY (ROFFDEF_USER | ROFFDEF_PRE | \
ROFFDEF_REN | ROFFDEF_STD)
+#define ROFFDEF_UNDEF (1 << 5) /* Completely undefined. */
/* --- data types --------------------------------------------------------- */
@@ -185,7 +186,7 @@ static int roff_getregn(const struct roff *,
const char *, size_t);
static int roff_getregro(const struct roff *,
const char *name);
-static const char *roff_getstrn(const struct roff *,
+static const char *roff_getstrn(struct roff *,
const char *, size_t, int *);
static int roff_hasregn(const struct roff *,
const char *, size_t);
@@ -1639,6 +1640,11 @@ roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos)
}
if (t != TOKEN_NONE)
*pos = cp - buf;
+ else if (deftype == ROFFDEF_UNDEF) {
+ /* Using an undefined macro defines it to be empty. */
+ roff_setstrn(&r->strtab, mac, maclen, "", 0, 0);
+ roff_setstrn(&r->rentab, mac, maclen, NULL, 0, 0);
+ }
return t;
}
@@ -3537,62 +3543,95 @@ roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
}
static const char *
-roff_getstrn(const struct roff *r, const char *name, size_t len,
+roff_getstrn(struct roff *r, const char *name, size_t len,
int *deftype)
{
const struct roffkv *n;
- int i;
+ int found, i;
enum roff_tok tok;
- if (*deftype & ROFFDEF_USER) {
- for (n = r->strtab; n != NULL; n = n->next) {
- if (strncmp(name, n->key.p, len) == 0 &&
- n->key.p[len] == '\0' &&
- n->val.p != NULL) {
- *deftype = ROFFDEF_USER;
- return n->val.p;
- }
+ found = 0;
+ for (n = r->strtab; n != NULL; n = n->next) {
+ if (strncmp(name, n->key.p, len) != 0 ||
+ n->key.p[len] != '\0' || n->val.p == NULL)
+ continue;
+ if (*deftype & ROFFDEF_USER) {
+ *deftype = ROFFDEF_USER;
+ return n->val.p;
+ } else {
+ found = 1;
+ break;
}
}
- if (*deftype & ROFFDEF_PRE) {
- for (i = 0; i < PREDEFS_MAX; i++) {
- if (strncmp(name, predefs[i].name, len) == 0 &&
- predefs[i].name[len] == '\0') {
- *deftype = ROFFDEF_PRE;
- return predefs[i].str;
- }
+ for (n = r->rentab; n != NULL; n = n->next) {
+ if (strncmp(name, n->key.p, len) != 0 ||
+ n->key.p[len] != '\0' || n->val.p == NULL)
+ continue;
+ if (*deftype & ROFFDEF_REN) {
+ *deftype = ROFFDEF_REN;
+ return n->val.p;
+ } else {
+ found = 1;
+ break;
}
}
- if (*deftype & ROFFDEF_REN) {
- for (n = r->rentab; n != NULL; n = n->next) {
- if (strncmp(name, n->key.p, len) == 0 &&
- n->key.p[len] == '\0' &&
- n->val.p != NULL) {
- *deftype = ROFFDEF_REN;
- return n->val.p;
- }
+ for (i = 0; i < PREDEFS_MAX; i++) {
+ if (strncmp(name, predefs[i].name, len) != 0 ||
+ predefs[i].name[len] != '\0')
+ continue;
+ if (*deftype & ROFFDEF_PRE) {
+ *deftype = ROFFDEF_PRE;
+ return predefs[i].str;
+ } else {
+ found = 1;
+ break;
}
}
- if (*deftype & ROFFDEF_STD) {
- if (r->man->macroset != MACROSET_MAN) {
- for (tok = MDOC_Dd; tok < MDOC_MAX; tok++) {
- if (strncmp(name, roff_name[tok], len) == 0 &&
- roff_name[tok][len] == '\0') {
- *deftype = ROFFDEF_STD;
- return NULL;
- }
+ if (r->man->macroset != MACROSET_MAN) {
+ for (tok = MDOC_Dd; tok < MDOC_MAX; tok++) {
+ if (strncmp(name, roff_name[tok], len) != 0 ||
+ roff_name[tok][len] != '\0')
+ continue;
+ if (*deftype & ROFFDEF_STD) {
+ *deftype = ROFFDEF_STD;
+ return NULL;
+ } else {
+ found = 1;
+ break;
}
}
- if (r->man->macroset != MACROSET_MDOC) {
- for (tok = MAN_TH; tok < MAN_MAX; tok++) {
- if (strncmp(name, roff_name[tok], len) == 0 &&
- roff_name[tok][len] == '\0') {
- *deftype = ROFFDEF_STD;
- return NULL;
- }
+ }
+ if (r->man->macroset != MACROSET_MDOC) {
+ for (tok = MAN_TH; tok < MAN_MAX; tok++) {
+ if (strncmp(name, roff_name[tok], len) != 0 ||
+ roff_name[tok][len] != '\0')
+ continue;
+ if (*deftype & ROFFDEF_STD) {
+ *deftype = ROFFDEF_STD;
+ return NULL;
+ } else {
+ found = 1;
+ break;
}
}
}
+
+ if (found == 0 && *deftype != ROFFDEF_ANY) {
+ if (*deftype & ROFFDEF_REN) {
+ /*
+ * This might still be a request,
+ * so do not treat it as undefined yet.
+ */
+ *deftype = ROFFDEF_UNDEF;
+ return NULL;
+ }
+
+ /* Using an undefined string defines it to be empty. */
+
+ roff_setstrn(&r->strtab, name, len, "", 0, 0);
+ roff_setstrn(&r->rentab, name, len, NULL, 0, 0);
+ }
+
*deftype = 0;
return NULL;
}