Print this page
9718 update mandoc to 1.14.4
   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 *);


  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)


 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:


 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",


 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 }
   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 *);


  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)


 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:


 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",


 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 }