xref: /dragonfly/contrib/mdocml/term_ascii.c (revision c93b565c)
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