1 /* $Id: term_ascii.c,v 1.27 2014/08/01 19:25:52 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2014 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 AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 #ifdef HAVE_CONFIG_H 19 #include "config.h" 20 #endif 21 22 #include <sys/types.h> 23 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 "mandoc_aux.h" 37 #include "out.h" 38 #include "term.h" 39 #include "main.h" 40 41 /* 42 * Sadly, this doesn't seem to be defined on systems even when they 43 * support it. For the time being, remove it and let those compiling 44 * the software decide for themselves what to use. 45 */ 46 #if 0 47 #if ! defined(__STDC_ISO_10646__) 48 # undef USE_WCHAR 49 #endif 50 #endif 51 52 static struct termp *ascii_init(enum termenc, char *); 53 static double ascii_hspan(const struct termp *, 54 const struct roffsu *); 55 static size_t ascii_width(const struct termp *, int); 56 static void ascii_advance(struct termp *, size_t); 57 static void ascii_begin(struct termp *); 58 static void ascii_end(struct termp *); 59 static void ascii_endline(struct termp *); 60 static void ascii_letter(struct termp *, int); 61 static void ascii_setwidth(struct termp *, int, size_t); 62 63 #ifdef USE_WCHAR 64 static void locale_advance(struct termp *, size_t); 65 static void locale_endline(struct termp *); 66 static void locale_letter(struct termp *, int); 67 static size_t locale_width(const struct termp *, int); 68 #endif 69 70 71 static struct termp * 72 ascii_init(enum termenc enc, char *outopts) 73 { 74 const char *toks[4]; 75 char *v; 76 struct termp *p; 77 78 p = mandoc_calloc(1, sizeof(struct termp)); 79 80 p->tabwidth = 5; 81 p->defrmargin = p->lastrmargin = 78; 82 83 p->begin = ascii_begin; 84 p->end = ascii_end; 85 p->hspan = ascii_hspan; 86 p->type = TERMTYPE_CHAR; 87 88 p->enc = TERMENC_ASCII; 89 p->advance = ascii_advance; 90 p->endline = ascii_endline; 91 p->letter = ascii_letter; 92 p->setwidth = ascii_setwidth; 93 p->width = ascii_width; 94 95 #ifdef USE_WCHAR 96 if (TERMENC_ASCII != enc) { 97 v = TERMENC_LOCALE == enc ? 98 setlocale(LC_ALL, "") : 99 setlocale(LC_CTYPE, "en_US.UTF-8"); 100 if (NULL != v && MB_CUR_MAX > 1) { 101 p->enc = enc; 102 p->advance = locale_advance; 103 p->endline = locale_endline; 104 p->letter = locale_letter; 105 p->width = locale_width; 106 } 107 } 108 #endif 109 110 toks[0] = "indent"; 111 toks[1] = "width"; 112 toks[2] = "mdoc"; 113 toks[3] = NULL; 114 115 while (outopts && *outopts) 116 switch (getsubopt(&outopts, UNCONST(toks), &v)) { 117 case 0: 118 p->defindent = (size_t)atoi(v); 119 break; 120 case 1: 121 p->defrmargin = (size_t)atoi(v); 122 break; 123 case 2: 124 /* 125 * Temporary, undocumented mode 126 * to imitate mdoc(7) output style. 127 */ 128 p->mdocstyle = 1; 129 p->defindent = 5; 130 break; 131 default: 132 break; 133 } 134 135 /* Enforce a lower boundary. */ 136 if (p->defrmargin < 58) 137 p->defrmargin = 58; 138 139 return(p); 140 } 141 142 void * 143 ascii_alloc(char *outopts) 144 { 145 146 return(ascii_init(TERMENC_ASCII, outopts)); 147 } 148 149 void * 150 utf8_alloc(char *outopts) 151 { 152 153 return(ascii_init(TERMENC_UTF8, outopts)); 154 } 155 156 void * 157 locale_alloc(char *outopts) 158 { 159 160 return(ascii_init(TERMENC_LOCALE, outopts)); 161 } 162 163 static void 164 ascii_setwidth(struct termp *p, int iop, size_t width) 165 { 166 167 p->rmargin = p->defrmargin; 168 if (0 < iop) 169 p->defrmargin += width; 170 else if (0 > iop) 171 p->defrmargin -= width; 172 else 173 p->defrmargin = width ? width : p->lastrmargin; 174 p->lastrmargin = p->rmargin; 175 p->rmargin = p->maxrmargin = p->defrmargin; 176 } 177 178 static size_t 179 ascii_width(const struct termp *p, int c) 180 { 181 182 return(1); 183 } 184 185 void 186 ascii_free(void *arg) 187 { 188 189 term_free((struct termp *)arg); 190 } 191 192 static void 193 ascii_letter(struct termp *p, int c) 194 { 195 196 putchar(c); 197 } 198 199 static void 200 ascii_begin(struct termp *p) 201 { 202 203 (*p->headf)(p, p->argf); 204 } 205 206 static void 207 ascii_end(struct termp *p) 208 { 209 210 (*p->footf)(p, p->argf); 211 } 212 213 static void 214 ascii_endline(struct termp *p) 215 { 216 217 putchar('\n'); 218 } 219 220 static void 221 ascii_advance(struct termp *p, size_t len) 222 { 223 size_t i; 224 225 for (i = 0; i < len; i++) 226 putchar(' '); 227 } 228 229 static double 230 ascii_hspan(const struct termp *p, const struct roffsu *su) 231 { 232 double r; 233 234 /* 235 * Approximate based on character width. These are generated 236 * entirely by eyeballing the screen, but appear to be correct. 237 */ 238 239 switch (su->unit) { 240 case SCALE_CM: 241 r = su->scale * 4.0; 242 break; 243 case SCALE_IN: 244 r = su->scale * 10.0; 245 break; 246 case SCALE_PC: 247 r = (su->scale * 10.0) / 6.0; 248 break; 249 case SCALE_PT: 250 r = (su->scale * 10.0) / 72.0; 251 break; 252 case SCALE_MM: 253 r = su->scale / 1000.0; 254 break; 255 case SCALE_VS: 256 r = su->scale * 2.0 - 1.0; 257 break; 258 default: 259 r = su->scale; 260 break; 261 } 262 263 return(r); 264 } 265 266 #ifdef USE_WCHAR 267 static size_t 268 locale_width(const struct termp *p, int c) 269 { 270 int rc; 271 272 if (c == ASCII_NBRSP) 273 c = ' '; 274 rc = wcwidth(c); 275 if (rc < 0) 276 rc = 0; 277 return(rc); 278 } 279 280 static void 281 locale_advance(struct termp *p, size_t len) 282 { 283 size_t i; 284 285 for (i = 0; i < len; i++) 286 putwchar(L' '); 287 } 288 289 static void 290 locale_endline(struct termp *p) 291 { 292 293 putwchar(L'\n'); 294 } 295 296 static void 297 locale_letter(struct termp *p, int c) 298 { 299 300 putwchar(c); 301 } 302 #endif 303