/* * Parse a roff node's type from the input buffer. This must be in the * form of ".foo xxx" in the usual way. */ static enum rofft roff_parse(struct roff *r, const char *buf, int *pos) { const char *mac; size_t maclen; enum rofft t; if ('\0' == buf[*pos] || '"' == buf[*pos] || '\t' == buf[*pos] || ' ' == buf[*pos]) return(ROFF_MAX); /* * We stop the macro parse at an escape, tab, space, or nil. * However, `\}' is also a valid macro, so make sure we don't * clobber it by seeing the `\' as the end of token. */ mac = buf + *pos; maclen = strcspn(mac + 1, " \\\t\0") + 1; t = (r->current_string = roff_getstrn(r, mac, maclen)) ? ROFF_USERDEF : roffhash_find(mac, maclen); *pos += (int)maclen; while (buf[*pos] && ' ' == buf[*pos]) (*pos)++; return(t); }
/* * 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); } cp++; /* * The third character decides the length * of the name of the string. * Save a pointer to the name. */ switch (*cp) { case ('\0'): return(ROFF_CONT); case ('('): cp++; maxl = 2; break; case ('['): cp++; maxl = 0; break; default: maxl = 1; break; } 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); return(ROFF_CONT); } if (0 == maxl && ']' == *cp) break; } /* * 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); res = ""; } /* 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; /* Just leave the string unexpanded. */ mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, ln, pos, NULL); return(ROFF_IGN); } return(ROFF_CONT); }
/* * 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); } 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'): return(ROFF_CONT); case ('('): cp++; maxl = 2; break; case ('['): cp++; maxl = 0; break; default: maxl = 1; break; } 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); return(ROFF_CONT); } if (0 == maxl && ']' == *cp) break; } /* * 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); res = ""; } /* 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; /* Just leave the string unexpanded. */ mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, ln, pos, NULL); return(ROFF_IGN); } return(ROFF_CONT); }