+
+
+/*
+ * Determine the symbol indicated by an escape sequences, that is, one
+ * starting with a backslash. Once done, we pass this value into the
+ * output buffer by way of the symbol table.
+ */
+static void
+nescape(struct termp *p, const char *word, size_t len)
+{
+
+ switch (len) {
+ case (1):
+ switch (word[0]) {
+ case ('\\'):
+ /* FALLTHROUGH */
+ case ('\''):
+ /* FALLTHROUGH */
+ case ('`'):
+ /* FALLTHROUGH */
+ case ('-'):
+ /* FALLTHROUGH */
+ case (' '):
+ /* FALLTHROUGH */
+ case ('.'):
+ chara(p, word[0]); /* FIXME */
+ break;
+ case ('&'):
+ break;
+ case ('e'):
+ chara(p, '\\'); /* FIXME */
+ break;
+ case ('q'):
+ symbola(p, TERMSYM_DQUOTE);
+ break;
+ default:
+ warnx("escape sequence not supported: %c",
+ word[0]);
+ break;
+ }
+ break;
+
+ case (2):
+ if ('r' == word[0] && 'B' == word[1])
+ symbola(p, TERMSYM_RBRACK);
+ else if ('l' == word[0] && 'B' == word[1])
+ symbola(p, TERMSYM_LBRACK);
+ else if ('l' == word[0] && 'q' == word[1])
+ symbola(p, TERMSYM_LDQUOTE);
+ else if ('r' == word[0] && 'q' == word[1])
+ symbola(p, TERMSYM_RDQUOTE);
+ else if ('o' == word[0] && 'q' == word[1])
+ symbola(p, TERMSYM_LSQUOTE);
+ else if ('a' == word[0] && 'q' == word[1])
+ symbola(p, TERMSYM_RSQUOTE);
+ else if ('<' == word[0] && '-' == word[1])
+ symbola(p, TERMSYM_LARROW);
+ else if ('-' == word[0] && '>' == word[1])
+ symbola(p, TERMSYM_RARROW);
+ else if ('b' == word[0] && 'u' == word[1])
+ symbola(p, TERMSYM_BULLET);
+ else if ('<' == word[0] && '=' == word[1])
+ symbola(p, TERMSYM_LE);
+ else if ('>' == word[0] && '=' == word[1])
+ symbola(p, TERMSYM_GE);
+ else if ('=' == word[0] && '=' == word[1])
+ symbola(p, TERMSYM_EQ);
+ else if ('+' == word[0] && '-' == word[1])
+ symbola(p, TERMSYM_PLUSMINUS);
+ else if ('u' == word[0] && 'a' == word[1])
+ symbola(p, TERMSYM_UARROW);
+ else if ('d' == word[0] && 'a' == word[1])
+ symbola(p, TERMSYM_DARROW);
+ else if ('a' == word[0] && 'a' == word[1])
+ symbola(p, TERMSYM_ACUTE);
+ else if ('g' == word[0] && 'a' == word[1])
+ symbola(p, TERMSYM_GRAVE);
+ else if ('!' == word[0] && '=' == word[1])
+ symbola(p, TERMSYM_NEQ);
+ else if ('i' == word[0] && 'f' == word[1])
+ symbola(p, TERMSYM_INF);
+ else if ('n' == word[0] && 'a' == word[1])
+ symbola(p, TERMSYM_NAN);
+ else if ('b' == word[0] && 'a' == word[1])
+ symbola(p, TERMSYM_BAR);
+
+ /* Deprecated forms. */
+ else if ('A' == word[0] && 'm' == word[1])
+ symbola(p, TERMSYM_AMP);
+ else if ('B' == word[0] && 'a' == word[1])
+ symbola(p, TERMSYM_BAR);
+ else if ('I' == word[0] && 'f' == word[1])
+ symbola(p, TERMSYM_INF2);
+ else if ('G' == word[0] && 'e' == word[1])
+ symbola(p, TERMSYM_GE);
+ else if ('G' == word[0] && 't' == word[1])
+ symbola(p, TERMSYM_GT);
+ else if ('L' == word[0] && 'e' == word[1])
+ symbola(p, TERMSYM_LE);
+ else if ('L' == word[0] && 'q' == word[1])
+ symbola(p, TERMSYM_LDQUOTE);
+ else if ('L' == word[0] && 't' == word[1])
+ symbola(p, TERMSYM_LT);
+ else if ('N' == word[0] && 'a' == word[1])
+ symbola(p, TERMSYM_NAN);
+ else if ('N' == word[0] && 'e' == word[1])
+ symbola(p, TERMSYM_NEQ);
+ else if ('P' == word[0] && 'i' == word[1])
+ symbola(p, TERMSYM_PI);
+ else if ('P' == word[0] && 'm' == word[1])
+ symbola(p, TERMSYM_PLUSMINUS);
+ else if ('R' == word[0] && 'q' == word[1])
+ symbola(p, TERMSYM_RDQUOTE);
+ else
+ warnx("escape sequence not supported: %c%c",
+ word[0], word[1]);
+ break;
+
+ default:
+ warnx("escape sequence not supported");
+ break;
+ }
+}
+
+
+/*
+ * Apply a style to the output buffer. This is looked up by means of
+ * the styletab.
+ */
+static void
+style(struct termp *p, enum tstyle esc)
+{
+
+ if (p->col + 4 >= p->maxcols)
+ errx(1, "line overrun");
+
+ p->buf[(p->col)++] = 27;
+ p->buf[(p->col)++] = '[';
+ switch (esc) {
+ case (TERMSTYLE_CLEAR):
+ p->buf[(p->col)++] = '0';
+ break;
+ case (TERMSTYLE_BOLD):
+ p->buf[(p->col)++] = '1';
+ break;
+ case (TERMSTYLE_UNDER):
+ p->buf[(p->col)++] = '4';
+ break;
+ default:
+ abort();
+ /* NOTREACHED */
+ }
+ p->buf[(p->col)++] = 'm';
+}
+
+
+/*
+ * Handle an escape sequence: determine its length and pass it to the
+ * escape-symbol look table. Note that we assume mdoc(3) has validated
+ * the escape sequence (we assert upon badly-formed escape sequences).
+ */
+static void
+pescape(struct termp *p, const char *word, size_t *i, size_t len)
+{
+ size_t j;
+
+ (*i)++;
+ assert(*i < len);
+
+ if ('(' == word[*i]) {
+ (*i)++;
+ assert(*i + 1 < len);
+ nescape(p, &word[*i], 2);
+ (*i)++;
+ return;
+
+ } else if ('*' == word[*i]) {
+ /* XXX - deprecated! */
+ (*i)++;
+ assert(*i < len);
+ switch (word[*i]) {
+ case ('('):
+ (*i)++;
+ assert(*i + 1 < len);
+ nescape(p, &word[*i], 2);
+ (*i)++;
+ return;
+ case ('['):
+ break;
+ default:
+ nescape(p, &word[*i], 1);
+ return;
+ }
+
+ } else if ('[' != word[*i]) {
+ nescape(p, &word[*i], 1);
+ return;
+ }
+
+ (*i)++;
+ for (j = 0; word[*i] && ']' != word[*i]; (*i)++, j++)
+ /* Loop... */ ;
+
+ assert(word[*i]);
+ nescape(p, &word[*i - j], j);
+}
+
+
+/*
+ * Handle pwords, partial words, which may be either a single word or a
+ * phrase that cannot be broken down (such as a literal string). This
+ * handles word styling.
+ */
+static void
+pword(struct termp *p, const char *word, size_t len)
+{
+ size_t i;
+
+ if ( ! (TERMP_NOSPACE & p->flags) &&
+ ! (TERMP_LITERAL & p->flags))
+ chara(p, ' ');
+
+ if ( ! (p->flags & TERMP_NONOSPACE))
+ p->flags &= ~TERMP_NOSPACE;
+
+ /*
+ * XXX - if literal and underlining, this will underline the
+ * spaces between literal words.
+ */
+
+ if (p->flags & TERMP_BOLD)
+ style(p, TERMSTYLE_BOLD);
+ if (p->flags & TERMP_UNDERLINE)
+ style(p, TERMSTYLE_UNDER);
+
+ for (i = 0; i < len; i++) {
+ if ('\\' == word[i]) {
+ pescape(p, word, &i, len);
+ continue;
+ }
+ chara(p, word[i]);
+ }
+
+ if (p->flags & TERMP_BOLD ||
+ p->flags & TERMP_UNDERLINE)
+ style(p, TERMSTYLE_CLEAR);
+}
+
+
+/*
+ * Add a symbol to the output line buffer.
+ */
+static void
+symbola(struct termp *p, enum tsym sym)
+{
+
+ assert(p->symtab[sym].sym);
+ stringa(p, p->symtab[sym].sym, p->symtab[sym].sz);
+}
+
+
+/*
+ * Like chara() but for arbitrary-length buffers. Resize the buffer by
+ * a factor of two (if the buffer is less than that) or the buffer's
+ * size.
+ */
+static void
+stringa(struct termp *p, const char *c, size_t sz)
+{
+ size_t s;
+
+ s = sz > p->maxcols * 2 ? sz : p->maxcols * 2;
+
+ assert(c);
+ if (p->col + sz >= p->maxcols) {
+ p->buf = realloc(p->buf, s);
+ if (NULL == p->buf)
+ err(1, "realloc");
+ p->maxcols = s;
+ }
+
+ (void)memcpy(&p->buf[p->col], c, sz);
+ p->col += sz;
+}
+
+
+/*
+ * Insert a single character into the line-buffer. If the buffer's
+ * space is exceeded, then allocate more space by doubling the buffer
+ * size.
+ */
+static void
+chara(struct termp *p, char c)
+{
+
+ if (p->col + 1 >= p->maxcols) {
+ p->buf = realloc(p->buf, p->maxcols * 2);
+ if (NULL == p->buf)
+ err(1, "malloc");
+ p->maxcols *= 2;
+ }
+ p->buf[(p->col)++] = c;
+}