Print this page
5051 import mdocml-1.12.3
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Approved by: TBD

*** 1,9 **** ! /* $Id: roff.c,v 1.172 2011/10/24 21:41:45 schwarze Exp $ */ /* ! * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> ! * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * --- 1,9 ---- ! /* $Id: roff.c,v 1.189 2013/12/30 18:44:06 schwarze Exp $ */ /* ! * Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> ! * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. *
*** 19,28 **** --- 19,29 ---- #include "config.h" #endif #include <assert.h> #include <ctype.h> + #include <stdio.h> #include <stdlib.h> #include <string.h> #include "mandoc.h" #include "libroff.h"
*** 37,51 **** --- 38,55 ---- enum rofft { ROFF_ad, ROFF_am, ROFF_ami, ROFF_am1, + ROFF_cc, ROFF_de, ROFF_dei, ROFF_de1, ROFF_ds, ROFF_el, + ROFF_fam, + ROFF_hw, ROFF_hy, ROFF_ie, ROFF_if, ROFF_ig, ROFF_it,
*** 56,65 **** --- 60,71 ---- ROFF_ps, ROFF_rm, ROFF_so, ROFF_ta, ROFF_tr, + ROFF_Dd, + ROFF_TH, ROFF_TS, ROFF_TE, ROFF_T_, ROFF_EQ, ROFF_EN,
*** 68,92 **** ROFF_USERDEF, ROFF_MAX }; enum roffrule { ! ROFFRULE_ALLOW, ! ROFFRULE_DENY }; /* - * A single register entity. If "set" is zero, the value of the - * register should be the default one, which is per-register. - * Registers are assumed to be unsigned ints for now. - */ - struct reg { - int set; /* whether set or not */ - unsigned int u; /* unsigned integer */ - }; - - /* * An incredibly-simple string buffer. */ struct roffstr { char *p; /* nil-terminated buffer */ size_t sz; /* saved strlen(p) */ --- 74,88 ---- ROFF_USERDEF, ROFF_MAX }; enum roffrule { ! ROFFRULE_DENY, ! ROFFRULE_ALLOW }; /* * An incredibly-simple string buffer. */ struct roffstr { char *p; /* nil-terminated buffer */ size_t sz; /* saved strlen(p) */
*** 99,114 **** struct roffstr key; struct roffstr val; struct roffkv *next; /* next in list */ }; struct roff { struct mparse *parse; /* parse point */ struct roffnode *last; /* leaf of stack */ enum roffrule rstack[RSTACK_MAX]; /* stack of !`ie' rules */ int rstackpos; /* position in rstack */ ! struct reg regs[REG__MAX]; struct roffkv *strtab; /* user-defined strings & macros */ struct roffkv *xmbtab; /* multi-byte trans table (`tr') */ struct roffstr *xtab; /* single-byte trans table (`tr') */ const char *current_string; /* value of last called user macro */ struct tbl_node *first_tbl; /* first table parsed */ --- 95,121 ---- struct roffstr key; struct roffstr val; struct roffkv *next; /* next in list */ }; + /* + * A single number register as part of a singly-linked list. + */ + struct roffreg { + struct roffstr key; + int val; + struct roffreg *next; + }; + struct roff { + enum mparset parsetype; /* requested parse type */ struct mparse *parse; /* parse point */ struct roffnode *last; /* leaf of stack */ enum roffrule rstack[RSTACK_MAX]; /* stack of !`ie' rules */ + char control; /* control character */ int rstackpos; /* position in rstack */ ! struct roffreg *regtab; /* number registers */ struct roffkv *strtab; /* user-defined strings & macros */ struct roffkv *xmbtab; /* multi-byte trans table (`tr') */ struct roffstr *xtab; /* single-byte trans table (`tr') */ const char *current_string; /* value of last called user macro */ struct tbl_node *first_tbl; /* first table parsed */
*** 167,202 **** const char *, int, int); static enum rofferr roff_block(ROFF_ARGS); static enum rofferr roff_block_text(ROFF_ARGS); static enum rofferr roff_block_sub(ROFF_ARGS); static enum rofferr roff_cblock(ROFF_ARGS); static enum rofferr roff_ccond(ROFF_ARGS); static enum rofferr roff_cond(ROFF_ARGS); static enum rofferr roff_cond_text(ROFF_ARGS); static enum rofferr roff_cond_sub(ROFF_ARGS); static enum rofferr roff_ds(ROFF_ARGS); static enum roffrule roff_evalcond(const char *, int *); static void roff_free1(struct roff *); static void roff_freestr(struct roffkv *); static char *roff_getname(struct roff *, char **, int, int); static const char *roff_getstrn(const struct roff *, const char *, size_t); static enum rofferr roff_line_ignore(ROFF_ARGS); static enum rofferr roff_nr(ROFF_ARGS); static void roff_openeqn(struct roff *, const char *, int, int, const char *); static enum rofft roff_parse(struct roff *, const char *, int *); ! static enum rofferr roff_parsetext(char *); static enum rofferr roff_res(struct roff *, char **, size_t *, int, int); static enum rofferr roff_rm(ROFF_ARGS); static void roff_setstr(struct roff *, const char *, const char *, int); static void roff_setstrn(struct roffkv **, const char *, size_t, const char *, size_t, int); static enum rofferr roff_so(ROFF_ARGS); static enum rofferr roff_tr(ROFF_ARGS); static enum rofferr roff_TE(ROFF_ARGS); static enum rofferr roff_TS(ROFF_ARGS); static enum rofferr roff_EQ(ROFF_ARGS); static enum rofferr roff_EN(ROFF_ARGS); static enum rofferr roff_T_(ROFF_ARGS); --- 174,218 ---- const char *, int, int); static enum rofferr roff_block(ROFF_ARGS); static enum rofferr roff_block_text(ROFF_ARGS); static enum rofferr roff_block_sub(ROFF_ARGS); static enum rofferr roff_cblock(ROFF_ARGS); + static enum rofferr roff_cc(ROFF_ARGS); static enum rofferr roff_ccond(ROFF_ARGS); static enum rofferr roff_cond(ROFF_ARGS); static enum rofferr roff_cond_text(ROFF_ARGS); static enum rofferr roff_cond_sub(ROFF_ARGS); static enum rofferr roff_ds(ROFF_ARGS); static enum roffrule roff_evalcond(const char *, int *); static void roff_free1(struct roff *); + static void roff_freereg(struct roffreg *); static void roff_freestr(struct roffkv *); static char *roff_getname(struct roff *, char **, int, int); + static int roff_getnum(const char *, int *, int *); + static int roff_getop(const char *, int *, char *); + static int roff_getregn(const struct roff *, + const char *, size_t); static const char *roff_getstrn(const struct roff *, const char *, size_t); + static enum rofferr roff_it(ROFF_ARGS); static enum rofferr roff_line_ignore(ROFF_ARGS); static enum rofferr roff_nr(ROFF_ARGS); static void roff_openeqn(struct roff *, const char *, int, int, const char *); static enum rofft roff_parse(struct roff *, const char *, int *); ! static enum rofferr roff_parsetext(char **, size_t *, int, int *); static enum rofferr roff_res(struct roff *, char **, size_t *, int, int); static enum rofferr roff_rm(ROFF_ARGS); static void roff_setstr(struct roff *, const char *, const char *, int); static void roff_setstrn(struct roffkv **, const char *, size_t, const char *, size_t, int); static enum rofferr roff_so(ROFF_ARGS); static enum rofferr roff_tr(ROFF_ARGS); + static enum rofferr roff_Dd(ROFF_ARGS); + static enum rofferr roff_TH(ROFF_ARGS); static enum rofferr roff_TE(ROFF_ARGS); static enum rofferr roff_TS(ROFF_ARGS); static enum rofferr roff_EQ(ROFF_ARGS); static enum rofferr roff_EN(ROFF_ARGS); static enum rofferr roff_T_(ROFF_ARGS);
*** 213,241 **** static struct roffmac roffs[ROFF_MAX] = { { "ad", roff_line_ignore, NULL, NULL, 0, NULL }, { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL }, { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL }, { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL }, { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL }, { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, { "ds", roff_ds, NULL, NULL, 0, NULL }, { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, { "hy", roff_line_ignore, NULL, NULL, 0, NULL }, { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, { "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL }, ! { "it", roff_line_ignore, NULL, NULL, 0, NULL }, { "ne", roff_line_ignore, NULL, NULL, 0, NULL }, { "nh", roff_line_ignore, NULL, NULL, 0, NULL }, { "nr", roff_nr, NULL, NULL, 0, NULL }, { "ns", roff_line_ignore, NULL, NULL, 0, NULL }, { "ps", roff_line_ignore, NULL, NULL, 0, NULL }, { "rm", roff_rm, NULL, NULL, 0, NULL }, { "so", roff_so, NULL, NULL, 0, NULL }, { "ta", roff_line_ignore, NULL, NULL, 0, NULL }, { "tr", roff_tr, NULL, NULL, 0, NULL }, { "TS", roff_TS, NULL, NULL, 0, NULL }, { "TE", roff_TE, NULL, NULL, 0, NULL }, { "T&", roff_T_, NULL, NULL, 0, NULL }, { "EQ", roff_EQ, NULL, NULL, 0, NULL }, { "EN", roff_EN, NULL, NULL, 0, NULL }, --- 229,262 ---- static struct roffmac roffs[ROFF_MAX] = { { "ad", roff_line_ignore, NULL, NULL, 0, NULL }, { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL }, { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL }, { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, + { "cc", roff_cc, NULL, NULL, 0, NULL }, { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL }, { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL }, { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, { "ds", roff_ds, NULL, NULL, 0, NULL }, { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, + { "fam", roff_line_ignore, NULL, NULL, 0, NULL }, + { "hw", roff_line_ignore, NULL, NULL, 0, NULL }, { "hy", roff_line_ignore, NULL, NULL, 0, NULL }, { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, { "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL }, ! { "it", roff_it, NULL, NULL, 0, NULL }, { "ne", roff_line_ignore, NULL, NULL, 0, NULL }, { "nh", roff_line_ignore, NULL, NULL, 0, NULL }, { "nr", roff_nr, NULL, NULL, 0, NULL }, { "ns", roff_line_ignore, NULL, NULL, 0, NULL }, { "ps", roff_line_ignore, NULL, NULL, 0, NULL }, { "rm", roff_rm, NULL, NULL, 0, NULL }, { "so", roff_so, NULL, NULL, 0, NULL }, { "ta", roff_line_ignore, NULL, NULL, 0, NULL }, { "tr", roff_tr, NULL, NULL, 0, NULL }, + { "Dd", roff_Dd, NULL, NULL, 0, NULL }, + { "TH", roff_TH, NULL, NULL, 0, NULL }, { "TS", roff_TS, NULL, NULL, 0, NULL }, { "TE", roff_TE, NULL, NULL, 0, NULL }, { "T&", roff_T_, NULL, NULL, 0, NULL }, { "EQ", roff_EQ, NULL, NULL, 0, NULL }, { "EN", roff_EN, NULL, NULL, 0, NULL },
*** 242,260 **** --- 263,315 ---- { ".", roff_cblock, NULL, NULL, 0, NULL }, { "\\}", roff_ccond, NULL, NULL, 0, NULL }, { NULL, roff_userdef, NULL, NULL, 0, NULL }, }; + const char *const __mdoc_reserved[] = { + "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At", + "Bc", "Bd", "Bf", "Bk", "Bl", "Bo", "Bq", + "Brc", "Bro", "Brq", "Bsx", "Bt", "Bx", + "Cd", "Cm", "Db", "Dc", "Dd", "Dl", "Do", "Dq", + "Ds", "Dt", "Dv", "Dx", "D1", + "Ec", "Ed", "Ef", "Ek", "El", "Em", "em", + "En", "Eo", "Eq", "Er", "Es", "Ev", "Ex", + "Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Fr", "Ft", "Fx", + "Hf", "Ic", "In", "It", "Lb", "Li", "Lk", "Lp", "LP", + "Me", "Ms", "Mt", "Nd", "Nm", "No", "Ns", "Nx", + "Oc", "Oo", "Op", "Os", "Ot", "Ox", + "Pa", "Pc", "Pf", "Po", "Pp", "PP", "pp", "Pq", + "Qc", "Ql", "Qo", "Qq", "Or", "Rd", "Re", "Rs", "Rv", + "Sc", "Sf", "Sh", "SH", "Sm", "So", "Sq", + "Ss", "St", "Sx", "Sy", + "Ta", "Tn", "Ud", "Ux", "Va", "Vt", "Xc", "Xo", "Xr", + "%A", "%B", "%D", "%I", "%J", "%N", "%O", + "%P", "%Q", "%R", "%T", "%U", "%V", + NULL + }; + + const char *const __man_reserved[] = { + "AT", "B", "BI", "BR", "BT", "DE", "DS", "DT", + "EE", "EN", "EQ", "EX", "HF", "HP", "I", "IB", "IP", "IR", + "LP", "ME", "MT", "OP", "P", "PD", "PP", "PT", + "R", "RB", "RE", "RI", "RS", "SB", "SH", "SM", "SS", "SY", + "TE", "TH", "TP", "TQ", "TS", "T&", "UC", "UE", "UR", "YS", + NULL + }; + /* Array of injected predefined strings. */ #define PREDEFS_MAX 38 static const struct predef predefs[PREDEFS_MAX] = { #include "predefs.in" }; /* See roffhash_find() */ #define ROFF_HASH(p) (p[0] - ASCII_LO) + static int roffit_lines; /* number of lines to delay */ + static char *roffit_macro; /* nil-terminated macro line */ + static void roffhash_init(void) { struct roffmac *n; int buc, i;
*** 349,365 **** static void roff_free1(struct roff *r) { ! struct tbl_node *t; struct eqn_node *e; int i; ! while (NULL != (t = r->first_tbl)) { ! r->first_tbl = t->next; ! tbl_free(t); } r->first_tbl = r->last_tbl = r->tbl = NULL; while (NULL != (e = r->first_eqn)) { --- 404,420 ---- static void roff_free1(struct roff *r) { ! struct tbl_node *tbl; struct eqn_node *e; int i; ! while (NULL != (tbl = r->first_tbl)) { ! r->first_tbl = tbl->next; ! tbl_free(tbl); } r->first_tbl = r->last_tbl = r->tbl = NULL; while (NULL != (e = r->first_eqn)) {
*** 375,384 **** --- 430,443 ---- roff_freestr(r->strtab); roff_freestr(r->xmbtab); r->strtab = r->xmbtab = NULL; + roff_freereg(r->regtab); + + r->regtab = NULL; + if (r->xtab) for (i = 0; i < 128; i++) free(r->xtab[i].p); free(r->xtab);
*** 390,400 **** { int i; roff_free1(r); ! memset(&r->regs, 0, sizeof(struct reg) * REG__MAX); for (i = 0; i < PREDEFS_MAX; i++) roff_setstr(r, predefs[i].name, predefs[i].str, 0); } --- 449,459 ---- { int i; roff_free1(r); ! r->control = 0; for (i = 0; i < PREDEFS_MAX; i++) roff_setstr(r, predefs[i].name, predefs[i].str, 0); }
*** 407,422 **** free(r); } struct roff * ! roff_alloc(struct mparse *parse) { struct roff *r; int i; r = mandoc_calloc(1, sizeof(struct roff)); r->parse = parse; r->rstackpos = -1; roffhash_init(); --- 466,482 ---- free(r); } struct roff * ! roff_alloc(enum mparset type, struct mparse *parse) { struct roff *r; int i; r = mandoc_calloc(1, sizeof(struct roff)); + r->parsetype = type; r->parse = parse; r->rstackpos = -1; roffhash_init();
*** 425,473 **** return(r); } /* ! * Pre-filter each and every line for reserved words (one beginning with ! * `\*', e.g., `\*(ab'). These must be handled before the actual line ! * is processed. ! * This also checks the syntax of regular escapes. */ static enum rofferr roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos) { ! enum mandoc_esc esc; const char *stesc; /* start of an escape sequence ('\\') */ const char *stnam; /* start of the name, after "[(*" */ const char *cp; /* end of the name, e.g. before ']' */ const char *res; /* the string to be substituted */ ! int i, maxl, expand_count; ! size_t nsz; ! char *n; expand_count = 0; again: cp = *bufp + pos; while (NULL != (cp = strchr(cp, '\\'))) { stesc = cp++; /* ! * The second character must be an asterisk. * If it isn't, skip it anyway: It is escaped, * so it can't start another escape sequence. */ if ('\0' == *cp) return(ROFF_CONT); ! if ('*' != *cp) { ! res = cp; ! esc = mandoc_escape(&cp, NULL, NULL); ! if (ESCAPE_ERROR != esc) continue; - cp = res; mandoc_msg (MANDOCERR_BADESCAPE, r->parse, ln, (int)(stesc - *bufp), NULL); return(ROFF_CONT); } --- 485,538 ---- return(r); } /* ! * In the current line, expand user-defined strings ("\*") ! * and references to number registers ("\n"). ! * Also check the syntax of other escape sequences. */ static enum rofferr roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos) { ! char ubuf[12]; /* buffer to print the number */ const char *stesc; /* start of an escape sequence ('\\') */ const char *stnam; /* start of the name, after "[(*" */ const char *cp; /* end of the name, e.g. before ']' */ const char *res; /* the string to be substituted */ ! char *nbuf; /* new buffer to copy bufp to */ ! size_t nsz; /* size of the new buffer */ ! size_t maxl; /* expected length of the escape name */ ! size_t naml; /* actual length of the escape name */ ! int expand_count; /* to avoid infinite loops */ expand_count = 0; again: cp = *bufp + pos; while (NULL != (cp = strchr(cp, '\\'))) { stesc = cp++; /* ! * The second character must be an asterisk or an n. * If it isn't, skip it anyway: It is escaped, * so it can't start another escape sequence. */ if ('\0' == *cp) return(ROFF_CONT); ! switch (*cp) { ! case ('*'): ! res = NULL; ! break; ! case ('n'): ! res = ubuf; ! break; ! default: ! if (ESCAPE_ERROR != mandoc_escape(&cp, NULL, NULL)) continue; mandoc_msg (MANDOCERR_BADESCAPE, r->parse, ln, (int)(stesc - *bufp), NULL); return(ROFF_CONT); }
*** 474,484 **** cp++; /* * The third character decides the length ! * of the name of the string. * Save a pointer to the name. */ switch (*cp) { case ('\0'): --- 539,549 ---- cp++; /* * The third character decides the length ! * of the name of the string or register. * Save a pointer to the name. */ switch (*cp) { case ('\0'):
*** 497,507 **** } stnam = cp; /* Advance to the end of the name. */ ! for (i = 0; 0 == maxl || i < maxl; i++, cp++) { if ('\0' == *cp) { mandoc_msg (MANDOCERR_BADESCAPE, r->parse, ln, (int)(stesc - *bufp), NULL); --- 562,572 ---- } stnam = cp; /* Advance to the end of the name. */ ! for (naml = 0; 0 == maxl || naml < maxl; naml++, cp++) { if ('\0' == *cp) { mandoc_msg (MANDOCERR_BADESCAPE, r->parse, ln, (int)(stesc - *bufp), NULL);
*** 514,524 **** /* * Retrieve the replacement string; if it is * undefined, resume searching for escapes. */ ! res = roff_getstrn(r, stnam, (size_t)i); if (NULL == res) { mandoc_msg (MANDOCERR_BADESCAPE, r->parse, ln, (int)(stesc - *bufp), NULL); --- 579,593 ---- /* * Retrieve the replacement string; if it is * undefined, resume searching for escapes. */ ! if (NULL == res) ! res = roff_getstrn(r, stnam, naml); ! else ! snprintf(ubuf, sizeof(ubuf), "%d", ! roff_getregn(r, stnam, naml)); if (NULL == res) { mandoc_msg (MANDOCERR_BADESCAPE, r->parse, ln, (int)(stesc - *bufp), NULL);
*** 528,546 **** /* Replace the escape sequence by the string. */ pos = stesc - *bufp; nsz = *szp + strlen(res) + 1; ! n = mandoc_malloc(nsz); ! strlcpy(n, *bufp, (size_t)(stesc - *bufp + 1)); ! strlcat(n, res, nsz); ! strlcat(n, cp + (maxl ? 0 : 1), nsz); free(*bufp); ! *bufp = n; *szp = nsz; if (EXPAND_LIMIT >= ++expand_count) goto again; --- 597,615 ---- /* Replace the escape sequence by the string. */ pos = stesc - *bufp; nsz = *szp + strlen(res) + 1; ! nbuf = mandoc_malloc(nsz); ! strlcpy(nbuf, *bufp, (size_t)(stesc - *bufp + 1)); ! strlcat(nbuf, res, nsz); ! strlcat(nbuf, cp + (maxl ? 0 : 1), nsz); free(*bufp); ! *bufp = nbuf; *szp = nsz; if (EXPAND_LIMIT >= ++expand_count) goto again;
*** 550,569 **** } return(ROFF_CONT); } /* ! * Process text streams: convert all breakable hyphens into ASCII_HYPH. */ static enum rofferr ! roff_parsetext(char *p) { size_t sz; const char *start; enum mandoc_esc esc; ! start = p; while ('\0' != *p) { sz = strcspn(p, "-\\"); p += sz; --- 619,642 ---- } return(ROFF_CONT); } /* ! * Process text streams: ! * Convert all breakable hyphens into ASCII_HYPH. ! * Decrement and spring input line trap. */ static enum rofferr ! roff_parsetext(char **bufp, size_t *szp, int pos, int *offs) { size_t sz; const char *start; + char *p; + int isz; enum mandoc_esc esc; ! start = p = *bufp + pos; while ('\0' != *p) { sz = strcspn(p, "-\\"); p += sz;
*** 571,582 **** break; if ('\\' == *p) { /* Skip over escapes. */ p++; ! esc = mandoc_escape ! ((const char **)&p, NULL, NULL); if (ESCAPE_ERROR == esc) break; continue; } else if (p == start) { p++; --- 644,654 ---- break; if ('\\' == *p) { /* Skip over escapes. */ p++; ! esc = mandoc_escape((const char **)&p, NULL, NULL); if (ESCAPE_ERROR == esc) break; continue; } else if (p == start) { p++;
*** 587,596 **** --- 659,684 ---- isalpha((unsigned char)p[1])) *p = ASCII_HYPH; p++; } + /* Spring the input line trap. */ + if (1 == roffit_lines) { + isz = asprintf(&p, "%s\n.%s", *bufp, roffit_macro); + if (-1 == isz) { + perror(NULL); + exit((int)MANDOCLEVEL_SYSERR); + } + free(*bufp); + *bufp = p; + *szp = isz + 1; + *offs = 0; + free(roffit_macro); + roffit_lines = 0; + return(ROFF_REPARSE); + } else if (1 < roffit_lines) + --roffit_lines; return(ROFF_CONT); } enum rofferr roff_parseln(struct roff *r, int ln, char **bufp,
*** 609,619 **** if (ROFF_IGN == e) return(e); assert(ROFF_CONT == e); ppos = pos; ! ctl = mandoc_getcontrol(*bufp, &pos); /* * First, if a scope is open and we're not a macro, pass the * text through the macro's filter. If a scope isn't open and * we're not a macro, just let it through. --- 697,707 ---- if (ROFF_IGN == e) return(e); assert(ROFF_CONT == e); ppos = pos; ! ctl = roff_getcontrol(r, *bufp, &pos); /* * First, if a scope is open and we're not a macro, pass the * text through the macro's filter. If a scope isn't open and * we're not a macro, just let it through.
*** 627,649 **** e = (*roffs[t].text) (r, t, bufp, szp, ln, pos, pos, offs); assert(ROFF_IGN == e || ROFF_CONT == e); if (ROFF_CONT != e) return(e); if (r->eqn) ! return(eqn_read(&r->eqn, ln, *bufp, pos, offs)); if (r->tbl) return(tbl_read(r->tbl, ln, *bufp, pos)); ! return(roff_parsetext(*bufp + pos)); ! } else if ( ! ctl) { ! if (r->eqn) ! return(eqn_read(&r->eqn, ln, *bufp, pos, offs)); ! if (r->tbl) ! return(tbl_read(r->tbl, ln, *bufp, pos)); ! return(roff_parsetext(*bufp + pos)); ! } else if (r->eqn) ! return(eqn_read(&r->eqn, ln, *bufp, ppos, offs)); /* * If a scope is open, go to the child handler for that macro, * as it may want to preprocess before doing anything with it. * Don't do so if an equation is open. --- 715,732 ---- e = (*roffs[t].text) (r, t, bufp, szp, ln, pos, pos, offs); assert(ROFF_IGN == e || ROFF_CONT == e); if (ROFF_CONT != e) return(e); + } if (r->eqn) ! return(eqn_read(&r->eqn, ln, *bufp, ppos, offs)); ! if ( ! ctl) { if (r->tbl) return(tbl_read(r->tbl, ln, *bufp, pos)); ! return(roff_parsetext(bufp, szp, pos, offs)); ! } /* * If a scope is open, go to the child handler for that macro, * as it may want to preprocess before doing anything with it. * Don't do so if an equation is open.
*** 776,786 **** static void roffnode_cleanscope(struct roff *r) { while (r->last) { ! if (--r->last->endspan < 0) break; roffnode_pop(r); } } --- 859,869 ---- static void roffnode_cleanscope(struct roff *r) { while (r->last) { ! if (--r->last->endspan != 0) break; roffnode_pop(r); } }
*** 982,1013 **** enum roffrule rr; char *ep; rr = r->last->rule; roffnode_cleanscope(r); /* ! * If the macro is unknown, first check if it contains a closing ! * delimiter `\}'. If it does, close out our scope and return ! * the currently-scoped rule (ignore or continue). Else, drop ! * into the currently-scoped rule. */ ! if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos))) { ep = &(*bufp)[pos]; ! for ( ; NULL != (ep = strchr(ep, '\\')); ep++) { ! ep++; ! if ('}' != *ep) continue; /* - * Make the \} go away. - * This is a little haphazard, as it's not quite - * clear how nroff does this. * If we're at the end of line, then just chop * off the \} and resize the buffer. ! * If we aren't, then conver it to spaces. */ if ('\0' == *(ep + 1)) { *--ep = '\0'; *szp -= 2; --- 1065,1100 ---- enum roffrule rr; char *ep; rr = r->last->rule; roffnode_cleanscope(r); + t = roff_parse(r, *bufp, &pos); /* ! * Fully handle known macros when they are structurally ! * required or when the conditional evaluated to true. */ ! if ((ROFF_MAX != t) && ! (ROFF_ccond == t || ROFFRULE_ALLOW == rr || ! ROFFMAC_STRUCT & roffs[t].flags)) { ! assert(roffs[t].proc); ! return((*roffs[t].proc)(r, t, bufp, szp, ! ln, ppos, pos, offs)); ! } ! ! /* Always check for the closing delimiter `\}'. */ ! ep = &(*bufp)[pos]; ! while (NULL != (ep = strchr(ep, '\\'))) { ! if ('}' != *(++ep)) continue; /* * If we're at the end of line, then just chop * off the \} and resize the buffer. ! * If we aren't, then convert it to spaces. */ if ('\0' == *(ep + 1)) { *--ep = '\0'; *szp -= 2;
*** 1017,1042 **** roff_ccond(r, ROFF_ccond, bufp, szp, ln, pos, pos + 2, offs); break; } return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); - } - - /* - * A denied conditional must evaluate its children if and only - * if they're either structurally required (such as loops and - * conditionals) or a closing macro. - */ - - if (ROFFRULE_DENY == rr) - if ( ! (ROFFMAC_STRUCT & roffs[t].flags)) - if (ROFF_ccond != t) - return(ROFF_IGN); - - assert(roffs[t].proc); - return((*roffs[t].proc)(r, t, bufp, szp, - ln, ppos, pos, offs)); } /* ARGSUSED */ static enum rofferr roff_cond_text(ROFF_ARGS) --- 1104,1113 ----
*** 1057,1069 **** --- 1128,1192 ---- ln, pos, pos + 2, offs); } return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); } + static int + roff_getnum(const char *v, int *pos, int *res) + { + int p, n; + + p = *pos; + n = v[p] == '-'; + if (n) + p++; + + for (*res = 0; isdigit((unsigned char)v[p]); p++) + *res += 10 * *res + v[p] - '0'; + if (p == *pos + n) + return 0; + + if (n) + *res = -*res; + + *pos = p; + return 1; + } + + static int + roff_getop(const char *v, int *pos, char *res) + { + int e; + + *res = v[*pos]; + e = v[*pos + 1] == '='; + + switch (*res) { + case '=': + break; + case '>': + if (e) + *res = 'g'; + break; + case '<': + if (e) + *res = 'l'; + break; + default: + return(0); + } + + *pos += 1 + e; + + return(*res); + } + static enum roffrule roff_evalcond(const char *v, int *pos) { + int not, lh, rh; + char op; switch (v[*pos]) { case ('n'): (*pos)++; return(ROFFRULE_ALLOW);
*** 1072,1142 **** case ('o'): /* FALLTHROUGH */ case ('t'): (*pos)++; return(ROFFRULE_DENY); default: break; } ! while (v[*pos] && ' ' != v[*pos]) ! (*pos)++; ! return(ROFFRULE_DENY); } /* ARGSUSED */ static enum rofferr roff_line_ignore(ROFF_ARGS) { - if (ROFF_it == tok) - mandoc_msg(MANDOCERR_REQUEST, r->parse, ln, ppos, "it"); - return(ROFF_IGN); } /* ARGSUSED */ static enum rofferr roff_cond(ROFF_ARGS) { - int sv; - enum roffrule rule; /* * An `.el' has no conditional body: it will consume the value * of the current rstack entry set in prior `ie' calls or * defaults to DENY. * * If we're not an `el', however, then evaluate the conditional. */ ! rule = ROFF_el == tok ? (r->rstackpos < 0 ? ROFFRULE_DENY : r->rstack[r->rstackpos--]) : roff_evalcond(*bufp, &pos); - sv = pos; - while (' ' == (*bufp)[pos]) - pos++; - /* - * Roff is weird. If we have just white-space after the - * conditional, it's considered the BODY and we exit without - * really doing anything. Warn about this. It's probably - * wrong. - */ - - if ('\0' == (*bufp)[pos] && sv != pos) { - mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL); - return(ROFF_IGN); - } - - roffnode_push(r, tok, NULL, ln, ppos); - - r->last->rule = rule; - - /* * An if-else will put the NEGATION of the current evaluated * conditional into the stack of rules. */ if (ROFF_ie == tok) { --- 1195,1276 ---- case ('o'): /* FALLTHROUGH */ case ('t'): (*pos)++; return(ROFFRULE_DENY); + case ('!'): + (*pos)++; + not = 1; + break; default: + not = 0; break; } ! if (!roff_getnum(v, pos, &lh)) ! return ROFFRULE_DENY; ! if (!roff_getop(v, pos, &op)) { ! if (lh < 0) ! lh = 0; ! goto out; ! } ! if (!roff_getnum(v, pos, &rh)) ! return ROFFRULE_DENY; ! switch (op) { ! case 'g': ! lh = lh >= rh; ! break; ! case 'l': ! lh = lh <= rh; ! break; ! case '=': ! lh = lh == rh; ! break; ! case '>': ! lh = lh > rh; ! break; ! case '<': ! lh = lh < rh; ! break; ! default: ! return ROFFRULE_DENY; ! } ! out: ! if (not) ! lh = !lh; ! return lh ? ROFFRULE_ALLOW : ROFFRULE_DENY; } /* ARGSUSED */ static enum rofferr roff_line_ignore(ROFF_ARGS) { return(ROFF_IGN); } /* ARGSUSED */ static enum rofferr roff_cond(ROFF_ARGS) { + roffnode_push(r, tok, NULL, ln, ppos); + /* * An `.el' has no conditional body: it will consume the value * of the current rstack entry set in prior `ie' calls or * defaults to DENY. * * If we're not an `el', however, then evaluate the conditional. */ ! r->last->rule = ROFF_el == tok ? (r->rstackpos < 0 ? ROFFRULE_DENY : r->rstack[r->rstackpos--]) : roff_evalcond(*bufp, &pos); /* * An if-else will put the NEGATION of the current evaluated * conditional into the stack of rules. */ if (ROFF_ie == tok) {
*** 1154,1185 **** if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule) r->last->rule = ROFFRULE_DENY; /* ! * Determine scope. If we're invoked with "\{" trailing the ! * conditional, then we're in a multiline scope. Else our scope ! * expires on the next line. */ ! r->last->endspan = 1; if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) { r->last->endspan = -1; pos += 2; } /* ! * If there are no arguments on the line, the next-line scope is ! * assumed. */ if ('\0' == (*bufp)[pos]) ! return(ROFF_IGN); ! /* Otherwise re-run the roff parser after recalculating. */ *offs = pos; return(ROFF_RERUN); } --- 1288,1330 ---- if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule) r->last->rule = ROFFRULE_DENY; /* ! * Determine scope. ! * If there is nothing on the line after the conditional, ! * not even whitespace, use next-line scope. */ ! if ('\0' == (*bufp)[pos]) { ! r->last->endspan = 2; ! goto out; ! } + while (' ' == (*bufp)[pos]) + pos++; + + /* An opening brace requests multiline scope. */ + if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) { r->last->endspan = -1; pos += 2; + goto out; } /* ! * Anything else following the conditional causes ! * single-line scope. Warn if the scope contains ! * nothing but trailing whitespace. */ if ('\0' == (*bufp)[pos]) ! mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL); ! r->last->endspan = 1; + out: *offs = pos; return(ROFF_RERUN); }
*** 1211,1260 **** /* The rest is the value. */ roff_setstr(r, name, string, 0); return(ROFF_IGN); } int ! roff_regisset(const struct roff *r, enum regs reg) { ! return(r->regs[(int)reg].set); } ! unsigned int ! roff_regget(const struct roff *r, enum regs reg) { ! return(r->regs[(int)reg].u); } ! void ! roff_regunset(struct roff *r, enum regs reg) { ! r->regs[(int)reg].set = 0; } /* ARGSUSED */ static enum rofferr roff_nr(ROFF_ARGS) { const char *key; char *val; int iv; val = *bufp + pos; key = roff_getname(r, &val, ln, pos); ! if (0 == strcmp(key, "nS")) { ! r->regs[(int)REG_nS].set = 1; ! if ((iv = mandoc_strntoi(val, strlen(val), 10)) >= 0) ! r->regs[(int)REG_nS].u = (unsigned)iv; ! else ! r->regs[(int)REG_nS].u = 0u; ! } return(ROFF_IGN); } /* ARGSUSED */ static enum rofferr --- 1356,1454 ---- /* The rest is the value. */ roff_setstr(r, name, string, 0); return(ROFF_IGN); } + void + roff_setreg(struct roff *r, const char *name, int val, char sign) + { + struct roffreg *reg; + + /* Search for an existing register with the same name. */ + reg = r->regtab; + + while (reg && strcmp(name, reg->key.p)) + reg = reg->next; + + if (NULL == reg) { + /* Create a new register. */ + reg = mandoc_malloc(sizeof(struct roffreg)); + reg->key.p = mandoc_strdup(name); + reg->key.sz = strlen(name); + reg->val = 0; + reg->next = r->regtab; + r->regtab = reg; + } + + if ('+' == sign) + reg->val += val; + else if ('-' == sign) + reg->val -= val; + else + reg->val = val; + } + int ! roff_getreg(const struct roff *r, const char *name) { + struct roffreg *reg; ! for (reg = r->regtab; reg; reg = reg->next) ! if (0 == strcmp(name, reg->key.p)) ! return(reg->val); ! ! return(0); } ! static int ! roff_getregn(const struct roff *r, const char *name, size_t len) { + struct roffreg *reg; ! for (reg = r->regtab; reg; reg = reg->next) ! if (len == reg->key.sz && ! 0 == strncmp(name, reg->key.p, len)) ! return(reg->val); ! ! return(0); } ! static void ! roff_freereg(struct roffreg *reg) { + struct roffreg *old_reg; ! while (NULL != reg) { ! free(reg->key.p); ! old_reg = reg; ! reg = reg->next; ! free(old_reg); ! } } /* ARGSUSED */ static enum rofferr roff_nr(ROFF_ARGS) { const char *key; char *val; + size_t sz; int iv; + char sign; val = *bufp + pos; key = roff_getname(r, &val, ln, pos); ! sign = *val; ! if ('+' == sign || '-' == sign) ! val++; + sz = strspn(val, "0123456789"); + iv = sz ? mandoc_strntoi(val, sz, 10) : 0; + + roff_setreg(r, key, iv, sign); + return(ROFF_IGN); } /* ARGSUSED */ static enum rofferr
*** 1272,1281 **** --- 1466,1526 ---- return(ROFF_IGN); } /* ARGSUSED */ static enum rofferr + roff_it(ROFF_ARGS) + { + char *cp; + size_t len; + int iv; + + /* Parse the number of lines. */ + cp = *bufp + pos; + len = strcspn(cp, " \t"); + cp[len] = '\0'; + if ((iv = mandoc_strntoi(cp, len, 10)) <= 0) { + mandoc_msg(MANDOCERR_NUMERIC, r->parse, + ln, ppos, *bufp + 1); + return(ROFF_IGN); + } + cp += len + 1; + + /* Arm the input line trap. */ + roffit_lines = iv; + roffit_macro = mandoc_strdup(cp); + return(ROFF_IGN); + } + + /* ARGSUSED */ + static enum rofferr + roff_Dd(ROFF_ARGS) + { + const char *const *cp; + + if (MPARSE_MDOC != r->parsetype) + for (cp = __mdoc_reserved; *cp; cp++) + roff_setstr(r, *cp, NULL, 0); + + return(ROFF_CONT); + } + + /* ARGSUSED */ + static enum rofferr + roff_TH(ROFF_ARGS) + { + const char *const *cp; + + if (MPARSE_MDOC != r->parsetype) + for (cp = __man_reserved; *cp; cp++) + roff_setstr(r, *cp, NULL, 0); + + return(ROFF_CONT); + } + + /* ARGSUSED */ + static enum rofferr roff_TE(ROFF_ARGS) { if (NULL == r->tbl) mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
*** 1350,1379 **** /* ARGSUSED */ static enum rofferr roff_TS(ROFF_ARGS) { ! struct tbl_node *t; if (r->tbl) { mandoc_msg(MANDOCERR_SCOPEBROKEN, r->parse, ln, ppos, NULL); tbl_end(&r->tbl); } ! t = tbl_alloc(ppos, ln, r->parse); if (r->last_tbl) ! r->last_tbl->next = t; else ! r->first_tbl = r->last_tbl = t; ! r->tbl = r->last_tbl = t; return(ROFF_IGN); } /* ARGSUSED */ static enum rofferr roff_tr(ROFF_ARGS) { const char *p, *first, *second; size_t fsz, ssz; enum mandoc_esc esc; --- 1595,1641 ---- /* ARGSUSED */ static enum rofferr roff_TS(ROFF_ARGS) { ! struct tbl_node *tbl; if (r->tbl) { mandoc_msg(MANDOCERR_SCOPEBROKEN, r->parse, ln, ppos, NULL); tbl_end(&r->tbl); } ! tbl = tbl_alloc(ppos, ln, r->parse); if (r->last_tbl) ! r->last_tbl->next = tbl; else ! r->first_tbl = r->last_tbl = tbl; ! r->tbl = r->last_tbl = tbl; return(ROFF_IGN); } /* ARGSUSED */ static enum rofferr + roff_cc(ROFF_ARGS) + { + const char *p; + + p = *bufp + pos; + + if ('\0' == *p || '.' == (r->control = *p++)) + r->control = 0; + + if ('\0' != *p) + mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL); + + return(ROFF_IGN); + } + + /* ARGSUSED */ + static enum rofferr roff_tr(ROFF_ARGS) { const char *p, *first, *second; size_t fsz, ssz; enum mandoc_esc esc;
*** 1468,1478 **** char *cp, *n1, *n2; int i; /* * Collect pointers to macro argument strings ! * and null-terminate them. */ cp = *bufp + pos; for (i = 0; i < 9; i++) arg[i] = '\0' == *cp ? "" : mandoc_getarg(r->parse, &cp, ln, &pos); --- 1730,1740 ---- char *cp, *n1, *n2; int i; /* * Collect pointers to macro argument strings ! * and NUL-terminate them. */ cp = *bufp + pos; for (i = 0; i < 9; i++) arg[i] = '\0' == *cp ? "" : mandoc_getarg(r->parse, &cp, ln, &pos);
*** 1763,1768 **** --- 2025,2065 ---- ssz += sz; } res[(int)ssz] = '\0'; return(res); + } + + /* + * Find out whether a line is a macro line or not. + * If it is, adjust the current position and return one; if it isn't, + * return zero and don't change the current position. + * If the control character has been set with `.cc', then let that grain + * precedence. + * This is slighly contrary to groff, where using the non-breaking + * control character when `cc' has been invoked will cause the + * non-breaking macro contents to be printed verbatim. + */ + int + roff_getcontrol(const struct roff *r, const char *cp, int *ppos) + { + int pos; + + pos = *ppos; + + if (0 != r->control && cp[pos] == r->control) + pos++; + else if (0 != r->control) + return(0); + else if ('\\' == cp[pos] && '.' == cp[pos + 1]) + pos += 2; + else if ('.' == cp[pos] || '\'' == cp[pos]) + pos++; + else + return(0); + + while (' ' == cp[pos] || '\t' == cp[pos]) + pos++; + + *ppos = pos; + return(1); }