]> git.cameronkatri.com Git - mandoc.git/blobdiff - term_ascii.c
do not crash when a tbl(7) cell uses roman font
[mandoc.git] / term_ascii.c
index f217b9a405bc62b16f3a64a670558f1731b0df18..bf7e9b639e04e39385196b28a40145b87fabf041 100644 (file)
@@ -1,7 +1,7 @@
-/*     $Id: term_ascii.c,v 1.44 2015/03/27 21:33:20 schwarze Exp $ */
+/* $Id: term_ascii.c,v 1.66 2020/09/09 13:45:05 schwarze Exp $ */
 /*
  * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2014,2015,2017,2018,2020 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
 
 #include <assert.h>
 #if HAVE_WCHAR
+#include <langinfo.h>
 #include <locale.h>
 #endif
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 #if HAVE_WCHAR
 #include <wchar.h>
@@ -38,9 +40,8 @@
 #include "manconf.h"
 #include "main.h"
 
-static struct termp     *ascii_init(enum termenc, const struct mchars *,
-                               const struct manoutput *);
-static double            ascii_hspan(const struct termp *,
+static struct termp     *ascii_init(enum termenc, const struct manoutput *);
+static int               ascii_hspan(const struct termp *,
                                const struct roffsu *);
 static size_t            ascii_width(const struct termp *, int);
 static void              ascii_advance(struct termp *, size_t);
@@ -48,7 +49,7 @@ static        void              ascii_begin(struct termp *);
 static void              ascii_end(struct termp *);
 static void              ascii_endline(struct termp *);
 static void              ascii_letter(struct termp *, int);
-static void              ascii_setwidth(struct termp *, int, size_t);
+static void              ascii_setwidth(struct termp *, int, int);
 
 #if HAVE_WCHAR
 static void              locale_advance(struct termp *, size_t);
@@ -59,19 +60,21 @@ static      size_t            locale_width(const struct termp *, int);
 
 
 static struct termp *
-ascii_init(enum termenc enc, const struct mchars *mchars,
-       const struct manoutput *outopts)
+ascii_init(enum termenc enc, const struct manoutput *outopts)
 {
+#if HAVE_WCHAR
        char            *v;
+#endif
        struct termp    *p;
 
-       p = mandoc_calloc(1, sizeof(struct termp));
+       p = mandoc_calloc(1, sizeof(*p));
+       p->tcol = p->tcols = mandoc_calloc(1, sizeof(*p->tcol));
+       p->maxtcol = 1;
 
-       p->symtab = mchars;
-       p->tabwidth = 5;
+       p->line = 1;
        p->defrmargin = p->lastrmargin = 78;
        p->fontq = mandoc_reallocarray(NULL,
-            (p->fontsz = 8), sizeof(enum termfont));
+            (p->fontsz = 8), sizeof(*p->fontq));
        p->fontq[0] = p->fontl = TERMFONT_NONE;
 
        p->begin = ascii_begin;
@@ -87,12 +90,30 @@ ascii_init(enum termenc enc, const struct mchars *mchars,
        p->width = ascii_width;
 
 #if HAVE_WCHAR
-       if (TERMENC_ASCII != enc) {
-               v = TERMENC_LOCALE == enc ?
-                   setlocale(LC_ALL, "") :
-                   setlocale(LC_CTYPE, "en_US.UTF-8");
-               if (NULL != v && MB_CUR_MAX > 1) {
-                       p->enc = enc;
+       if (enc != TERMENC_ASCII) {
+
+               /*
+                * Do not change any of this to LC_ALL.  It might break
+                * the formatting by subtly changing the behaviour of
+                * various functions, for example strftime(3).  As a
+                * worst case, it might even cause buffer overflows.
+                */
+
+               v = enc == TERMENC_LOCALE ?
+                   setlocale(LC_CTYPE, "") :
+                   setlocale(LC_CTYPE, UTF8_LOCALE);
+
+               /*
+                * We only support UTF-8,
+                * so revert to ASCII for anything else.
+                */
+
+               if (v != NULL &&
+                   strcmp(nl_langinfo(CODESET), "UTF-8") != 0)
+                       v = setlocale(LC_CTYPE, "C");
+
+               if (v != NULL && MB_CUR_MAX > 1) {
+                       p->enc = TERMENC_UTF8;
                        p->advance = locale_advance;
                        p->endline = locale_endline;
                        p->letter = locale_letter;
@@ -112,66 +133,70 @@ ascii_init(enum termenc enc, const struct mchars *mchars,
        if (outopts->synopsisonly)
                p->synopsisonly = 1;
 
-       return(p);
+       assert(p->defindent < UINT16_MAX);
+       assert(p->defrmargin < UINT16_MAX);
+       return p;
 }
 
 void *
-ascii_alloc(const struct mchars *mchars, const struct manoutput *outopts)
+ascii_alloc(const struct manoutput *outopts)
 {
 
-       return(ascii_init(TERMENC_ASCII, mchars, outopts));
+       return ascii_init(TERMENC_ASCII, outopts);
 }
 
 void *
-utf8_alloc(const struct mchars *mchars, const struct manoutput *outopts)
+utf8_alloc(const struct manoutput *outopts)
 {
 
-       return(ascii_init(TERMENC_UTF8, mchars, outopts));
+       return ascii_init(TERMENC_UTF8, outopts);
 }
 
 void *
-locale_alloc(const struct mchars *mchars, const struct manoutput *outopts)
+locale_alloc(const struct manoutput *outopts)
 {
 
-       return(ascii_init(TERMENC_LOCALE, mchars, outopts));
+       return ascii_init(TERMENC_LOCALE, outopts);
 }
 
 static void
-ascii_setwidth(struct termp *p, int iop, size_t width)
+ascii_setwidth(struct termp *p, int iop, int width)
 {
 
-       p->rmargin = p->defrmargin;
+       width /= 24;
+       p->tcol->rmargin = p->defrmargin;
        if (iop > 0)
                p->defrmargin += width;
        else if (iop == 0)
-               p->defrmargin = width ? width : p->lastrmargin;
-       else if (p->defrmargin > width)
+               p->defrmargin = width ? (size_t)width : p->lastrmargin;
+       else if (p->defrmargin > (size_t)width)
                p->defrmargin -= width;
        else
                p->defrmargin = 0;
-       p->lastrmargin = p->rmargin;
-       p->rmargin = p->maxrmargin = p->defrmargin;
+       if (p->defrmargin > 1000)
+               p->defrmargin = 1000;
+       p->lastrmargin = p->tcol->rmargin;
+       p->tcol->rmargin = p->maxrmargin = p->defrmargin;
 }
 
 void
-ascii_sepline(void *arg)
+terminal_sepline(void *arg)
 {
        struct termp    *p;
        size_t           i;
 
        p = (struct termp *)arg;
-       putchar('\n');
+       (*p->endline)(p);
        for (i = 0; i < p->defrmargin; i++)
-               putchar('-');
-       putchar('\n');
-       putchar('\n');
+               (*p->letter)(p, '-');
+       (*p->endline)(p);
+       (*p->endline)(p);
 }
 
 static size_t
 ascii_width(const struct termp *p, int c)
 {
-
-       return(1);
+       return c != ASCII_BREAK;
 }
 
 void
@@ -206,6 +231,12 @@ static void
 ascii_endline(struct termp *p)
 {
 
+       p->line++;
+       if ((int)p->tcol->offset > p->ti)
+               p->tcol->offset -= p->ti;
+       else
+               p->tcol->offset = 0;
+       p->ti = 0;
        putchar('\n');
 }
 
@@ -214,56 +245,54 @@ ascii_advance(struct termp *p, size_t len)
 {
        size_t          i;
 
+       /*
+        * XXX We used to have "assert(len < UINT16_MAX)" here.
+        * that is not quite right because the input document
+        * can trigger that by merely providing large input.
+        * For now, simply truncate.
+        */
+       if (len > 256)
+               len = 256;
        for (i = 0; i < len; i++)
                putchar(' ');
 }
 
-static double
+static int
 ascii_hspan(const struct termp *p, const struct roffsu *su)
 {
        double           r;
 
-       /*
-        * Approximate based on character width.
-        * None of these will be actually correct given that an inch on
-        * the screen depends on character size, terminal, etc., etc.
-        */
        switch (su->unit) {
        case SCALE_BU:
-               r = su->scale * 10.0 / 240.0;
+               r = su->scale;
                break;
        case SCALE_CM:
-               r = su->scale * 10.0 / 2.54;
+               r = su->scale * 240.0 / 2.54;
                break;
        case SCALE_FS:
-               r = su->scale * 2730.666;
+               r = su->scale * 65536.0;
                break;
        case SCALE_IN:
-               r = su->scale * 10.0;
+               r = su->scale * 240.0;
                break;
        case SCALE_MM:
-               r = su->scale / 100.0;
+               r = su->scale * 0.24;
                break;
+       case SCALE_VS:
        case SCALE_PC:
-               r = su->scale * 10.0 / 6.0;
+               r = su->scale * 40.0;
                break;
        case SCALE_PT:
-               r = su->scale * 10.0 / 72.0;
-               break;
-       case SCALE_VS:
-               r = su->scale * 2.0 - 1.0;
+               r = su->scale * 10.0 / 3.0;
                break;
        case SCALE_EN:
-               /* FALLTHROUGH */
        case SCALE_EM:
-               r = su->scale;
+               r = su->scale * 24.0;
                break;
        default:
                abort();
-               /* NOTREACHED */
        }
-
-       return(r);
+       return r > 0.0 ? r + 0.01 : r - 0.01;
 }
 
 const char *
@@ -290,18 +319,18 @@ ascii_uc2str(int uc)
        "<80>", "<81>", "<82>", "<83>", "<84>", "<85>", "<86>", "<87>",
        "<88>", "<89>", "<8A>", "<8B>", "<8C>", "<8D>", "<8E>", "<8F>",
        "<90>", "<91>", "<92>", "<93>", "<94>", "<95>", "<96>", "<97>",
-       "<99>", "<99>", "<9A>", "<9B>", "<9C>", "<9D>", "<9E>", "<9F>",
-       nbrsp,  "!",    "/\bc", "GBP",  "o\bx", "=\bY", "|",    "<sec>",
+       "<98>", "<99>", "<9A>", "<9B>", "<9C>", "<9D>", "<9E>", "<9F>",
+       nbrsp,  "!",    "/\bc", "-\bL", "o\bx", "=\bY", "|",    "<section>",
        "\"",   "(C)",  "_\ba", "<<",   "~",    "",     "(R)",  "-",
-       "<deg>","+-",   "2",    "3",    "'",    ",\bu", "<par>",".",
-       ",",    "1",    "_\bo", ">>",   "1/4",  "1/2",  "3/4",  "?",
+       "<degree>","+-","^2",   "^3",   "'","<micro>","<paragraph>",".",
+       ",",    "^1",   "_\bo", ">>",   "1/4",  "1/2",  "3/4",  "?",
        "`\bA", "'\bA", "^\bA", "~\bA", "\"\bA","o\bA", "AE",   ",\bC",
        "`\bE", "'\bE", "^\bE", "\"\bE","`\bI", "'\bI", "^\bI", "\"\bI",
-       "-\bD", "~\bN", "`\bO", "'\bO", "^\bO", "~\bO", "\"\bO","x",
+       "Dh",   "~\bN", "`\bO", "'\bO", "^\bO", "~\bO", "\"\bO","x",
        "/\bO", "`\bU", "'\bU", "^\bU", "\"\bU","'\bY", "Th",   "ss",
        "`\ba", "'\ba", "^\ba", "~\ba", "\"\ba","o\ba", "ae",   ",\bc",
        "`\be", "'\be", "^\be", "\"\be","`\bi", "'\bi", "^\bi", "\"\bi",
-       "d",    "~\bn", "`\bo", "'\bo", "^\bo", "~\bo", "\"\bo","-:-",
+       "dh",   "~\bn", "`\bo", "'\bo", "^\bo", "~\bo", "\"\bo","/",
        "/\bo", "`\bu", "'\bu", "^\bu", "\"\bu","'\by", "th",   "\"\by",
        "A",    "a",    "A",    "a",    "A",    "a",    "'\bC", "'\bc",
        "^\bC", "^\bc", "C",    "c",    "C",    "c",    "D",    "d",
@@ -338,8 +367,8 @@ ascii_uc2str(int uc)
 
        assert(uc >= 0);
        if ((size_t)uc < sizeof(tab)/sizeof(tab[0]))
-               return(tab[uc]);
-       return(mchars_uc2str(uc));
+               return tab[uc];
+       return mchars_uc2str(uc);
 }
 
 #if HAVE_WCHAR
@@ -353,7 +382,7 @@ locale_width(const struct termp *p, int c)
        rc = wcwidth(c);
        if (rc < 0)
                rc = 0;
-       return(rc);
+       return rc;
 }
 
 static void
@@ -361,6 +390,14 @@ locale_advance(struct termp *p, size_t len)
 {
        size_t          i;
 
+       /*
+        * XXX We used to have "assert(len < UINT16_MAX)" here.
+        * that is not quite right because the input document
+        * can trigger that by merely providing large input.
+        * For now, simply truncate.
+        */
+       if (len > 256)
+               len = 256;
        for (i = 0; i < len; i++)
                putwchar(L' ');
 }
@@ -369,6 +406,12 @@ static void
 locale_endline(struct termp *p)
 {
 
+       p->line++;
+       if ((int)p->tcol->offset > p->ti)
+               p->tcol->offset -= p->ti;
+       else 
+               p->tcol->offset = 0;
+       p->ti = 0;
        putwchar(L'\n');
 }