| @@ -1,430 +1,430 @@ | | | @@ -1,430 +1,430 @@ |
1 | /* Id: mdoc.c,v 1.274 2018/12/31 07:46:07 schwarze Exp */ | | 1 | /* Id: mdoc.c,v 1.274 2018/12/31 07:46:07 schwarze Exp */ |
2 | /* | | 2 | /* |
3 | * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> | | 3 | * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> |
4 | * Copyright (c) 2010, 2012-2018 Ingo Schwarze <schwarze@openbsd.org> | | 4 | * Copyright (c) 2010, 2012-2018 Ingo Schwarze <schwarze@openbsd.org> |
5 | * | | 5 | * |
6 | * Permission to use, copy, modify, and distribute this software for any | | 6 | * Permission to use, copy, modify, and distribute this software for any |
7 | * purpose with or without fee is hereby granted, provided that the above | | 7 | * purpose with or without fee is hereby granted, provided that the above |
8 | * copyright notice and this permission notice appear in all copies. | | 8 | * copyright notice and this permission notice appear in all copies. |
9 | * | | 9 | * |
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES | | 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES |
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | | 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR | | 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR |
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | | 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | | 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | | 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | | 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | */ | | 17 | */ |
18 | #include "config.h" | | 18 | #include "config.h" |
19 | | | 19 | |
20 | #include <sys/types.h> | | 20 | #include <sys/types.h> |
21 | | | 21 | |
22 | #include <assert.h> | | 22 | #include <assert.h> |
23 | #include <ctype.h> | | 23 | #include <ctype.h> |
24 | #include <stdarg.h> | | 24 | #include <stdarg.h> |
25 | #include <stdio.h> | | 25 | #include <stdio.h> |
26 | #include <stdlib.h> | | 26 | #include <stdlib.h> |
27 | #include <string.h> | | 27 | #include <string.h> |
28 | #include <time.h> | | 28 | #include <time.h> |
29 | | | 29 | |
30 | #include "mandoc_aux.h" | | 30 | #include "mandoc_aux.h" |
31 | #include "mandoc.h" | | 31 | #include "mandoc.h" |
32 | #include "roff.h" | | 32 | #include "roff.h" |
33 | #include "roff_int.h" | | | |
34 | #include "mdoc.h" | | 33 | #include "mdoc.h" |
35 | #include "libmandoc.h" | | 34 | #include "libmandoc.h" |
| | | 35 | #include "roff_int.h" |
36 | #include "libmdoc.h" | | 36 | #include "libmdoc.h" |
37 | | | 37 | |
38 | const char *const __mdoc_argnames[MDOC_ARG_MAX] = { | | 38 | const char *const __mdoc_argnames[MDOC_ARG_MAX] = { |
39 | "split", "nosplit", "ragged", | | 39 | "split", "nosplit", "ragged", |
40 | "unfilled", "literal", "file", | | 40 | "unfilled", "literal", "file", |
41 | "offset", "bullet", "dash", | | 41 | "offset", "bullet", "dash", |
42 | "hyphen", "item", "enum", | | 42 | "hyphen", "item", "enum", |
43 | "tag", "diag", "hang", | | 43 | "tag", "diag", "hang", |
44 | "ohang", "inset", "column", | | 44 | "ohang", "inset", "column", |
45 | "width", "compact", "std", | | 45 | "width", "compact", "std", |
46 | "filled", "words", "emphasis", | | 46 | "filled", "words", "emphasis", |
47 | "symbolic", "nested", "centered" | | 47 | "symbolic", "nested", "centered" |
48 | }; | | 48 | }; |
49 | const char * const *mdoc_argnames = __mdoc_argnames; | | 49 | const char * const *mdoc_argnames = __mdoc_argnames; |
50 | | | 50 | |
51 | static int mdoc_ptext(struct roff_man *, int, char *, int); | | 51 | static int mdoc_ptext(struct roff_man *, int, char *, int); |
52 | static int mdoc_pmacro(struct roff_man *, int, char *, int); | | 52 | static int mdoc_pmacro(struct roff_man *, int, char *, int); |
53 | | | 53 | |
54 | | | 54 | |
55 | /* | | 55 | /* |
56 | * Main parse routine. Parses a single line -- really just hands off to | | 56 | * Main parse routine. Parses a single line -- really just hands off to |
57 | * the macro (mdoc_pmacro()) or text parser (mdoc_ptext()). | | 57 | * the macro (mdoc_pmacro()) or text parser (mdoc_ptext()). |
58 | */ | | 58 | */ |
59 | int | | 59 | int |
60 | mdoc_parseln(struct roff_man *mdoc, int ln, char *buf, int offs) | | 60 | mdoc_parseln(struct roff_man *mdoc, int ln, char *buf, int offs) |
61 | { | | 61 | { |
62 | | | 62 | |
63 | if (mdoc->last->type != ROFFT_EQN || ln > mdoc->last->line) | | 63 | if (mdoc->last->type != ROFFT_EQN || ln > mdoc->last->line) |
64 | mdoc->flags |= MDOC_NEWLINE; | | 64 | mdoc->flags |= MDOC_NEWLINE; |
65 | | | 65 | |
66 | /* | | 66 | /* |
67 | * Let the roff nS register switch SYNOPSIS mode early, | | 67 | * Let the roff nS register switch SYNOPSIS mode early, |
68 | * such that the parser knows at all times | | 68 | * such that the parser knows at all times |
69 | * whether this mode is on or off. | | 69 | * whether this mode is on or off. |
70 | * Note that this mode is also switched by the Sh macro. | | 70 | * Note that this mode is also switched by the Sh macro. |
71 | */ | | 71 | */ |
72 | if (roff_getreg(mdoc->roff, "nS")) | | 72 | if (roff_getreg(mdoc->roff, "nS")) |
73 | mdoc->flags |= MDOC_SYNOPSIS; | | 73 | mdoc->flags |= MDOC_SYNOPSIS; |
74 | else | | 74 | else |
75 | mdoc->flags &= ~MDOC_SYNOPSIS; | | 75 | mdoc->flags &= ~MDOC_SYNOPSIS; |
76 | | | 76 | |
77 | return roff_getcontrol(mdoc->roff, buf, &offs) ? | | 77 | return roff_getcontrol(mdoc->roff, buf, &offs) ? |
78 | mdoc_pmacro(mdoc, ln, buf, offs) : | | 78 | mdoc_pmacro(mdoc, ln, buf, offs) : |
79 | mdoc_ptext(mdoc, ln, buf, offs); | | 79 | mdoc_ptext(mdoc, ln, buf, offs); |
80 | } | | 80 | } |
81 | | | 81 | |
82 | void | | 82 | void |
83 | mdoc_tail_alloc(struct roff_man *mdoc, int line, int pos, enum roff_tok tok) | | 83 | mdoc_tail_alloc(struct roff_man *mdoc, int line, int pos, enum roff_tok tok) |
84 | { | | 84 | { |
85 | struct roff_node *p; | | 85 | struct roff_node *p; |
86 | | | 86 | |
87 | p = roff_node_alloc(mdoc, line, pos, ROFFT_TAIL, tok); | | 87 | p = roff_node_alloc(mdoc, line, pos, ROFFT_TAIL, tok); |
88 | roff_node_append(mdoc, p); | | 88 | roff_node_append(mdoc, p); |
89 | mdoc->next = ROFF_NEXT_CHILD; | | 89 | mdoc->next = ROFF_NEXT_CHILD; |
90 | } | | 90 | } |
91 | | | 91 | |
92 | struct roff_node * | | 92 | struct roff_node * |
93 | mdoc_endbody_alloc(struct roff_man *mdoc, int line, int pos, | | 93 | mdoc_endbody_alloc(struct roff_man *mdoc, int line, int pos, |
94 | enum roff_tok tok, struct roff_node *body) | | 94 | enum roff_tok tok, struct roff_node *body) |
95 | { | | 95 | { |
96 | struct roff_node *p; | | 96 | struct roff_node *p; |
97 | | | 97 | |
98 | body->flags |= NODE_ENDED; | | 98 | body->flags |= NODE_ENDED; |
99 | body->parent->flags |= NODE_ENDED; | | 99 | body->parent->flags |= NODE_ENDED; |
100 | p = roff_node_alloc(mdoc, line, pos, ROFFT_BODY, tok); | | 100 | p = roff_node_alloc(mdoc, line, pos, ROFFT_BODY, tok); |
101 | p->body = body; | | 101 | p->body = body; |
102 | p->norm = body->norm; | | 102 | p->norm = body->norm; |
103 | p->end = ENDBODY_SPACE; | | 103 | p->end = ENDBODY_SPACE; |
104 | roff_node_append(mdoc, p); | | 104 | roff_node_append(mdoc, p); |
105 | mdoc->next = ROFF_NEXT_SIBLING; | | 105 | mdoc->next = ROFF_NEXT_SIBLING; |
106 | return p; | | 106 | return p; |
107 | } | | 107 | } |
108 | | | 108 | |
109 | struct roff_node * | | 109 | struct roff_node * |
110 | mdoc_block_alloc(struct roff_man *mdoc, int line, int pos, | | 110 | mdoc_block_alloc(struct roff_man *mdoc, int line, int pos, |
111 | enum roff_tok tok, struct mdoc_arg *args) | | 111 | enum roff_tok tok, struct mdoc_arg *args) |
112 | { | | 112 | { |
113 | struct roff_node *p; | | 113 | struct roff_node *p; |
114 | | | 114 | |
115 | p = roff_node_alloc(mdoc, line, pos, ROFFT_BLOCK, tok); | | 115 | p = roff_node_alloc(mdoc, line, pos, ROFFT_BLOCK, tok); |
116 | p->args = args; | | 116 | p->args = args; |
117 | if (p->args) | | 117 | if (p->args) |
118 | (args->refcnt)++; | | 118 | (args->refcnt)++; |
119 | | | 119 | |
120 | switch (tok) { | | 120 | switch (tok) { |
121 | case MDOC_Bd: | | 121 | case MDOC_Bd: |
122 | case MDOC_Bf: | | 122 | case MDOC_Bf: |
123 | case MDOC_Bl: | | 123 | case MDOC_Bl: |
124 | case MDOC_En: | | 124 | case MDOC_En: |
125 | case MDOC_Rs: | | 125 | case MDOC_Rs: |
126 | p->norm = mandoc_calloc(1, sizeof(union mdoc_data)); | | 126 | p->norm = mandoc_calloc(1, sizeof(union mdoc_data)); |
127 | break; | | 127 | break; |
128 | default: | | 128 | default: |
129 | break; | | 129 | break; |
130 | } | | 130 | } |
131 | roff_node_append(mdoc, p); | | 131 | roff_node_append(mdoc, p); |
132 | mdoc->next = ROFF_NEXT_CHILD; | | 132 | mdoc->next = ROFF_NEXT_CHILD; |
133 | return p; | | 133 | return p; |
134 | } | | 134 | } |
135 | | | 135 | |
136 | void | | 136 | void |
137 | mdoc_elem_alloc(struct roff_man *mdoc, int line, int pos, | | 137 | mdoc_elem_alloc(struct roff_man *mdoc, int line, int pos, |
138 | enum roff_tok tok, struct mdoc_arg *args) | | 138 | enum roff_tok tok, struct mdoc_arg *args) |
139 | { | | 139 | { |
140 | struct roff_node *p; | | 140 | struct roff_node *p; |
141 | | | 141 | |
142 | p = roff_node_alloc(mdoc, line, pos, ROFFT_ELEM, tok); | | 142 | p = roff_node_alloc(mdoc, line, pos, ROFFT_ELEM, tok); |
143 | p->args = args; | | 143 | p->args = args; |
144 | if (p->args) | | 144 | if (p->args) |
145 | (args->refcnt)++; | | 145 | (args->refcnt)++; |
146 | | | 146 | |
147 | switch (tok) { | | 147 | switch (tok) { |
148 | case MDOC_An: | | 148 | case MDOC_An: |
149 | p->norm = mandoc_calloc(1, sizeof(union mdoc_data)); | | 149 | p->norm = mandoc_calloc(1, sizeof(union mdoc_data)); |
150 | break; | | 150 | break; |
151 | default: | | 151 | default: |
152 | break; | | 152 | break; |
153 | } | | 153 | } |
154 | roff_node_append(mdoc, p); | | 154 | roff_node_append(mdoc, p); |
155 | mdoc->next = ROFF_NEXT_CHILD; | | 155 | mdoc->next = ROFF_NEXT_CHILD; |
156 | } | | 156 | } |
157 | | | 157 | |
158 | /* | | 158 | /* |
159 | * Parse free-form text, that is, a line that does not begin with the | | 159 | * Parse free-form text, that is, a line that does not begin with the |
160 | * control character. | | 160 | * control character. |
161 | */ | | 161 | */ |
162 | static int | | 162 | static int |
163 | mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs) | | 163 | mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs) |
164 | { | | 164 | { |
165 | struct roff_node *n; | | 165 | struct roff_node *n; |
166 | const char *cp, *sp; | | 166 | const char *cp, *sp; |
167 | char *c, *ws, *end; | | 167 | char *c, *ws, *end; |
168 | | | 168 | |
169 | n = mdoc->last; | | 169 | n = mdoc->last; |
170 | | | 170 | |
171 | /* | | 171 | /* |
172 | * If a column list contains plain text, assume an implicit item | | 172 | * If a column list contains plain text, assume an implicit item |
173 | * macro. This can happen one or more times at the beginning | | 173 | * macro. This can happen one or more times at the beginning |
174 | * of such a list, intermixed with non-It mdoc macros and with | | 174 | * of such a list, intermixed with non-It mdoc macros and with |
175 | * nodes generated on the roff level, for example by tbl. | | 175 | * nodes generated on the roff level, for example by tbl. |
176 | */ | | 176 | */ |
177 | | | 177 | |
178 | if ((n->tok == MDOC_Bl && n->type == ROFFT_BODY && | | 178 | if ((n->tok == MDOC_Bl && n->type == ROFFT_BODY && |
179 | n->end == ENDBODY_NOT && n->norm->Bl.type == LIST_column) || | | 179 | n->end == ENDBODY_NOT && n->norm->Bl.type == LIST_column) || |
180 | (n->parent != NULL && n->parent->tok == MDOC_Bl && | | 180 | (n->parent != NULL && n->parent->tok == MDOC_Bl && |
181 | n->parent->norm->Bl.type == LIST_column)) { | | 181 | n->parent->norm->Bl.type == LIST_column)) { |
182 | mdoc->flags |= MDOC_FREECOL; | | 182 | mdoc->flags |= MDOC_FREECOL; |
183 | (*mdoc_macro(MDOC_It)->fp)(mdoc, MDOC_It, | | 183 | (*mdoc_macro(MDOC_It)->fp)(mdoc, MDOC_It, |
184 | line, offs, &offs, buf); | | 184 | line, offs, &offs, buf); |
185 | return 1; | | 185 | return 1; |
186 | } | | 186 | } |
187 | | | 187 | |
188 | /* | | 188 | /* |
189 | * Search for the beginning of unescaped trailing whitespace (ws) | | 189 | * Search for the beginning of unescaped trailing whitespace (ws) |
190 | * and for the first character not to be output (end). | | 190 | * and for the first character not to be output (end). |
191 | */ | | 191 | */ |
192 | | | 192 | |
193 | /* FIXME: replace with strcspn(). */ | | 193 | /* FIXME: replace with strcspn(). */ |
194 | ws = NULL; | | 194 | ws = NULL; |
195 | for (c = end = buf + offs; *c; c++) { | | 195 | for (c = end = buf + offs; *c; c++) { |
196 | switch (*c) { | | 196 | switch (*c) { |
197 | case ' ': | | 197 | case ' ': |
198 | if (NULL == ws) | | 198 | if (NULL == ws) |
199 | ws = c; | | 199 | ws = c; |
200 | continue; | | 200 | continue; |
201 | case '\t': | | 201 | case '\t': |
202 | /* | | 202 | /* |
203 | * Always warn about trailing tabs, | | 203 | * Always warn about trailing tabs, |
204 | * even outside literal context, | | 204 | * even outside literal context, |
205 | * where they should be put on the next line. | | 205 | * where they should be put on the next line. |
206 | */ | | 206 | */ |
207 | if (NULL == ws) | | 207 | if (NULL == ws) |
208 | ws = c; | | 208 | ws = c; |
209 | /* | | 209 | /* |
210 | * Strip trailing tabs in literal context only; | | 210 | * Strip trailing tabs in literal context only; |
211 | * outside, they affect the next line. | | 211 | * outside, they affect the next line. |
212 | */ | | 212 | */ |
213 | if (mdoc->flags & ROFF_NOFILL) | | 213 | if (mdoc->flags & ROFF_NOFILL) |
214 | continue; | | 214 | continue; |
215 | break; | | 215 | break; |
216 | case '\\': | | 216 | case '\\': |
217 | /* Skip the escaped character, too, if any. */ | | 217 | /* Skip the escaped character, too, if any. */ |
218 | if (c[1]) | | 218 | if (c[1]) |
219 | c++; | | 219 | c++; |
220 | /* FALLTHROUGH */ | | 220 | /* FALLTHROUGH */ |
221 | default: | | 221 | default: |
222 | ws = NULL; | | 222 | ws = NULL; |
223 | break; | | 223 | break; |
224 | } | | 224 | } |
225 | end = c + 1; | | 225 | end = c + 1; |
226 | } | | 226 | } |
227 | *end = '\0'; | | 227 | *end = '\0'; |
228 | | | 228 | |
229 | if (ws) | | 229 | if (ws) |
230 | mandoc_msg(MANDOCERR_SPACE_EOL, line, (int)(ws - buf), NULL); | | 230 | mandoc_msg(MANDOCERR_SPACE_EOL, line, (int)(ws - buf), NULL); |
231 | | | 231 | |
232 | /* | | 232 | /* |
233 | * Blank lines are allowed in no-fill mode | | 233 | * Blank lines are allowed in no-fill mode |
234 | * and cancel preceding \c, | | 234 | * and cancel preceding \c, |
235 | * but add a single vertical space elsewhere. | | 235 | * but add a single vertical space elsewhere. |
236 | */ | | 236 | */ |
237 | | | 237 | |
238 | if (buf[offs] == '\0' && (mdoc->flags & ROFF_NOFILL) == 0) { | | 238 | if (buf[offs] == '\0' && (mdoc->flags & ROFF_NOFILL) == 0) { |
239 | switch (mdoc->last->type) { | | 239 | switch (mdoc->last->type) { |
240 | case ROFFT_TEXT: | | 240 | case ROFFT_TEXT: |
241 | sp = mdoc->last->string; | | 241 | sp = mdoc->last->string; |
242 | cp = end = strchr(sp, '\0') - 2; | | 242 | cp = end = strchr(sp, '\0') - 2; |
243 | if (cp < sp || cp[0] != '\\' || cp[1] != 'c') | | 243 | if (cp < sp || cp[0] != '\\' || cp[1] != 'c') |
244 | break; | | 244 | break; |
245 | while (cp > sp && cp[-1] == '\\') | | 245 | while (cp > sp && cp[-1] == '\\') |
246 | cp--; | | 246 | cp--; |
247 | if ((end - cp) % 2) | | 247 | if ((end - cp) % 2) |
248 | break; | | 248 | break; |
249 | *end = '\0'; | | 249 | *end = '\0'; |
250 | return 1; | | 250 | return 1; |
251 | default: | | 251 | default: |
252 | break; | | 252 | break; |
253 | } | | 253 | } |
254 | mandoc_msg(MANDOCERR_FI_BLANK, line, (int)(c - buf), NULL); | | 254 | mandoc_msg(MANDOCERR_FI_BLANK, line, (int)(c - buf), NULL); |
255 | roff_elem_alloc(mdoc, line, offs, ROFF_sp); | | 255 | roff_elem_alloc(mdoc, line, offs, ROFF_sp); |
256 | mdoc->last->flags |= NODE_VALID | NODE_ENDED; | | 256 | mdoc->last->flags |= NODE_VALID | NODE_ENDED; |
257 | mdoc->next = ROFF_NEXT_SIBLING; | | 257 | mdoc->next = ROFF_NEXT_SIBLING; |
258 | return 1; | | 258 | return 1; |
259 | } | | 259 | } |
260 | | | 260 | |
261 | roff_word_alloc(mdoc, line, offs, buf+offs); | | 261 | roff_word_alloc(mdoc, line, offs, buf+offs); |
262 | | | 262 | |
263 | if (mdoc->flags & ROFF_NOFILL) | | 263 | if (mdoc->flags & ROFF_NOFILL) |
264 | return 1; | | 264 | return 1; |
265 | | | 265 | |
266 | /* | | 266 | /* |
267 | * End-of-sentence check. If the last character is an unescaped | | 267 | * End-of-sentence check. If the last character is an unescaped |
268 | * EOS character, then flag the node as being the end of a | | 268 | * EOS character, then flag the node as being the end of a |
269 | * sentence. The front-end will know how to interpret this. | | 269 | * sentence. The front-end will know how to interpret this. |
270 | */ | | 270 | */ |
271 | | | 271 | |
272 | assert(buf < end); | | 272 | assert(buf < end); |
273 | | | 273 | |
274 | if (mandoc_eos(buf+offs, (size_t)(end-buf-offs))) | | 274 | if (mandoc_eos(buf+offs, (size_t)(end-buf-offs))) |
275 | mdoc->last->flags |= NODE_EOS; | | 275 | mdoc->last->flags |= NODE_EOS; |
276 | | | 276 | |
277 | for (c = buf + offs; c != NULL; c = strchr(c + 1, '.')) { | | 277 | for (c = buf + offs; c != NULL; c = strchr(c + 1, '.')) { |
278 | if (c - buf < offs + 2) | | 278 | if (c - buf < offs + 2) |
279 | continue; | | 279 | continue; |
280 | if (end - c < 3) | | 280 | if (end - c < 3) |
281 | break; | | 281 | break; |
282 | if (c[1] != ' ' || | | 282 | if (c[1] != ' ' || |
283 | isalnum((unsigned char)c[-2]) == 0 || | | 283 | isalnum((unsigned char)c[-2]) == 0 || |
284 | isalnum((unsigned char)c[-1]) == 0 || | | 284 | isalnum((unsigned char)c[-1]) == 0 || |
285 | (c[-2] == 'n' && c[-1] == 'c') || | | 285 | (c[-2] == 'n' && c[-1] == 'c') || |
286 | (c[-2] == 'v' && c[-1] == 's')) | | 286 | (c[-2] == 'v' && c[-1] == 's')) |
287 | continue; | | 287 | continue; |
288 | c += 2; | | 288 | c += 2; |
289 | if (*c == ' ') | | 289 | if (*c == ' ') |
290 | c++; | | 290 | c++; |
291 | if (*c == ' ') | | 291 | if (*c == ' ') |
292 | c++; | | 292 | c++; |
293 | if (isupper((unsigned char)(*c))) | | 293 | if (isupper((unsigned char)(*c))) |
294 | mandoc_msg(MANDOCERR_EOS, line, (int)(c - buf), NULL); | | 294 | mandoc_msg(MANDOCERR_EOS, line, (int)(c - buf), NULL); |
295 | } | | 295 | } |
296 | | | 296 | |
297 | return 1; | | 297 | return 1; |
298 | } | | 298 | } |
299 | | | 299 | |
300 | /* | | 300 | /* |
301 | * Parse a macro line, that is, a line beginning with the control | | 301 | * Parse a macro line, that is, a line beginning with the control |
302 | * character. | | 302 | * character. |
303 | */ | | 303 | */ |
304 | static int | | 304 | static int |
305 | mdoc_pmacro(struct roff_man *mdoc, int ln, char *buf, int offs) | | 305 | mdoc_pmacro(struct roff_man *mdoc, int ln, char *buf, int offs) |
306 | { | | 306 | { |
307 | struct roff_node *n; | | 307 | struct roff_node *n; |
308 | const char *cp; | | 308 | const char *cp; |
309 | size_t sz; | | 309 | size_t sz; |
310 | enum roff_tok tok; | | 310 | enum roff_tok tok; |
311 | int sv; | | 311 | int sv; |
312 | | | 312 | |
313 | /* Determine the line macro. */ | | 313 | /* Determine the line macro. */ |
314 | | | 314 | |
315 | sv = offs; | | 315 | sv = offs; |
316 | tok = TOKEN_NONE; | | 316 | tok = TOKEN_NONE; |
317 | for (sz = 0; sz < 4 && strchr(" \t\\", buf[offs]) == NULL; sz++) | | 317 | for (sz = 0; sz < 4 && strchr(" \t\\", buf[offs]) == NULL; sz++) |
318 | offs++; | | 318 | offs++; |
319 | if (sz == 2 || sz == 3) | | 319 | if (sz == 2 || sz == 3) |
320 | tok = roffhash_find(mdoc->mdocmac, buf + sv, sz); | | 320 | tok = roffhash_find(mdoc->mdocmac, buf + sv, sz); |
321 | if (tok == TOKEN_NONE) { | | 321 | if (tok == TOKEN_NONE) { |
322 | mandoc_msg(MANDOCERR_MACRO, ln, sv, "%s", buf + sv - 1); | | 322 | mandoc_msg(MANDOCERR_MACRO, ln, sv, "%s", buf + sv - 1); |
323 | return 1; | | 323 | return 1; |
324 | } | | 324 | } |
325 | | | 325 | |
326 | /* Skip a leading escape sequence or tab. */ | | 326 | /* Skip a leading escape sequence or tab. */ |
327 | | | 327 | |
328 | switch (buf[offs]) { | | 328 | switch (buf[offs]) { |
329 | case '\\': | | 329 | case '\\': |
330 | cp = buf + offs + 1; | | 330 | cp = buf + offs + 1; |
331 | mandoc_escape(&cp, NULL, NULL); | | 331 | mandoc_escape(&cp, NULL, NULL); |
332 | offs = cp - buf; | | 332 | offs = cp - buf; |
333 | break; | | 333 | break; |
334 | case '\t': | | 334 | case '\t': |
335 | offs++; | | 335 | offs++; |
336 | break; | | 336 | break; |
337 | default: | | 337 | default: |
338 | break; | | 338 | break; |
339 | } | | 339 | } |
340 | | | 340 | |
341 | /* Jump to the next non-whitespace word. */ | | 341 | /* Jump to the next non-whitespace word. */ |
342 | | | 342 | |
343 | while (buf[offs] == ' ') | | 343 | while (buf[offs] == ' ') |
344 | offs++; | | 344 | offs++; |
345 | | | 345 | |
346 | /* | | 346 | /* |
347 | * Trailing whitespace. Note that tabs are allowed to be passed | | 347 | * Trailing whitespace. Note that tabs are allowed to be passed |
348 | * into the parser as "text", so we only warn about spaces here. | | 348 | * into the parser as "text", so we only warn about spaces here. |
349 | */ | | 349 | */ |
350 | | | 350 | |
351 | if ('\0' == buf[offs] && ' ' == buf[offs - 1]) | | 351 | if ('\0' == buf[offs] && ' ' == buf[offs - 1]) |
352 | mandoc_msg(MANDOCERR_SPACE_EOL, ln, offs - 1, NULL); | | 352 | mandoc_msg(MANDOCERR_SPACE_EOL, ln, offs - 1, NULL); |
353 | | | 353 | |
354 | /* | | 354 | /* |
355 | * If an initial macro or a list invocation, divert directly | | 355 | * If an initial macro or a list invocation, divert directly |
356 | * into macro processing. | | 356 | * into macro processing. |
357 | */ | | 357 | */ |
358 | | | 358 | |
359 | n = mdoc->last; | | 359 | n = mdoc->last; |
360 | if (n == NULL || tok == MDOC_It || tok == MDOC_El) { | | 360 | if (n == NULL || tok == MDOC_It || tok == MDOC_El) { |
361 | (*mdoc_macro(tok)->fp)(mdoc, tok, ln, sv, &offs, buf); | | 361 | (*mdoc_macro(tok)->fp)(mdoc, tok, ln, sv, &offs, buf); |
362 | return 1; | | 362 | return 1; |
363 | } | | 363 | } |
364 | | | 364 | |
365 | /* | | 365 | /* |
366 | * If a column list contains a non-It macro, assume an implicit | | 366 | * If a column list contains a non-It macro, assume an implicit |
367 | * item macro. This can happen one or more times at the | | 367 | * item macro. This can happen one or more times at the |
368 | * beginning of such a list, intermixed with text lines and | | 368 | * beginning of such a list, intermixed with text lines and |
369 | * with nodes generated on the roff level, for example by tbl. | | 369 | * with nodes generated on the roff level, for example by tbl. |
370 | */ | | 370 | */ |
371 | | | 371 | |
372 | if ((n->tok == MDOC_Bl && n->type == ROFFT_BODY && | | 372 | if ((n->tok == MDOC_Bl && n->type == ROFFT_BODY && |
373 | n->end == ENDBODY_NOT && n->norm->Bl.type == LIST_column) || | | 373 | n->end == ENDBODY_NOT && n->norm->Bl.type == LIST_column) || |
374 | (n->parent != NULL && n->parent->tok == MDOC_Bl && | | 374 | (n->parent != NULL && n->parent->tok == MDOC_Bl && |
375 | n->parent->norm->Bl.type == LIST_column)) { | | 375 | n->parent->norm->Bl.type == LIST_column)) { |
376 | mdoc->flags |= MDOC_FREECOL; | | 376 | mdoc->flags |= MDOC_FREECOL; |
377 | (*mdoc_macro(MDOC_It)->fp)(mdoc, MDOC_It, ln, sv, &sv, buf); | | 377 | (*mdoc_macro(MDOC_It)->fp)(mdoc, MDOC_It, ln, sv, &sv, buf); |
378 | return 1; | | 378 | return 1; |
379 | } | | 379 | } |
380 | | | 380 | |
381 | /* Normal processing of a macro. */ | | 381 | /* Normal processing of a macro. */ |
382 | | | 382 | |
383 | (*mdoc_macro(tok)->fp)(mdoc, tok, ln, sv, &offs, buf); | | 383 | (*mdoc_macro(tok)->fp)(mdoc, tok, ln, sv, &offs, buf); |
384 | | | 384 | |
385 | /* In quick mode (for mandocdb), abort after the NAME section. */ | | 385 | /* In quick mode (for mandocdb), abort after the NAME section. */ |
386 | | | 386 | |
387 | if (mdoc->quick && MDOC_Sh == tok && | | 387 | if (mdoc->quick && MDOC_Sh == tok && |
388 | SEC_NAME != mdoc->last->sec) | | 388 | SEC_NAME != mdoc->last->sec) |
389 | return 2; | | 389 | return 2; |
390 | | | 390 | |
391 | return 1; | | 391 | return 1; |
392 | } | | 392 | } |
393 | | | 393 | |
394 | enum mdelim | | 394 | enum mdelim |
395 | mdoc_isdelim(const char *p) | | 395 | mdoc_isdelim(const char *p) |
396 | { | | 396 | { |
397 | | | 397 | |
398 | if ('\0' == p[0]) | | 398 | if ('\0' == p[0]) |
399 | return DELIM_NONE; | | 399 | return DELIM_NONE; |
400 | | | 400 | |
401 | if ('\0' == p[1]) | | 401 | if ('\0' == p[1]) |
402 | switch (p[0]) { | | 402 | switch (p[0]) { |
403 | case '(': | | 403 | case '(': |
404 | case '[': | | 404 | case '[': |
405 | return DELIM_OPEN; | | 405 | return DELIM_OPEN; |
406 | case '|': | | 406 | case '|': |
407 | return DELIM_MIDDLE; | | 407 | return DELIM_MIDDLE; |
408 | case '.': | | 408 | case '.': |
409 | case ',': | | 409 | case ',': |
410 | case ';': | | 410 | case ';': |
411 | case ':': | | 411 | case ':': |
412 | case '?': | | 412 | case '?': |
413 | case '!': | | 413 | case '!': |
414 | case ')': | | 414 | case ')': |
415 | case ']': | | 415 | case ']': |
416 | return DELIM_CLOSE; | | 416 | return DELIM_CLOSE; |
417 | default: | | 417 | default: |
418 | return DELIM_NONE; | | 418 | return DELIM_NONE; |
419 | } | | 419 | } |
420 | | | 420 | |
421 | if ('\\' != p[0]) | | 421 | if ('\\' != p[0]) |
422 | return DELIM_NONE; | | 422 | return DELIM_NONE; |
423 | | | 423 | |
424 | if (0 == strcmp(p + 1, ".")) | | 424 | if (0 == strcmp(p + 1, ".")) |
425 | return DELIM_CLOSE; | | 425 | return DELIM_CLOSE; |
426 | if (0 == strcmp(p + 1, "fR|\\fP")) | | 426 | if (0 == strcmp(p + 1, "fR|\\fP")) |
427 | return DELIM_MIDDLE; | | 427 | return DELIM_MIDDLE; |
428 | | | 428 | |
429 | return DELIM_NONE; | | 429 | return DELIM_NONE; |
430 | } | | 430 | } |