Commit | Line | Data |
---|---|---|
54ba9607 | 1 | /* $Id: man.c,v 1.187 2019/01/05 00:36:50 schwarze Exp $ */ |
80387638 | 2 | /* |
60e1e752 | 3 | * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> |
54ba9607 | 4 | * Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org> |
070c62a6 | 5 | * Copyright (c) 2011 Joerg Sonnenberger <joerg@netbsd.org> |
80387638 SW |
6 | * |
7 | * Permission to use, copy, modify, and distribute this software for any | |
8 | * purpose with or without fee is hereby granted, provided that the above | |
9 | * copyright notice and this permission notice appear in all copies. | |
10 | * | |
54ba9607 | 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES |
80387638 | 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
54ba9607 | 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR |
80387638 SW |
14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
18 | */ | |
80387638 | 19 | #include "config.h" |
80387638 SW |
20 | |
21 | #include <sys/types.h> | |
22 | ||
23 | #include <assert.h> | |
070c62a6 | 24 | #include <ctype.h> |
80387638 SW |
25 | #include <stdarg.h> |
26 | #include <stdlib.h> | |
27 | #include <stdio.h> | |
28 | #include <string.h> | |
29 | ||
070c62a6 | 30 | #include "mandoc_aux.h" |
54ba9607 SW |
31 | #include "mandoc.h" |
32 | #include "roff.h" | |
33 | #include "man.h" | |
80387638 | 34 | #include "libmandoc.h" |
54ba9607 SW |
35 | #include "roff_int.h" |
36 | #include "libman.h" | |
80387638 | 37 | |
54ba9607 SW |
38 | static char *man_hasc(char *); |
39 | static int man_ptext(struct roff_man *, int, char *, int); | |
40 | static int man_pmacro(struct roff_man *, int, char *, int); | |
80387638 | 41 | |
80387638 | 42 | |
80387638 | 43 | int |
54ba9607 | 44 | man_parseln(struct roff_man *man, int ln, char *buf, int offs) |
80387638 SW |
45 | { |
46 | ||
54ba9607 SW |
47 | if (man->last->type != ROFFT_EQN || ln > man->last->line) |
48 | man->flags |= MAN_NEWLINE; | |
80387638 | 49 | |
54ba9607 | 50 | return roff_getcontrol(man->roff, buf, &offs) ? |
070c62a6 | 51 | man_pmacro(man, ln, buf, offs) : |
54ba9607 | 52 | man_ptext(man, ln, buf, offs); |
80387638 SW |
53 | } |
54 | ||
80387638 | 55 | /* |
54ba9607 SW |
56 | * If the string ends with \c, return a pointer to the backslash. |
57 | * Otherwise, return NULL. | |
80387638 | 58 | */ |
54ba9607 SW |
59 | static char * |
60 | man_hasc(char *start) | |
80387638 | 61 | { |
54ba9607 | 62 | char *cp, *ep; |
80387638 | 63 | |
54ba9607 SW |
64 | ep = strchr(start, '\0') - 2; |
65 | if (ep < start || ep[0] != '\\' || ep[1] != 'c') | |
66 | return NULL; | |
67 | for (cp = ep; cp > start; cp--) | |
68 | if (cp[-1] != '\\') | |
69 | break; | |
70 | return (ep - cp) % 2 ? NULL : ep; | |
80387638 SW |
71 | } |
72 | ||
80387638 | 73 | void |
54ba9607 | 74 | man_descope(struct roff_man *man, int line, int offs, char *start) |
80387638 | 75 | { |
54ba9607 | 76 | /* Trailing \c keeps next-line scope open. */ |
80387638 | 77 | |
54ba9607 SW |
78 | if (start != NULL && man_hasc(start) != NULL) |
79 | return; | |
80387638 | 80 | |
80387638 SW |
81 | /* |
82 | * Co-ordinate what happens with having a next-line scope open: | |
54ba9607 SW |
83 | * first close out the element scopes (if applicable), |
84 | * then close out the block scope (also if applicable). | |
80387638 SW |
85 | */ |
86 | ||
54ba9607 SW |
87 | if (man->flags & MAN_ELINE) { |
88 | while (man->last->parent->type != ROFFT_ROOT && | |
89 | man_macro(man->last->parent->tok)->flags & MAN_ESCOPED) | |
90 | man_unscope(man, man->last->parent); | |
f88b6c16 | 91 | man->flags &= ~MAN_ELINE; |
80387638 | 92 | } |
54ba9607 SW |
93 | if ( ! (man->flags & MAN_BLINE)) |
94 | return; | |
95 | man_unscope(man, man->last->parent); | |
96 | roff_body_alloc(man, line, offs, man->last->tok); | |
97 | man->flags &= ~(MAN_BLINE | ROFF_NONOFILL); | |
80387638 SW |
98 | } |
99 | ||
80387638 | 100 | static int |
54ba9607 | 101 | man_ptext(struct roff_man *man, int line, char *buf, int offs) |
80387638 SW |
102 | { |
103 | int i; | |
54ba9607 | 104 | char *ep; |
80387638 | 105 | |
54ba9607 | 106 | /* In no-fill mode, whitespace is preserved on text lines. */ |
80387638 | 107 | |
54ba9607 SW |
108 | if (man->flags & ROFF_NOFILL) { |
109 | roff_word_alloc(man, line, offs, buf + offs); | |
110 | man_descope(man, line, offs, buf + offs); | |
111 | return 1; | |
80387638 SW |
112 | } |
113 | ||
54ba9607 | 114 | for (i = offs; buf[i] == ' '; i++) |
80387638 SW |
115 | /* Skip leading whitespace. */ ; |
116 | ||
7888c61d | 117 | /* |
54ba9607 SW |
118 | * Blank lines are ignored in next line scope |
119 | * and right after headings and cancel preceding \c, | |
7888c61d FF |
120 | * but add a single vertical space elsewhere. |
121 | */ | |
122 | ||
54ba9607 SW |
123 | if (buf[i] == '\0') { |
124 | if (man->flags & (MAN_ELINE | MAN_BLINE)) { | |
125 | mandoc_msg(MANDOCERR_BLK_BLANK, line, 0, NULL); | |
126 | return 1; | |
127 | } | |
128 | if (man->last->tok == MAN_SH || man->last->tok == MAN_SS) | |
129 | return 1; | |
130 | if (man->last->type == ROFFT_TEXT && | |
131 | ((ep = man_hasc(man->last->string)) != NULL)) { | |
132 | *ep = '\0'; | |
133 | return 1; | |
7888c61d | 134 | } |
54ba9607 SW |
135 | roff_elem_alloc(man, line, offs, ROFF_sp); |
136 | man->next = ROFF_NEXT_SIBLING; | |
137 | return 1; | |
80387638 SW |
138 | } |
139 | ||
070c62a6 | 140 | /* |
80387638 | 141 | * Warn if the last un-escaped character is whitespace. Then |
070c62a6 | 142 | * strip away the remaining spaces (tabs stay!). |
80387638 SW |
143 | */ |
144 | ||
145 | i = (int)strlen(buf); | |
146 | assert(i); | |
147 | ||
148 | if (' ' == buf[i - 1] || '\t' == buf[i - 1]) { | |
149 | if (i > 1 && '\\' != buf[i - 2]) | |
54ba9607 | 150 | mandoc_msg(MANDOCERR_SPACE_EOL, line, i - 1, NULL); |
80387638 SW |
151 | |
152 | for (--i; i && ' ' == buf[i]; i--) | |
153 | /* Spin back to non-space. */ ; | |
154 | ||
155 | /* Jump ahead of escaped whitespace. */ | |
156 | i += '\\' == buf[i] ? 2 : 1; | |
157 | ||
158 | buf[i] = '\0'; | |
159 | } | |
54ba9607 | 160 | roff_word_alloc(man, line, offs, buf + offs); |
80387638 SW |
161 | |
162 | /* | |
163 | * End-of-sentence check. If the last character is an unescaped | |
164 | * EOS character, then flag the node as being the end of a | |
165 | * sentence. The front-end will know how to interpret this. | |
166 | */ | |
167 | ||
168 | assert(i); | |
070c62a6 | 169 | if (mandoc_eos(buf, (size_t)i)) |
54ba9607 | 170 | man->last->flags |= NODE_EOS; |
80387638 | 171 | |
54ba9607 SW |
172 | man_descope(man, line, offs, buf + offs); |
173 | return 1; | |
80387638 SW |
174 | } |
175 | ||
80387638 | 176 | static int |
54ba9607 | 177 | man_pmacro(struct roff_man *man, int ln, char *buf, int offs) |
80387638 | 178 | { |
54ba9607 SW |
179 | struct roff_node *n; |
180 | const char *cp; | |
181 | size_t sz; | |
182 | enum roff_tok tok; | |
183 | int ppos; | |
070c62a6 | 184 | int bline; |
80387638 | 185 | |
54ba9607 | 186 | /* Determine the line macro. */ |
80387638 | 187 | |
60e1e752 | 188 | ppos = offs; |
54ba9607 SW |
189 | tok = TOKEN_NONE; |
190 | for (sz = 0; sz < 4 && strchr(" \t\\", buf[offs]) == NULL; sz++) | |
191 | offs++; | |
192 | if (sz > 0 && sz < 4) | |
193 | tok = roffhash_find(man->manmac, buf + ppos, sz); | |
194 | if (tok == TOKEN_NONE) { | |
195 | mandoc_msg(MANDOCERR_MACRO, ln, ppos, "%s", buf + ppos - 1); | |
196 | return 1; | |
197 | } | |
80387638 | 198 | |
54ba9607 | 199 | /* Skip a leading escape sequence or tab. */ |
80387638 | 200 | |
54ba9607 SW |
201 | switch (buf[offs]) { |
202 | case '\\': | |
203 | cp = buf + offs + 1; | |
204 | mandoc_escape(&cp, NULL, NULL); | |
205 | offs = cp - buf; | |
206 | break; | |
207 | case '\t': | |
208 | offs++; | |
209 | break; | |
210 | default: | |
211 | break; | |
80387638 SW |
212 | } |
213 | ||
54ba9607 | 214 | /* Jump to the next non-whitespace word. */ |
80387638 | 215 | |
54ba9607 | 216 | while (buf[offs] == ' ') |
60e1e752 | 217 | offs++; |
80387638 | 218 | |
070c62a6 | 219 | /* |
80387638 SW |
220 | * Trailing whitespace. Note that tabs are allowed to be passed |
221 | * into the parser as "text", so we only warn about spaces here. | |
222 | */ | |
223 | ||
54ba9607 SW |
224 | if (buf[offs] == '\0' && buf[offs - 1] == ' ') |
225 | mandoc_msg(MANDOCERR_SPACE_EOL, ln, offs - 1, NULL); | |
80387638 | 226 | |
070c62a6 | 227 | /* |
54ba9607 SW |
228 | * Some macros break next-line scopes; otherwise, remember |
229 | * whether we are in next-line scope for a block head. | |
80387638 SW |
230 | */ |
231 | ||
54ba9607 SW |
232 | man_breakscope(man, tok); |
233 | bline = man->flags & MAN_BLINE; | |
80387638 | 234 | |
36342e81 | 235 | /* |
54ba9607 SW |
236 | * If the line in next-line scope ends with \c, keep the |
237 | * next-line scope open for the subsequent input line. | |
238 | * That is not at all portable, only groff >= 1.22.4 | |
239 | * does it, but *if* this weird idiom occurs in a manual | |
240 | * page, that's very likely what the author intended. | |
36342e81 | 241 | */ |
36342e81 | 242 | |
54ba9607 SW |
243 | if (bline && man_hasc(buf + offs)) |
244 | bline = 0; | |
80387638 SW |
245 | |
246 | /* Call to handler... */ | |
247 | ||
54ba9607 | 248 | (*man_macro(tok)->fp)(man, tok, ln, ppos, &offs, buf); |
80387638 | 249 | |
070c62a6 | 250 | /* In quick mode (for mandocdb), abort after the NAME section. */ |
80387638 | 251 | |
54ba9607 | 252 | if (man->quick && tok == MAN_SH) { |
070c62a6 | 253 | n = man->last; |
54ba9607 | 254 | if (n->type == ROFFT_BODY && |
070c62a6 | 255 | strcmp(n->prev->child->string, "NAME")) |
54ba9607 | 256 | return 2; |
80387638 | 257 | } |
80387638 SW |
258 | |
259 | /* | |
070c62a6 FF |
260 | * If we are in a next-line scope for a block head, |
261 | * close it out now and switch to the body, | |
262 | * unless the next-line scope is allowed to continue. | |
80387638 SW |
263 | */ |
264 | ||
54ba9607 SW |
265 | if (bline == 0 || |
266 | (man->flags & MAN_BLINE) == 0 || | |
267 | man->flags & MAN_ELINE || | |
268 | man_macro(tok)->flags & MAN_NSCOPED) | |
269 | return 1; | |
80387638 | 270 | |
54ba9607 SW |
271 | man_unscope(man, man->last->parent); |
272 | roff_body_alloc(man, ln, ppos, man->last->tok); | |
273 | man->flags &= ~(MAN_BLINE | ROFF_NONOFILL); | |
274 | return 1; | |
80387638 SW |
275 | } |
276 | ||
54ba9607 SW |
277 | void |
278 | man_breakscope(struct roff_man *man, int tok) | |
80387638 | 279 | { |
54ba9607 | 280 | struct roff_node *n; |
80387638 | 281 | |
54ba9607 SW |
282 | /* |
283 | * An element next line scope is open, | |
284 | * and the new macro is not allowed inside elements. | |
285 | * Delete the element that is being broken. | |
286 | */ | |
36342e81 | 287 | |
54ba9607 SW |
288 | if (man->flags & MAN_ELINE && (tok < MAN_TH || |
289 | (man_macro(tok)->flags & MAN_NSCOPED) == 0)) { | |
290 | n = man->last; | |
291 | if (n->type == ROFFT_TEXT) | |
292 | n = n->parent; | |
293 | if (n->tok < MAN_TH || | |
294 | (man_macro(n->tok)->flags & (MAN_NSCOPED | MAN_ESCOPED)) | |
295 | == MAN_NSCOPED) | |
296 | n = n->parent; | |
070c62a6 | 297 | |
54ba9607 SW |
298 | mandoc_msg(MANDOCERR_BLK_LINE, n->line, n->pos, |
299 | "%s breaks %s", roff_name[tok], roff_name[n->tok]); | |
070c62a6 | 300 | |
54ba9607 SW |
301 | roff_node_delete(man, n); |
302 | man->flags &= ~MAN_ELINE; | |
070c62a6 FF |
303 | } |
304 | ||
54ba9607 SW |
305 | /* |
306 | * Weird special case: | |
307 | * Switching fill mode closes section headers. | |
308 | */ | |
070c62a6 | 309 | |
54ba9607 SW |
310 | if (man->flags & MAN_BLINE && |
311 | (tok == ROFF_nf || tok == ROFF_fi) && | |
312 | (man->last->tok == MAN_SH || man->last->tok == MAN_SS)) { | |
313 | n = man->last; | |
314 | man_unscope(man, n); | |
315 | roff_body_alloc(man, n->line, n->pos, n->tok); | |
316 | man->flags &= ~(MAN_BLINE | ROFF_NONOFILL); | |
070c62a6 FF |
317 | } |
318 | ||
54ba9607 SW |
319 | /* |
320 | * A block header next line scope is open, | |
321 | * and the new macro is not allowed inside block headers. | |
322 | * Delete the block that is being broken. | |
323 | */ | |
070c62a6 | 324 | |
54ba9607 SW |
325 | if (man->flags & MAN_BLINE && tok != ROFF_nf && tok != ROFF_fi && |
326 | (tok < MAN_TH || man_macro(tok)->flags & MAN_XSCOPE)) { | |
327 | n = man->last; | |
328 | if (n->type == ROFFT_TEXT) | |
329 | n = n->parent; | |
330 | if (n->tok < MAN_TH || | |
331 | (man_macro(n->tok)->flags & MAN_XSCOPE) == 0) | |
332 | n = n->parent; | |
070c62a6 | 333 | |
54ba9607 SW |
334 | assert(n->type == ROFFT_HEAD); |
335 | n = n->parent; | |
336 | assert(n->type == ROFFT_BLOCK); | |
337 | assert(man_macro(n->tok)->flags & MAN_BSCOPED); | |
070c62a6 | 338 | |
54ba9607 SW |
339 | mandoc_msg(MANDOCERR_BLK_LINE, n->line, n->pos, |
340 | "%s breaks %s", roff_name[tok], roff_name[n->tok]); | |
070c62a6 | 341 | |
54ba9607 SW |
342 | roff_node_delete(man, n); |
343 | man->flags &= ~(MAN_BLINE | ROFF_NONOFILL); | |
070c62a6 | 344 | } |
070c62a6 | 345 | } |