1 /*      $Id: term_ascii.c,v 1.20 2011/12/04 23:10:52 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         p->enc = enc;
  77 
  78         p->tabwidth = 5;
  79         p->defrmargin = 78;
  80 
  81         p->begin = ascii_begin;
  82         p->end = ascii_end;
  83         p->hspan = ascii_hspan;
  84         p->type = TERMTYPE_CHAR;
  85 
  86         p->enc = TERMENC_ASCII;
  87         p->advance = ascii_advance;
  88         p->endline = ascii_endline;
  89         p->letter = ascii_letter;
  90         p->width = ascii_width;
  91 
  92 #ifdef  USE_WCHAR
  93         if (TERMENC_ASCII != enc) {
  94                 v = TERMENC_LOCALE == enc ?
  95                         setlocale(LC_ALL, "") :
  96                         setlocale(LC_CTYPE, "UTF-8");
  97                 if (NULL != v && MB_CUR_MAX > 1) {
  98                         p->enc = enc;
  99                         p->advance = locale_advance;
 100                         p->endline = locale_endline;
 101                         p->letter = locale_letter;
 102                         p->width = locale_width;
 103                 }
 104         }
 105 #endif
 106 
 107         toks[0] = "indent";
 108         toks[1] = "width";
 109         toks[2] = "mdoc";
 110         toks[3] = NULL;
 111 
 112         while (outopts && *outopts)
 113                 switch (getsubopt(&outopts, UNCONST(toks), &v)) {
 114                 case (0):
 115                         p->defindent = (size_t)atoi(v);
 116                         break;
 117                 case (1):
 118                         p->defrmargin = (size_t)atoi(v);
 119                         break;
 120                 case (2):
 121                         /*
 122                          * Temporary, undocumented mode
 123                          * to imitate mdoc(7) output style.
 124                          */
 125                         p->mdocstyle = 1;
 126                         p->defindent = 5;
 127                         break;
 128                 default:
 129                         break;
 130                 }
 131 
 132         /* Enforce a lower boundary. */
 133         if (p->defrmargin < 58)
 134                 p->defrmargin = 58;
 135 
 136         return(p);
 137 }
 138 
 139 void *
 140 ascii_alloc(char *outopts)
 141 {
 142 
 143         return(ascii_init(TERMENC_ASCII, outopts));
 144 }
 145 
 146 void *
 147 utf8_alloc(char *outopts)
 148 {
 149 
 150         return(ascii_init(TERMENC_UTF8, outopts));
 151 }
 152 
 153 
 154 void *
 155 locale_alloc(char *outopts)
 156 {
 157 
 158         return(ascii_init(TERMENC_LOCALE, outopts));
 159 }
 160 
 161 /* ARGSUSED */
 162 static size_t
 163 ascii_width(const struct termp *p, int c)
 164 {
 165 
 166         return(1);
 167 }
 168 
 169 void
 170 ascii_free(void *arg)
 171 {
 172 
 173         term_free((struct termp *)arg);
 174 }
 175 
 176 /* ARGSUSED */
 177 static void
 178 ascii_letter(struct termp *p, int c)
 179 {
 180         
 181         putchar(c);
 182 }
 183 
 184 static void
 185 ascii_begin(struct termp *p)
 186 {
 187 
 188         (*p->headf)(p, p->argf);
 189 }
 190 
 191 static void
 192 ascii_end(struct termp *p)
 193 {
 194 
 195         (*p->footf)(p, p->argf);
 196 }
 197 
 198 /* ARGSUSED */
 199 static void
 200 ascii_endline(struct termp *p)
 201 {
 202 
 203         putchar('\n');
 204 }
 205 
 206 /* ARGSUSED */
 207 static void
 208 ascii_advance(struct termp *p, size_t len)
 209 {
 210         size_t          i;
 211 
 212         for (i = 0; i < len; i++)
 213                 putchar(' ');
 214 }
 215 
 216 /* ARGSUSED */
 217 static double
 218 ascii_hspan(const struct termp *p, const struct roffsu *su)
 219 {
 220         double           r;
 221 
 222         /*
 223          * Approximate based on character width.  These are generated
 224          * entirely by eyeballing the screen, but appear to be correct.
 225          */
 226 
 227         switch (su->unit) {
 228         case (SCALE_CM):
 229                 r = 4 * su->scale;
 230                 break;
 231         case (SCALE_IN):
 232                 r = 10 * su->scale;
 233                 break;
 234         case (SCALE_PC):
 235                 r = (10 * su->scale) / 6;
 236                 break;
 237         case (SCALE_PT):
 238                 r = (10 * su->scale) / 72;
 239                 break;
 240         case (SCALE_MM):
 241                 r = su->scale / 1000;
 242                 break;
 243         case (SCALE_VS):
 244                 r = su->scale * 2 - 1;
 245                 break;
 246         default:
 247                 r = su->scale;
 248                 break;
 249         }
 250 
 251         return(r);
 252 }
 253 
 254 #ifdef USE_WCHAR
 255 /* ARGSUSED */
 256 static size_t
 257 locale_width(const struct termp *p, int c)
 258 {
 259         int             rc;
 260 
 261         return((rc = wcwidth(c)) < 0 ? 0 : rc);
 262 }
 263 
 264 /* ARGSUSED */
 265 static void
 266 locale_advance(struct termp *p, size_t len)
 267 {
 268         size_t          i;
 269 
 270         for (i = 0; i < len; i++)
 271                 putwchar(L' ');
 272 }
 273 
 274 /* ARGSUSED */
 275 static void
 276 locale_endline(struct termp *p)
 277 {
 278 
 279         putwchar(L'\n');
 280 }
 281 
 282 /* ARGSUSED */
 283 static void
 284 locale_letter(struct termp *p, int c)
 285 {
 286         
 287         putwchar(c);
 288 }
 289 #endif