GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: man.c,v 1.124 2017/06/28 12:52:27 schwarze Exp $ */ |
||
2 |
/* |
||
3 |
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> |
||
4 |
* Copyright (c) 2013, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> |
||
5 |
* Copyright (c) 2011 Joerg Sonnenberger <joerg@netbsd.org> |
||
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 |
* |
||
11 |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES |
||
12 |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
||
13 |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR |
||
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 |
*/ |
||
19 |
#include <sys/types.h> |
||
20 |
|||
21 |
#include <assert.h> |
||
22 |
#include <ctype.h> |
||
23 |
#include <stdarg.h> |
||
24 |
#include <stdlib.h> |
||
25 |
#include <stdio.h> |
||
26 |
#include <string.h> |
||
27 |
|||
28 |
#include "mandoc_aux.h" |
||
29 |
#include "mandoc.h" |
||
30 |
#include "roff.h" |
||
31 |
#include "man.h" |
||
32 |
#include "libmandoc.h" |
||
33 |
#include "roff_int.h" |
||
34 |
#include "libman.h" |
||
35 |
|||
36 |
static void man_descope(struct roff_man *, int, int); |
||
37 |
static int man_ptext(struct roff_man *, int, char *, int); |
||
38 |
static int man_pmacro(struct roff_man *, int, char *, int); |
||
39 |
|||
40 |
|||
41 |
int |
||
42 |
man_parseln(struct roff_man *man, int ln, char *buf, int offs) |
||
43 |
{ |
||
44 |
|||
45 |
✗✓✗✗ |
369507 |
if (man->last->type != ROFFT_EQN || ln > man->last->line) |
46 |
369507 |
man->flags |= MAN_NEWLINE; |
|
47 |
|||
48 |
✓✓ | 1108521 |
return roff_getcontrol(man->roff, buf, &offs) ? |
49 |
118864 |
man_pmacro(man, ln, buf, offs) : |
|
50 |
250643 |
man_ptext(man, ln, buf, offs); |
|
51 |
} |
||
52 |
|||
53 |
static void |
||
54 |
man_descope(struct roff_man *man, int line, int offs) |
||
55 |
{ |
||
56 |
/* |
||
57 |
* Co-ordinate what happens with having a next-line scope open: |
||
58 |
* first close out the element scope (if applicable), then close |
||
59 |
* out the block scope (also if applicable). |
||
60 |
*/ |
||
61 |
|||
62 |
✓✓ | 500306 |
if (man->flags & MAN_ELINE) { |
63 |
39 |
man->flags &= ~MAN_ELINE; |
|
64 |
39 |
man_unscope(man, man->last->parent); |
|
65 |
39 |
} |
|
66 |
✓✓ | 250153 |
if ( ! (man->flags & MAN_BLINE)) |
67 |
return; |
||
68 |
1334 |
man->flags &= ~MAN_BLINE; |
|
69 |
1334 |
man_unscope(man, man->last->parent); |
|
70 |
1334 |
roff_body_alloc(man, line, offs, man->last->tok); |
|
71 |
251487 |
} |
|
72 |
|||
73 |
static int |
||
74 |
man_ptext(struct roff_man *man, int line, char *buf, int offs) |
||
75 |
{ |
||
76 |
int i; |
||
77 |
const char *cp, *sp; |
||
78 |
char *ep; |
||
79 |
|||
80 |
/* Literal free-form text whitespace is preserved. */ |
||
81 |
|||
82 |
✓✓ | 501286 |
if (man->flags & MAN_LITERAL) { |
83 |
64354 |
roff_word_alloc(man, line, offs, buf + offs); |
|
84 |
64354 |
man_descope(man, line, offs); |
|
85 |
64354 |
return 1; |
|
86 |
} |
||
87 |
|||
88 |
✓✓ | 377878 |
for (i = offs; buf[i] == ' '; i++) |
89 |
/* Skip leading whitespace. */ ; |
||
90 |
|||
91 |
/* |
||
92 |
* Blank lines are ignored in next line scope |
||
93 |
* and right after headings and cancel preceding \c, |
||
94 |
* but add a single vertical space elsewhere. |
||
95 |
*/ |
||
96 |
|||
97 |
✓✓ | 186289 |
if (buf[i] == '\0') { |
98 |
✓✓ | 490 |
if (man->flags & (MAN_ELINE | MAN_BLINE)) { |
99 |
24 |
mandoc_msg(MANDOCERR_BLK_BLANK, man->parse, |
|
100 |
line, 0, NULL); |
||
101 |
24 |
return 1; |
|
102 |
} |
||
103 |
✓✓✓✓ |
788 |
if (man->last->tok == MAN_SH || man->last->tok == MAN_SS) |
104 |
290 |
return 1; |
|
105 |
✓✓ | 176 |
switch (man->last->type) { |
106 |
case ROFFT_TEXT: |
||
107 |
107 |
sp = man->last->string; |
|
108 |
107 |
cp = ep = strchr(sp, '\0') - 2; |
|
109 |
✓✗✓✓ ✓✗ |
217 |
if (cp < sp || cp[0] != '\\' || cp[1] != 'c') |
110 |
break; |
||
111 |
✓✗✗✓ |
9 |
while (cp > sp && cp[-1] == '\\') |
112 |
cp--; |
||
113 |
✓✗ | 3 |
if ((ep - cp) % 2) |
114 |
break; |
||
115 |
3 |
*ep = '\0'; |
|
116 |
3 |
return 1; |
|
117 |
default: |
||
118 |
break; |
||
119 |
} |
||
120 |
173 |
roff_elem_alloc(man, line, offs, ROFF_sp); |
|
121 |
173 |
man->next = ROFF_NEXT_SIBLING; |
|
122 |
173 |
return 1; |
|
123 |
} |
||
124 |
|||
125 |
/* |
||
126 |
* Warn if the last un-escaped character is whitespace. Then |
||
127 |
* strip away the remaining spaces (tabs stay!). |
||
128 |
*/ |
||
129 |
|||
130 |
185799 |
i = (int)strlen(buf); |
|
131 |
✗✓ | 185799 |
assert(i); |
132 |
|||
133 |
✓✓✗✓ |
369965 |
if (' ' == buf[i - 1] || '\t' == buf[i - 1]) { |
134 |
✓✗✓✗ |
3266 |
if (i > 1 && '\\' != buf[i - 2]) |
135 |
1633 |
mandoc_msg(MANDOCERR_SPACE_EOL, man->parse, |
|
136 |
line, i - 1, NULL); |
||
137 |
|||
138 |
✓✗✓✓ |
10287 |
for (--i; i && ' ' == buf[i]; i--) |
139 |
/* Spin back to non-space. */ ; |
||
140 |
|||
141 |
/* Jump ahead of escaped whitespace. */ |
||
142 |
1633 |
i += '\\' == buf[i] ? 2 : 1; |
|
143 |
|||
144 |
1633 |
buf[i] = '\0'; |
|
145 |
1633 |
} |
|
146 |
185799 |
roff_word_alloc(man, line, offs, buf + offs); |
|
147 |
|||
148 |
/* |
||
149 |
* End-of-sentence check. If the last character is an unescaped |
||
150 |
* EOS character, then flag the node as being the end of a |
||
151 |
* sentence. The front-end will know how to interpret this. |
||
152 |
*/ |
||
153 |
|||
154 |
✗✓ | 185799 |
assert(i); |
155 |
✓✓ | 185799 |
if (mandoc_eos(buf, (size_t)i)) |
156 |
51585 |
man->last->flags |= NODE_EOS; |
|
157 |
|||
158 |
185799 |
man_descope(man, line, offs); |
|
159 |
185799 |
return 1; |
|
160 |
250643 |
} |
|
161 |
|||
162 |
static int |
||
163 |
man_pmacro(struct roff_man *man, int ln, char *buf, int offs) |
||
164 |
{ |
||
165 |
struct roff_node *n; |
||
166 |
118864 |
const char *cp; |
|
167 |
size_t sz; |
||
168 |
enum roff_tok tok; |
||
169 |
int ppos; |
||
170 |
int bline; |
||
171 |
|||
172 |
/* Determine the line macro. */ |
||
173 |
|||
174 |
118864 |
ppos = offs; |
|
175 |
tok = TOKEN_NONE; |
||
176 |
✓✗✓✓ |
1062459 |
for (sz = 0; sz < 4 && strchr(" \t\\", buf[offs]) == NULL; sz++) |
177 |
235289 |
offs++; |
|
178 |
✓✗ | 118864 |
if (sz > 0 && sz < 4) |
179 |
118864 |
tok = roffhash_find(man->manmac, buf + ppos, sz); |
|
180 |
✗✓ | 118864 |
if (tok == TOKEN_NONE) { |
181 |
mandoc_msg(MANDOCERR_MACRO, man->parse, |
||
182 |
ln, ppos, buf + ppos - 1); |
||
183 |
return 1; |
||
184 |
} |
||
185 |
|||
186 |
/* Skip a leading escape sequence or tab. */ |
||
187 |
|||
188 |
✓✗✓ | 118870 |
switch (buf[offs]) { |
189 |
case '\\': |
||
190 |
6 |
cp = buf + offs + 1; |
|
191 |
6 |
mandoc_escape(&cp, NULL, NULL); |
|
192 |
6 |
offs = cp - buf; |
|
193 |
6 |
break; |
|
194 |
case '\t': |
||
195 |
offs++; |
||
196 |
break; |
||
197 |
default: |
||
198 |
break; |
||
199 |
} |
||
200 |
|||
201 |
/* Jump to the next non-whitespace word. */ |
||
202 |
|||
203 |
✓✓ | 363682 |
while (buf[offs] == ' ') |
204 |
62977 |
offs++; |
|
205 |
|||
206 |
/* |
||
207 |
* Trailing whitespace. Note that tabs are allowed to be passed |
||
208 |
* into the parser as "text", so we only warn about spaces here. |
||
209 |
*/ |
||
210 |
|||
211 |
✓✓✓✓ |
174780 |
if (buf[offs] == '\0' && buf[offs - 1] == ' ') |
212 |
8 |
mandoc_msg(MANDOCERR_SPACE_EOL, man->parse, |
|
213 |
ln, offs - 1, NULL); |
||
214 |
|||
215 |
/* |
||
216 |
* Some macros break next-line scopes; otherwise, remember |
||
217 |
* whether we are in next-line scope for a block head. |
||
218 |
*/ |
||
219 |
|||
220 |
118864 |
man_breakscope(man, tok); |
|
221 |
118864 |
bline = man->flags & MAN_BLINE; |
|
222 |
|||
223 |
/* |
||
224 |
* If the line in next-line scope ends with \c, keep the |
||
225 |
* next-line scope open for the subsequent input line. |
||
226 |
* That is not at all portable, only groff >= 1.22.4 |
||
227 |
* does it, but *if* this weird idiom occurs in a manual |
||
228 |
* page, that's very likely what the author intended. |
||
229 |
*/ |
||
230 |
|||
231 |
✓✓ | 118864 |
if (bline) { |
232 |
1193 |
cp = strchr(buf + offs, '\0') - 2; |
|
233 |
✓✗✓✓ ✓✓ |
2404 |
if (cp >= buf && cp[0] == '\\' && cp[1] == 'c') |
234 |
15 |
bline = 0; |
|
235 |
} |
||
236 |
|||
237 |
/* Call to handler... */ |
||
238 |
|||
239 |
✗✓ | 118864 |
assert(man_macros[tok].fp); |
240 |
118864 |
(*man_macros[tok].fp)(man, tok, ln, ppos, &offs, buf); |
|
241 |
|||
242 |
/* In quick mode (for mandocdb), abort after the NAME section. */ |
||
243 |
|||
244 |
✗✓ | 118864 |
if (man->quick && tok == MAN_SH) { |
245 |
n = man->last; |
||
246 |
if (n->type == ROFFT_BODY && |
||
247 |
strcmp(n->prev->child->string, "NAME")) |
||
248 |
return 2; |
||
249 |
} |
||
250 |
|||
251 |
/* |
||
252 |
* If we are in a next-line scope for a block head, |
||
253 |
* close it out now and switch to the body, |
||
254 |
* unless the next-line scope is allowed to continue. |
||
255 |
*/ |
||
256 |
|||
257 |
✓✓✓✓ ✓✓ |
121206 |
if ( ! bline || man->flags & MAN_ELINE || |
258 |
1164 |
man_macros[tok].flags & MAN_NSCOPED) |
|
259 |
117715 |
return 1; |
|
260 |
|||
261 |
✗✓ | 1149 |
assert(man->flags & MAN_BLINE); |
262 |
1149 |
man->flags &= ~MAN_BLINE; |
|
263 |
|||
264 |
1149 |
man_unscope(man, man->last->parent); |
|
265 |
1149 |
roff_body_alloc(man, ln, ppos, man->last->tok); |
|
266 |
1149 |
return 1; |
|
267 |
118864 |
} |
|
268 |
|||
269 |
void |
||
270 |
man_breakscope(struct roff_man *man, int tok) |
||
271 |
{ |
||
272 |
struct roff_node *n; |
||
273 |
|||
274 |
/* |
||
275 |
* An element next line scope is open, |
||
276 |
* and the new macro is not allowed inside elements. |
||
277 |
* Delete the element that is being broken. |
||
278 |
*/ |
||
279 |
|||
280 |
✓✓✓✓ ✓✓ |
247032 |
if (man->flags & MAN_ELINE && (tok < MAN_TH || |
281 |
11 |
! (man_macros[tok].flags & MAN_NSCOPED))) { |
|
282 |
14 |
n = man->last; |
|
283 |
✗✓ | 14 |
if (n->type == ROFFT_TEXT) |
284 |
n = n->parent; |
||
285 |
✓✗✗✓ |
28 |
if (n->tok < MAN_TH || |
286 |
14 |
man_macros[n->tok].flags & MAN_NSCOPED) |
|
287 |
n = n->parent; |
||
288 |
|||
289 |
28 |
mandoc_vmsg(MANDOCERR_BLK_LINE, man->parse, |
|
290 |
14 |
n->line, n->pos, "%s breaks %s", |
|
291 |
14 |
roff_name[tok], roff_name[n->tok]); |
|
292 |
|||
293 |
14 |
roff_node_delete(man, n); |
|
294 |
14 |
man->flags &= ~MAN_ELINE; |
|
295 |
14 |
} |
|
296 |
|||
297 |
/* |
||
298 |
* Weird special case: |
||
299 |
* Switching fill mode closes section headers. |
||
300 |
*/ |
||
301 |
|||
302 |
✓✓✓✓ |
123517 |
if (man->flags & MAN_BLINE && |
303 |
✓✓ | 1379 |
(tok == MAN_nf || tok == MAN_fi) && |
304 |
✓✓ | 42 |
(man->last->tok == MAN_SH || man->last->tok == MAN_SS)) { |
305 |
24 |
n = man->last; |
|
306 |
24 |
man_unscope(man, n); |
|
307 |
24 |
roff_body_alloc(man, n->line, n->pos, n->tok); |
|
308 |
24 |
man->flags &= ~MAN_BLINE; |
|
309 |
24 |
} |
|
310 |
|||
311 |
/* |
||
312 |
* A block header next line scope is open, |
||
313 |
* and the new macro is not allowed inside block headers. |
||
314 |
* Delete the block that is being broken. |
||
315 |
*/ |
||
316 |
|||
317 |
✓✓✓✓ ✓✓ |
126194 |
if (man->flags & MAN_BLINE && (tok < MAN_TH || |
318 |
1337 |
man_macros[tok].flags & MAN_BSCOPE)) { |
|
319 |
162 |
n = man->last; |
|
320 |
✓✓ | 162 |
if (n->type == ROFFT_TEXT) |
321 |
18 |
n = n->parent; |
|
322 |
✓✗✓✓ |
324 |
if (n->tok < MAN_TH || |
323 |
162 |
(man_macros[n->tok].flags & MAN_BSCOPE) == 0) |
|
324 |
5 |
n = n->parent; |
|
325 |
|||
326 |
✗✓ | 162 |
assert(n->type == ROFFT_HEAD); |
327 |
162 |
n = n->parent; |
|
328 |
✗✓ | 162 |
assert(n->type == ROFFT_BLOCK); |
329 |
✗✓ | 162 |
assert(man_macros[n->tok].flags & MAN_SCOPED); |
330 |
|||
331 |
324 |
mandoc_vmsg(MANDOCERR_BLK_LINE, man->parse, |
|
332 |
162 |
n->line, n->pos, "%s breaks %s", |
|
333 |
162 |
roff_name[tok], roff_name[n->tok]); |
|
334 |
|||
335 |
162 |
roff_node_delete(man, n); |
|
336 |
162 |
man->flags &= ~MAN_BLINE; |
|
337 |
162 |
} |
|
338 |
123502 |
} |
|
339 |
|||
340 |
const struct mparse * |
||
341 |
man_mparse(const struct roff_man *man) |
||
342 |
{ |
||
343 |
|||
344 |
assert(man && man->parse); |
||
345 |
return man->parse; |
||
346 |
} |
||
347 |
|||
348 |
void |
||
349 |
man_state(struct roff_man *man, struct roff_node *n) |
||
350 |
{ |
||
351 |
|||
352 |
✗✓✗✓ ✓ |
681176 |
switch(n->tok) { |
353 |
case MAN_nf: |
||
354 |
case MAN_EX: |
||
355 |
✓✓✓✓ |
22423 |
if (man->flags & MAN_LITERAL && ! (n->flags & NODE_VALID)) |
356 |
32 |
mandoc_msg(MANDOCERR_NF_SKIP, man->parse, |
|
357 |
16 |
n->line, n->pos, "nf"); |
|
358 |
22390 |
man->flags |= MAN_LITERAL; |
|
359 |
22390 |
break; |
|
360 |
case MAN_fi: |
||
361 |
case MAN_EE: |
||
362 |
✓✓✓✓ |
22384 |
if ( ! (man->flags & MAN_LITERAL) && |
363 |
48 |
! (n->flags & NODE_VALID)) |
|
364 |
60 |
mandoc_msg(MANDOCERR_FI_SKIP, man->parse, |
|
365 |
30 |
n->line, n->pos, "fi"); |
|
366 |
22336 |
man->flags &= ~MAN_LITERAL; |
|
367 |
22336 |
break; |
|
368 |
default: |
||
369 |
break; |
||
370 |
} |
||
371 |
318225 |
man->last->flags |= NODE_VALID; |
|
372 |
318225 |
} |
|
373 |
|||
374 |
void |
||
375 |
man_validate(struct roff_man *man) |
||
376 |
{ |
||
377 |
|||
378 |
4522 |
man->last = man->first; |
|
379 |
2261 |
man_node_validate(man); |
|
380 |
2261 |
man->flags &= ~MAN_LITERAL; |
|
381 |
2261 |
} |
Generated by: GCOVR (Version 3.3) |