1 /*      $Id: term_ascii.c,v 1.61 2018/05/20 21:37:34 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
   4  * Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
   5  *
   6  * Permission to use, copy, modify, and distribute this software for any
   7  * purpose with or without fee is hereby granted, provided that the above
   8  * copyright notice and this permission notice appear in all copies.
   9  *
  10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
  11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
  13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17  */
  18 #include "config.h"
  19 
  20 #include <sys/types.h>
  21 
  22 #include <assert.h>
  23 #if HAVE_WCHAR
  24 #include <langinfo.h>
  25 #include <locale.h>
  26 #endif
  27 #include <stdint.h>
  28 #include <stdio.h>
  29 #include <stdlib.h>
  30 #include <string.h>
  31 #include <unistd.h>
  32 #if HAVE_WCHAR
  33 #include <wchar.h>
  34 #endif
  35 
  36 #include "mandoc.h"
  37 #include "mandoc_aux.h"
  38 #include "out.h"
  39 #include "term.h"
  40 #include "manconf.h"
  41 #include "main.h"
  42 
  43 static  struct termp     *ascii_init(enum termenc, const struct manoutput *);
  44 static  int               ascii_hspan(const struct termp *,
  45                                 const struct roffsu *);
  46 static  size_t            ascii_width(const struct termp *, int);
  47 static  void              ascii_advance(struct termp *, size_t);
  48 static  void              ascii_begin(struct termp *);
  49 static  void              ascii_end(struct termp *);
  50 static  void              ascii_endline(struct termp *);
  51 static  void              ascii_letter(struct termp *, int);
  52 static  void              ascii_setwidth(struct termp *, int, int);
  53 
  54 #if HAVE_WCHAR
  55 static  void              locale_advance(struct termp *, size_t);
  56 static  void              locale_endline(struct termp *);
  57 static  void              locale_letter(struct termp *, int);
  58 static  size_t            locale_width(const struct termp *, int);
  59 #endif
  60 
  61 
  62 static struct termp *
  63 ascii_init(enum termenc enc, const struct manoutput *outopts)
  64 {
  65 #if HAVE_WCHAR
  66         char            *v;
  67 #endif
  68         struct termp    *p;
  69 
  70         p = mandoc_calloc(1, sizeof(*p));
  71         p->tcol = p->tcols = mandoc_calloc(1, sizeof(*p->tcol));
  72         p->maxtcol = 1;
  73 
  74         p->line = 1;
  75         p->defrmargin = p->lastrmargin = 78;
  76         p->fontq = mandoc_reallocarray(NULL,
  77              (p->fontsz = 8), sizeof(*p->fontq));
  78         p->fontq[0] = p->fontl = TERMFONT_NONE;
  79 
  80         p->begin = ascii_begin;
  81         p->end = ascii_end;
  82         p->hspan = ascii_hspan;
  83         p->type = TERMTYPE_CHAR;
  84 
  85         p->enc = TERMENC_ASCII;
  86         p->advance = ascii_advance;
  87         p->endline = ascii_endline;
  88         p->letter = ascii_letter;
  89         p->setwidth = ascii_setwidth;
  90         p->width = ascii_width;
  91 
  92 #if HAVE_WCHAR
  93         if (TERMENC_ASCII != enc) {
  94 
  95                 /*
  96                  * Do not change any of this to LC_ALL.  It might break
  97                  * the formatting by subtly changing the behaviour of
  98                  * various functions, for example strftime(3).  As a
  99                  * worst case, it might even cause buffer overflows.
 100                  */
 101 
 102                 v = TERMENC_LOCALE == enc ?
 103                     setlocale(LC_CTYPE, "") :
 104                     setlocale(LC_CTYPE, UTF8_LOCALE);
 105 
 106                 /*
 107                  * We only support UTF-8,
 108                  * so revert to ASCII for anything else.
 109                  */
 110 
 111                 if (v != NULL &&
 112                     strcmp(nl_langinfo(CODESET), "UTF-8") != 0)
 113                         v = setlocale(LC_CTYPE, "C");
 114 
 115                 if (v != NULL && MB_CUR_MAX > 1) {
 116                         p->enc = enc;
 117                         p->advance = locale_advance;
 118                         p->endline = locale_endline;
 119                         p->letter = locale_letter;
 120                         p->width = locale_width;
 121                 }
 122         }
 123 #endif
 124 
 125         if (outopts->mdoc) {
 126                 p->mdocstyle = 1;
 127                 p->defindent = 5;
 128         }
 129         if (outopts->indent)
 130                 p->defindent = outopts->indent;
 131         if (outopts->width)
 132                 p->defrmargin = outopts->width;
 133         if (outopts->synopsisonly)
 134                 p->synopsisonly = 1;
 135 
 136         assert(p->defindent < UINT16_MAX);
 137         assert(p->defrmargin < UINT16_MAX);
 138         return p;
 139 }
 140 
 141 void *
 142 ascii_alloc(const struct manoutput *outopts)
 143 {
 144 
 145         return ascii_init(TERMENC_ASCII, outopts);
 146 }
 147 
 148 void *
 149 utf8_alloc(const struct manoutput *outopts)
 150 {
 151 
 152         return ascii_init(TERMENC_UTF8, outopts);
 153 }
 154 
 155 void *
 156 locale_alloc(const struct manoutput *outopts)
 157 {
 158 
 159         return ascii_init(TERMENC_LOCALE, outopts);
 160 }
 161 
 162 static void
 163 ascii_setwidth(struct termp *p, int iop, int width)
 164 {
 165 
 166         width /= 24;
 167         p->tcol->rmargin = p->defrmargin;
 168         if (iop > 0)
 169                 p->defrmargin += width;
 170         else if (iop == 0)
 171                 p->defrmargin = width ? (size_t)width : p->lastrmargin;
 172         else if (p->defrmargin > (size_t)width)
 173                 p->defrmargin -= width;
 174         else
 175                 p->defrmargin = 0;
 176         if (p->defrmargin > 1000)
 177                 p->defrmargin = 1000;
 178         p->lastrmargin = p->tcol->rmargin;
 179         p->tcol->rmargin = p->maxrmargin = p->defrmargin;
 180 }
 181 
 182 void
 183 terminal_sepline(void *arg)
 184 {
 185         struct termp    *p;
 186         size_t           i;
 187 
 188         p = (struct termp *)arg;
 189         (*p->endline)(p);
 190         for (i = 0; i < p->defrmargin; i++)
 191                 (*p->letter)(p, '-');
 192         (*p->endline)(p);
 193         (*p->endline)(p);
 194 }
 195 
 196 static size_t
 197 ascii_width(const struct termp *p, int c)
 198 {
 199 
 200         return 1;
 201 }
 202 
 203 void
 204 ascii_free(void *arg)
 205 {
 206 
 207         term_free((struct termp *)arg);
 208 }
 209 
 210 static void
 211 ascii_letter(struct termp *p, int c)
 212 {
 213 
 214         putchar(c);
 215 }
 216 
 217 static void
 218 ascii_begin(struct termp *p)
 219 {
 220 
 221         (*p->headf)(p, p->argf);
 222 }
 223 
 224 static void
 225 ascii_end(struct termp *p)
 226 {
 227 
 228         (*p->footf)(p, p->argf);
 229 }
 230 
 231 static void
 232 ascii_endline(struct termp *p)
 233 {
 234 
 235         p->line++;
 236         p->tcol->offset -= p->ti;
 237         p->ti = 0;
 238         putchar('\n');
 239 }
 240 
 241 static void
 242 ascii_advance(struct termp *p, size_t len)
 243 {
 244         size_t          i;
 245 
 246         assert(len < UINT16_MAX);
 247         for (i = 0; i < len; i++)
 248                 putchar(' ');
 249 }
 250 
 251 static int
 252 ascii_hspan(const struct termp *p, const struct roffsu *su)
 253 {
 254         double           r;
 255 
 256         switch (su->unit) {
 257         case SCALE_BU:
 258                 r = su->scale;
 259                 break;
 260         case SCALE_CM:
 261                 r = su->scale * 240.0 / 2.54;
 262                 break;
 263         case SCALE_FS:
 264                 r = su->scale * 65536.0;
 265                 break;
 266         case SCALE_IN:
 267                 r = su->scale * 240.0;
 268                 break;
 269         case SCALE_MM:
 270                 r = su->scale * 0.24;
 271                 break;
 272         case SCALE_VS:
 273         case SCALE_PC:
 274                 r = su->scale * 40.0;
 275                 break;
 276         case SCALE_PT:
 277                 r = su->scale * 10.0 / 3.0;
 278                 break;
 279         case SCALE_EN:
 280         case SCALE_EM:
 281                 r = su->scale * 24.0;
 282                 break;
 283         default:
 284                 abort();
 285         }
 286         return r > 0.0 ? r + 0.01 : r - 0.01;
 287 }
 288 
 289 const char *
 290 ascii_uc2str(int uc)
 291 {
 292         static const char nbrsp[2] = { ASCII_NBRSP, '\0' };
 293         static const char *tab[] = {
 294         "<NUL>","<SOH>","<STX>","<ETX>","<EOT>","<ENQ>","<ACK>","<BEL>",
 295         "<BS>",   "\t",   "<LF>",   "<VT>",   "<FF>",   "<CR>",   "<SO>",   "<SI>",
 296         "<DLE>","<DC1>","<DC2>","<DC3>","<DC4>","<NAK>","<SYN>","<ETB>",
 297         "<CAN>","<EM>",     "<SUB>","<ESC>","<FS>",       "<GS>",   "<RS>",   "<US>",
 298         " ",    "!",    "\"",   "#",    "$",    "%",    "&",        "'",
 299         "(",    ")",    "*",    "+",    ",",    "-",    ".",    "/",
 300         "0",    "1",    "2",    "3",    "4",    "5",    "6",    "7",
 301         "8",    "9",    ":",    ";",    "<", "=",    ">", "?",
 302         "@",    "A",    "B",    "C",    "D",    "E",    "F",    "G",
 303         "H",    "I",    "J",    "K",    "L",    "M",    "N",    "O",
 304         "P",    "Q",    "R",    "S",    "T",    "U",    "V",    "W",
 305         "X",    "Y",    "Z",    "[",    "\\",   "]",    "^",    "_",
 306         "`",    "a",    "b",    "c",    "d",    "e",    "f",    "g",
 307         "h",    "i",    "j",    "k",    "l",    "m",    "n",    "o",
 308         "p",    "q",    "r",    "s",    "t",    "u",    "v",    "w",
 309         "x",    "y",    "z",    "{",    "|",    "}",    "~",    "<DEL>",
 310         "<80>",   "<81>",   "<82>",   "<83>",   "<84>",   "<85>",   "<86>",   "<87>",
 311         "<88>",   "<89>",   "<8A>",   "<8B>",   "<8C>",   "<8D>",   "<8E>",   "<8F>",
 312         "<90>",   "<91>",   "<92>",   "<93>",   "<94>",   "<95>",   "<96>",   "<97>",
 313         "<98>",   "<99>",   "<9A>",   "<9B>",   "<9C>",   "<9D>",   "<9E>",   "<9F>",
 314         nbrsp,  "!",    "/\bc", "GBP",  "o\bx", "=\bY", "|",    "<section>",
 315         "\"",   "(C)",  "_\ba", "<<",     "~",    "",     "(R)",  "-",
 316         "<degree>","+-","^2",     "^3",   "'","<micro>","<paragraph>",".",
 317         ",",    "^1",   "_\bo", ">>",     "1/4",  "1/2",  "3/4",  "?",
 318         "`\bA", "'\bA", "^\bA", "~\bA", "\"\bA","o\bA", "AE",   ",\bC",
 319         "`\bE", "'\bE", "^\bE", "\"\bE","`\bI", "'\bI", "^\bI", "\"\bI",
 320         "Dh",   "~\bN", "`\bO", "'\bO", "^\bO", "~\bO", "\"\bO","x",
 321         "/\bO", "`\bU", "'\bU", "^\bU", "\"\bU","'\bY", "Th",   "ss",
 322         "`\ba", "'\ba", "^\ba", "~\ba", "\"\ba","o\ba", "ae",   ",\bc",
 323         "`\be", "'\be", "^\be", "\"\be","`\bi", "'\bi", "^\bi", "\"\bi",
 324         "dh",   "~\bn", "`\bo", "'\bo", "^\bo", "~\bo", "\"\bo","/",
 325         "/\bo", "`\bu", "'\bu", "^\bu", "\"\bu","'\by", "th",   "\"\by",
 326         "A",    "a",    "A",    "a",    "A",    "a",    "'\bC", "'\bc",
 327         "^\bC", "^\bc", "C",    "c",    "C",    "c",    "D",    "d",
 328         "/\bD", "/\bd", "E",    "e",    "E",    "e",    "E",    "e",
 329         "E",    "e",    "E",    "e",    "^\bG", "^\bg", "G",    "g",
 330         "G",    "g",    ",\bG", ",\bg", "^\bH", "^\bh", "/\bH", "/\bh",
 331         "~\bI", "~\bi", "I",    "i",    "I",    "i",    "I",    "i",
 332         "I",    "i",    "IJ",   "ij",   "^\bJ", "^\bj", ",\bK", ",\bk",
 333         "q",    "'\bL", "'\bl", ",\bL", ",\bl", "L",    "l",    "L",
 334         "l",    "/\bL", "/\bl", "'\bN", "'\bn", ",\bN", ",\bn", "N",
 335         "n",    "'n",   "Ng",   "ng",   "O",    "o",    "O",    "o",
 336         "O",    "o",    "OE",   "oe",   "'\bR", "'\br", ",\bR", ",\br",
 337         "R",    "r",    "'\bS", "'\bs", "^\bS", "^\bs", ",\bS", ",\bs",
 338         "S",    "s",    ",\bT", ",\bt", "T",    "t",    "/\bT", "/\bt",
 339         "~\bU", "~\bu", "U",    "u",    "U",    "u",    "U",    "u",
 340         "U",    "u",    "U",    "u",    "^\bW", "^\bw", "^\bY", "^\by",
 341         "\"\bY","'\bZ", "'\bz", "Z",    "z",    "Z",    "z",    "s",
 342         "b",    "B",    "B",    "b",    "6",    "6",    "O",    "C",
 343         "c",    "D",    "D",    "D",    "d",    "d",    "3",    "@",
 344         "E",    "F",    ",\bf", "G",    "G",    "hv",   "I",    "/\bI",
 345         "K",    "k",    "/\bl", "l",    "W",    "N",    "n",    "~\bO",
 346         "O",    "o",    "OI",   "oi",   "P",    "p",    "YR",   "2",
 347         "2",    "SH",   "sh",   "t",    "T",    "t",    "T",    "U",
 348         "u",    "Y",    "V",    "Y",    "y",    "/\bZ", "/\bz", "ZH",
 349         "ZH",   "zh",   "zh",   "/\b2", "5",    "5",    "ts",   "w",
 350         "|",    "||",   "|=",   "!",    "DZ",   "Dz",   "dz",   "LJ",
 351         "Lj",   "lj",   "NJ",   "Nj",   "nj",   "A",    "a",    "I",
 352         "i",    "O",    "o",    "U",    "u",    "U",    "u",    "U",
 353         "u",    "U",    "u",    "U",    "u",    "@",    "A",    "a",
 354         "A",    "a",    "AE",   "ae",   "/\bG", "/\bg", "G",    "g",
 355         "K",    "k",    "O",    "o",    "O",    "o",    "ZH",   "zh",
 356         "j",    "DZ",   "Dz",   "dz",   "'\bG", "'\bg", "HV",   "W",
 357         "`\bN", "`\bn", "A",    "a",    "'\bAE","'\bae","O",    "o"};
 358 
 359         assert(uc >= 0);
 360         if ((size_t)uc < sizeof(tab)/sizeof(tab[0]))
 361                 return tab[uc];
 362         return mchars_uc2str(uc);
 363 }
 364 
 365 #if HAVE_WCHAR
 366 static size_t
 367 locale_width(const struct termp *p, int c)
 368 {
 369         int             rc;
 370 
 371         if (c == ASCII_NBRSP)
 372                 c = ' ';
 373         rc = wcwidth(c);
 374         if (rc < 0)
 375                 rc = 0;
 376         return rc;
 377 }
 378 
 379 static void
 380 locale_advance(struct termp *p, size_t len)
 381 {
 382         size_t          i;
 383 
 384         assert(len < UINT16_MAX);
 385         for (i = 0; i < len; i++)
 386                 putwchar(L' ');
 387 }
 388 
 389 static void
 390 locale_endline(struct termp *p)
 391 {
 392 
 393         p->line++;
 394         p->tcol->offset -= p->ti;
 395         p->ti = 0;
 396         putwchar(L'\n');
 397 }
 398 
 399 static void
 400 locale_letter(struct termp *p, int c)
 401 {
 402 
 403         putwchar(c);
 404 }
 405 #endif