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