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