1 /*      $Id: term_ascii.c,v 1.21 2013/06/01 14:27:20 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
   4  *
   5  * Permission to use, copy, modify, and distribute this software for any
   6  * purpose with or without fee is hereby granted, provided that the above
   7  * copyright notice and this permission notice appear in all copies.
   8  *
   9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16  */
  17 #ifdef HAVE_CONFIG_H
  18 #include "config.h"
  19 #endif
  20 
  21 #include <sys/types.h>
  22 
  23 #include <assert.h>
  24 #ifdef USE_WCHAR
  25 # include <locale.h>
  26 #endif
  27 #include <stdint.h>
  28 #include <stdio.h>
  29 #include <stdlib.h>
  30 #include <unistd.h>
  31 #ifdef USE_WCHAR
  32 # include <wchar.h>
  33 #endif
  34 
  35 #include "mandoc.h"
  36 #include "out.h"
  37 #include "term.h"
  38 #include "main.h"
  39 
  40 /* 
  41  * Sadly, this doesn't seem to be defined on systems even when they
  42  * support it.  For the time being, remove it and let those compiling
  43  * the software decide for themselves what to use.
  44  */
  45 #if 0
  46 #if ! defined(__STDC_ISO_10646__)
  47 # undef USE_WCHAR
  48 #endif
  49 #endif
  50 
  51 static  struct termp     *ascii_init(enum termenc, char *);
  52 static  double            ascii_hspan(const struct termp *,
  53                                 const struct roffsu *);
  54 static  size_t            ascii_width(const struct termp *, int);
  55 static  void              ascii_advance(struct termp *, size_t);
  56 static  void              ascii_begin(struct termp *);
  57 static  void              ascii_end(struct termp *);
  58 static  void              ascii_endline(struct termp *);
  59 static  void              ascii_letter(struct termp *, int);
  60 
  61 #ifdef  USE_WCHAR
  62 static  void              locale_advance(struct termp *, size_t);
  63 static  void              locale_endline(struct termp *);
  64 static  void              locale_letter(struct termp *, int);
  65 static  size_t            locale_width(const struct termp *, int);
  66 #endif
  67 
  68 static struct termp *
  69 ascii_init(enum termenc enc, char *outopts)
  70 {
  71         const char      *toks[4];
  72         char            *v;
  73         struct termp    *p;
  74 
  75         p = mandoc_calloc(1, sizeof(struct termp));
  76 
  77         p->tabwidth = 5;
  78         p->defrmargin = 78;
  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->width = ascii_width;
  90 
  91 #ifdef  USE_WCHAR
  92         if (TERMENC_ASCII != enc) {
  93                 v = TERMENC_LOCALE == enc ?
  94                         setlocale(LC_ALL, "") :
  95                         setlocale(LC_CTYPE, "en_US.UTF-8");
  96                 if (NULL != v && MB_CUR_MAX > 1) {
  97                         p->enc = enc;
  98                         p->advance = locale_advance;
  99                         p->endline = locale_endline;
 100                         p->letter = locale_letter;
 101                         p->width = locale_width;
 102                 }
 103         }
 104 #endif
 105 
 106         toks[0] = "indent";
 107         toks[1] = "width";
 108         toks[2] = "mdoc";
 109         toks[3] = NULL;
 110 
 111         while (outopts && *outopts)
 112                 switch (getsubopt(&outopts, UNCONST(toks), &v)) {
 113                 case (0):
 114                         p->defindent = (size_t)atoi(v);
 115                         break;
 116                 case (1):
 117                         p->defrmargin = (size_t)atoi(v);
 118                         break;
 119                 case (2):
 120                         /*
 121                          * Temporary, undocumented mode
 122                          * to imitate mdoc(7) output style.
 123                          */
 124                         p->mdocstyle = 1;
 125                         p->defindent = 5;
 126                         break;
 127                 default:
 128                         break;
 129                 }
 130 
 131         /* Enforce a lower boundary. */
 132         if (p->defrmargin < 58)
 133                 p->defrmargin = 58;
 134 
 135         return(p);
 136 }
 137 
 138 void *
 139 ascii_alloc(char *outopts)
 140 {
 141 
 142         return(ascii_init(TERMENC_ASCII, outopts));
 143 }
 144 
 145 void *
 146 utf8_alloc(char *outopts)
 147 {
 148 
 149         return(ascii_init(TERMENC_UTF8, outopts));
 150 }
 151 
 152 
 153 void *
 154 locale_alloc(char *outopts)
 155 {
 156 
 157         return(ascii_init(TERMENC_LOCALE, outopts));
 158 }
 159 
 160 /* ARGSUSED */
 161 static size_t
 162 ascii_width(const struct termp *p, int c)
 163 {
 164 
 165         return(1);
 166 }
 167 
 168 void
 169 ascii_free(void *arg)
 170 {
 171 
 172         term_free((struct termp *)arg);
 173 }
 174 
 175 /* ARGSUSED */
 176 static void
 177 ascii_letter(struct termp *p, int c)
 178 {
 179         
 180         putchar(c);
 181 }
 182 
 183 static void
 184 ascii_begin(struct termp *p)
 185 {
 186 
 187         (*p->headf)(p, p->argf);
 188 }
 189 
 190 static void
 191 ascii_end(struct termp *p)
 192 {
 193 
 194         (*p->footf)(p, p->argf);
 195 }
 196 
 197 /* ARGSUSED */
 198 static void
 199 ascii_endline(struct termp *p)
 200 {
 201 
 202         putchar('\n');
 203 }
 204 
 205 /* ARGSUSED */
 206 static void
 207 ascii_advance(struct termp *p, size_t len)
 208 {
 209         size_t          i;
 210 
 211         for (i = 0; i < len; i++)
 212                 putchar(' ');
 213 }
 214 
 215 /* ARGSUSED */
 216 static double
 217 ascii_hspan(const struct termp *p, const struct roffsu *su)
 218 {
 219         double           r;
 220 
 221         /*
 222          * Approximate based on character width.  These are generated
 223          * entirely by eyeballing the screen, but appear to be correct.
 224          */
 225 
 226         switch (su->unit) {
 227         case (SCALE_CM):
 228                 r = 4 * su->scale;
 229                 break;
 230         case (SCALE_IN):
 231                 r = 10 * su->scale;
 232                 break;
 233         case (SCALE_PC):
 234                 r = (10 * su->scale) / 6;
 235                 break;
 236         case (SCALE_PT):
 237                 r = (10 * su->scale) / 72;
 238                 break;
 239         case (SCALE_MM):
 240                 r = su->scale / 1000;
 241                 break;
 242         case (SCALE_VS):
 243                 r = su->scale * 2 - 1;
 244                 break;
 245         default:
 246                 r = su->scale;
 247                 break;
 248         }
 249 
 250         return(r);
 251 }
 252 
 253 #ifdef USE_WCHAR
 254 /* ARGSUSED */
 255 static size_t
 256 locale_width(const struct termp *p, int c)
 257 {
 258         int             rc;
 259 
 260         return((rc = wcwidth(c)) < 0 ? 0 : rc);
 261 }
 262 
 263 /* ARGSUSED */
 264 static void
 265 locale_advance(struct termp *p, size_t len)
 266 {
 267         size_t          i;
 268 
 269         for (i = 0; i < len; i++)
 270                 putwchar(L' ');
 271 }
 272 
 273 /* ARGSUSED */
 274 static void
 275 locale_endline(struct termp *p)
 276 {
 277 
 278         putwchar(L'\n');
 279 }
 280 
 281 /* ARGSUSED */
 282 static void
 283 locale_letter(struct termp *p, int c)
 284 {
 285         
 286         putwchar(c);
 287 }
 288 #endif