1 /* $OpenBSD: man.c,v 1.136 2022/04/28 10:17:37 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2013-2015,2017-2019,2022 Ingo Schwarze <schwarze@openbsd.org> 4 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 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 char *man_hasc(char *); 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 if (man->last->type != ROFFT_EQN || ln > man->last->line) 46 man->flags |= MAN_NEWLINE; 47 48 return roff_getcontrol(man->roff, buf, &offs) ? 49 man_pmacro(man, ln, buf, offs) : 50 man_ptext(man, ln, buf, offs); 51 } 52 53 /* 54 * If the string ends with \c, return a pointer to the backslash. 55 * Otherwise, return NULL. 56 */ 57 static char * 58 man_hasc(char *start) 59 { 60 char *cp, *ep; 61 62 ep = strchr(start, '\0') - 2; 63 if (ep < start || ep[0] != '\\' || ep[1] != 'c') 64 return NULL; 65 for (cp = ep; cp > start; cp--) 66 if (cp[-1] != '\\') 67 break; 68 return (ep - cp) % 2 ? NULL : ep; 69 } 70 71 /* 72 * Rewind all open next-line scopes. 73 */ 74 void 75 man_descope(struct roff_man *man, int line, int offs, char *start) 76 { 77 /* Trailing \c keeps next-line scope open. */ 78 79 if (start != NULL && man_hasc(start) != NULL) 80 return; 81 82 /* 83 * Co-ordinate what happens with having a next-line scope open: 84 * first close out the element scopes (if applicable), 85 * then close out the block scope (also if applicable). 86 */ 87 88 if (man->flags & MAN_ELINE) { 89 while (man->last->parent->type != ROFFT_ROOT && 90 man_macro(man->last->parent->tok)->flags & MAN_ESCOPED) 91 man_unscope(man, man->last->parent); 92 man->flags &= ~MAN_ELINE; 93 } 94 if ( ! (man->flags & MAN_BLINE)) 95 return; 96 man_unscope(man, man->last->parent); 97 roff_body_alloc(man, line, offs, man->last->tok); 98 man->flags &= ~(MAN_BLINE | ROFF_NONOFILL); 99 } 100 101 static int 102 man_ptext(struct roff_man *man, int line, char *buf, int offs) 103 { 104 int i; 105 char *ep; 106 107 /* In no-fill mode, whitespace is preserved on text lines. */ 108 109 if (man->flags & ROFF_NOFILL) { 110 roff_word_alloc(man, line, offs, buf + offs); 111 man_descope(man, line, offs, buf + offs); 112 return 1; 113 } 114 115 for (i = offs; buf[i] == ' '; i++) 116 /* Skip leading whitespace. */ ; 117 118 /* 119 * Blank lines are ignored in next line scope 120 * and right after headings and cancel preceding \c, 121 * but add a single vertical space elsewhere. 122 */ 123 124 if (buf[i] == '\0') { 125 if (man->flags & (MAN_ELINE | MAN_BLINE)) { 126 mandoc_msg(MANDOCERR_BLK_BLANK, line, 0, NULL); 127 return 1; 128 } 129 if (man->last->tok == MAN_SH || man->last->tok == MAN_SS) 130 return 1; 131 if (man->last->type == ROFFT_TEXT && 132 ((ep = man_hasc(man->last->string)) != NULL)) { 133 *ep = '\0'; 134 return 1; 135 } 136 roff_elem_alloc(man, line, offs, ROFF_sp); 137 man->next = ROFF_NEXT_SIBLING; 138 return 1; 139 } 140 141 /* 142 * Warn if the last un-escaped character is whitespace. Then 143 * strip away the remaining spaces (tabs stay!). 144 */ 145 146 i = (int)strlen(buf); 147 assert(i); 148 149 if (' ' == buf[i - 1] || '\t' == buf[i - 1]) { 150 if (i > 1 && '\\' != buf[i - 2]) 151 mandoc_msg(MANDOCERR_SPACE_EOL, line, i - 1, NULL); 152 153 for (--i; i && ' ' == buf[i]; i--) 154 /* Spin back to non-space. */ ; 155 156 /* Jump ahead of escaped whitespace. */ 157 i += '\\' == buf[i] ? 2 : 1; 158 159 buf[i] = '\0'; 160 } 161 roff_word_alloc(man, line, offs, buf + offs); 162 163 /* 164 * End-of-sentence check. If the last character is an unescaped 165 * EOS character, then flag the node as being the end of a 166 * sentence. The front-end will know how to interpret this. 167 */ 168 169 assert(i); 170 if (mandoc_eos(buf, (size_t)i)) 171 man->last->flags |= NODE_EOS; 172 173 man_descope(man, line, offs, buf + offs); 174 return 1; 175 } 176 177 static int 178 man_pmacro(struct roff_man *man, int ln, char *buf, int offs) 179 { 180 struct roff_node *n; 181 const char *cp; 182 size_t sz; 183 enum roff_tok tok; 184 int ppos; 185 int bline; 186 187 /* Determine the line macro. */ 188 189 ppos = offs; 190 tok = TOKEN_NONE; 191 for (sz = 0; sz < 4 && strchr(" \t\\", buf[offs]) == NULL; sz++) 192 offs++; 193 if (sz > 0 && sz < 4) 194 tok = roffhash_find(man->manmac, buf + ppos, sz); 195 if (tok == TOKEN_NONE) { 196 mandoc_msg(MANDOCERR_MACRO, ln, ppos, "%s", buf + ppos - 1); 197 return 1; 198 } 199 200 /* Skip a leading escape sequence or tab. */ 201 202 switch (buf[offs]) { 203 case '\\': 204 cp = buf + offs + 1; 205 mandoc_escape(&cp, NULL, NULL); 206 offs = cp - buf; 207 break; 208 case '\t': 209 offs++; 210 break; 211 default: 212 break; 213 } 214 215 /* Jump to the next non-whitespace word. */ 216 217 while (buf[offs] == ' ') 218 offs++; 219 220 /* 221 * Trailing whitespace. Note that tabs are allowed to be passed 222 * into the parser as "text", so we only warn about spaces here. 223 */ 224 225 if (buf[offs] == '\0' && buf[offs - 1] == ' ') 226 mandoc_msg(MANDOCERR_SPACE_EOL, ln, offs - 1, NULL); 227 228 /* 229 * Some macros break next-line scopes; otherwise, remember 230 * whether we are in next-line scope for a block head. 231 */ 232 233 man_breakscope(man, tok); 234 bline = man->flags & MAN_BLINE; 235 236 /* 237 * If the line in next-line scope ends with \c, keep the 238 * next-line scope open for the subsequent input line. 239 * That is not at all portable, only groff >= 1.22.4 240 * does it, but *if* this weird idiom occurs in a manual 241 * page, that's very likely what the author intended. 242 */ 243 244 if (bline && man_hasc(buf + offs)) 245 bline = 0; 246 247 /* Call to handler... */ 248 249 (*man_macro(tok)->fp)(man, tok, ln, ppos, &offs, buf); 250 251 /* In quick mode (for mandocdb), abort after the NAME section. */ 252 253 if (man->quick && tok == MAN_SH) { 254 n = man->last; 255 if (n->type == ROFFT_BODY && 256 strcmp(n->prev->child->string, "NAME")) 257 return 2; 258 } 259 260 /* 261 * If we are in a next-line scope for a block head, 262 * close it out now and switch to the body, 263 * unless the next-line scope is allowed to continue. 264 */ 265 266 if (bline == 0 || 267 (man->flags & MAN_BLINE) == 0 || 268 man->flags & MAN_ELINE || 269 man_macro(tok)->flags & MAN_NSCOPED) 270 return 1; 271 272 man_unscope(man, man->last->parent); 273 roff_body_alloc(man, ln, ppos, man->last->tok); 274 man->flags &= ~(MAN_BLINE | ROFF_NONOFILL); 275 return 1; 276 } 277 278 /* 279 * Rewind open next-line scopes 280 * unless the tok request or macro is allowed inside them. 281 */ 282 void 283 man_breakscope(struct roff_man *man, int tok) 284 { 285 struct roff_node *n; 286 287 /* 288 * An element next line scope is open, 289 * and the new macro is not allowed inside elements. 290 * Delete the element that is being broken. 291 */ 292 293 if (man->flags & MAN_ELINE && (tok < MAN_TH || 294 (man_macro(tok)->flags & MAN_NSCOPED) == 0)) { 295 n = man->last; 296 if (n->type == ROFFT_TEXT) 297 n = n->parent; 298 if (n->tok < MAN_TH || 299 (man_macro(n->tok)->flags & (MAN_NSCOPED | MAN_ESCOPED)) 300 == MAN_NSCOPED) 301 n = n->parent; 302 for (;;) { 303 mandoc_msg(MANDOCERR_BLK_LINE, n->line, n->pos, 304 "%s breaks %s", roff_name[tok], roff_name[n->tok]); 305 if (n->parent->type != ROFFT_ELEM || 306 (man_macro(n->parent->tok)->flags & 307 MAN_ESCOPED) == 0) 308 break; 309 n = n->parent; 310 } 311 roff_node_delete(man, n); 312 man->flags &= ~MAN_ELINE; 313 } 314 315 /* 316 * Weird special case: 317 * Switching fill mode closes section headers. 318 */ 319 320 if (man->flags & MAN_BLINE && 321 (tok == ROFF_nf || tok == ROFF_fi) && 322 (man->last->tok == MAN_SH || man->last->tok == MAN_SS)) { 323 n = man->last; 324 man_unscope(man, n); 325 roff_body_alloc(man, n->line, n->pos, n->tok); 326 man->flags &= ~(MAN_BLINE | ROFF_NONOFILL); 327 } 328 329 /* 330 * A block header next line scope is open, 331 * and the new macro is not allowed inside block headers. 332 * Delete the block that is being broken. 333 */ 334 335 if (man->flags & MAN_BLINE && tok != ROFF_nf && tok != ROFF_fi && 336 (tok < MAN_TH || man_macro(tok)->flags & MAN_XSCOPE)) { 337 n = man->last; 338 if (n->type == ROFFT_TEXT) 339 n = n->parent; 340 if (n->tok < MAN_TH || 341 (man_macro(n->tok)->flags & MAN_XSCOPE) == 0) 342 n = n->parent; 343 344 assert(n->type == ROFFT_HEAD); 345 n = n->parent; 346 assert(n->type == ROFFT_BLOCK); 347 assert(man_macro(n->tok)->flags & MAN_BSCOPED); 348 349 mandoc_msg(MANDOCERR_BLK_LINE, n->line, n->pos, 350 "%s breaks %s", roff_name[tok], roff_name[n->tok]); 351 352 roff_node_delete(man, n); 353 man->flags &= ~(MAN_BLINE | ROFF_NONOFILL); 354 } 355 } 356