xref: /illumos-gate/usr/src/lib/libadm/common/puttext.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright (c) 1997-1999 by Sun Microsystems, Inc.
28  * All rights reserved.
29  */
30 
31 /*LINTLIBRARY*/
32 
33 #include <sys/types.h>
34 #include <stdio.h>
35 #include <ctype.h>
36 #include <wchar.h>
37 #include <libintl.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <limits.h>
41 #include "libadm.h"
42 
43 #define	MWIDTH	256
44 #define	WIDTH	60
45 
46 int
47 puttext(FILE *fp, char *str, int lmarg, int rmarg)
48 {
49 	wchar_t	*wstr, *wp;
50 	wchar_t	*copy, *lastword, *lastend, temp[MWIDTH+1];
51 	size_t	len, ret;
52 	int	width, i, n, force, wordcnt;
53 	int	wlen, mlen, bdg;
54 	char	mbs[MB_LEN_MAX];
55 	char	mbtemp[(MWIDTH+1) * MB_LEN_MAX];
56 
57 	width = rmarg ? (rmarg - lmarg) : (WIDTH - lmarg);
58 	if (width > MWIDTH)
59 		width = MWIDTH;
60 
61 	if (!str || !*str)
62 		return (width);
63 
64 	len = strlen(str);
65 	wstr = (wchar_t *)malloc(sizeof (wchar_t) * (len + 1));
66 	if (wstr == NULL)
67 		return (width);
68 
69 	ret = mbstowcs(wstr, (const char *)str, len + 1);
70 	if (ret == (size_t)-1) {
71 		free(wstr);
72 		return (width);
73 	}
74 
75 	wp = wstr;
76 
77 	if (*wp == L'!') {
78 		wp++;
79 		force = 1;
80 		for (i = 0; i < lmarg; i++)
81 			(void) putc(' ', fp);
82 	} else {
83 		while (iswspace(*wp))
84 			++wp;	/* eat leading white space */
85 		force = 0;
86 	}
87 
88 	wordcnt = 0;
89 	n = 0;
90 	copy = temp;
91 	lastword = wp;
92 	lastend = NULL;
93 	do {
94 		if (force) {
95 			if (*wp == L'\n') {
96 				(void) putc('\n', fp);
97 				for (i = 0; i < lmarg; i++)
98 					(void) putc(' ', fp);
99 				wp++;
100 				n = 0;
101 			} else {
102 				wlen = wcwidth(*wp);
103 		/*
104 		 * Using putc instead of fputwc here to avoid
105 		 * mixing up the byte stream and the wide stream
106 		 * for fp.
107 		 */
108 				mlen = wctomb(mbs, *wp);
109 				if (mlen == -1) {
110 		/*
111 		 * wctomb failed
112 		 * nothing will be outputted
113 		 */
114 					wp++;
115 				} else {
116 					for (i = 0; i < mlen; i++)
117 						(void) putc(mbs[i], fp);
118 					wp++;
119 		/*
120 		 * if wlen is a negative value (*wp is not printable),
121 		 * add 1 to n. (non-printable char shares 1 column.
122 		 */
123 					if (wlen >= 0)
124 						n += wlen;
125 					else
126 						n++;
127 				}
128 			}
129 			continue;
130 		}
131 		if (iswspace(*wp)) {
132 			/* eat multiple tabs/nl after whitespace */
133 			while ((*++wp == L'\t') || (*wp == '\n'));
134 			wordcnt++;
135 			lastword = wp;
136 			lastend = copy;
137 			*copy++ = L' ';
138 			n++;
139 		} else if (*wp == L'\\') {
140 			if (*(wp + 1) == L'n') {
141 				wordcnt++;
142 				n = width + 1;
143 				wp += 2;
144 				lastword = wp;
145 				lastend = copy;
146 			} else if (*(wp + 1) == L't') {
147 				wordcnt++;
148 				do {
149 					*copy++ = L' ';
150 				} while (++n % 8);
151 				n++;
152 				wp += 2;
153 				lastword = wp;
154 				lastend = copy;
155 			} else if (*(wp + 1) == L' ') {
156 				*copy++ = L' ';
157 				wp += 2;
158 				n++;
159 			} else {
160 				if (iswprint(*wp) && iswprint(*(wp + 1))) {
161 		/*
162 		 * Only if both *wp and *(wp +1) are printable,
163 		 * tries to check the binding weight between them.
164 		 */
165 					wlen = wcwidth(*wp);
166 					if (n + wlen > width) {
167 		/*
168 		 * if (n + wlen) is larger than width, *wp will be
169 		 * put to the next line.
170 		 */
171 						*copy++ = *wp++;
172 						n = width + 1;
173 						goto fold;
174 					} else {
175 						n += wlen;
176 						bdg = wdbindf(*wp,
177 							*(wp + 1), 1);
178 						*copy++ = *wp++;
179 						if (bdg < 5) {
180 		/*
181 		 * binding weight between *wp and *(wp + 1) is
182 		 * enough small to fold the line there.
183 		 */
184 							lastword = wp;
185 							lastend = copy;
186 							wordcnt++;
187 						}
188 					}
189 				} else {
190 					wlen = wcwidth(*wp);
191 					if (wlen > 0) {
192 		/*
193 		 * *wp is printable
194 		 */
195 						if (n + wlen > width) {
196 		/*
197 		 * if (n + wlen) is larger than width, *wp will
198 		 * be put to the next line.
199 		 */
200 							*copy++ = *wp++;
201 							n = width + 1;
202 							goto fold;
203 						} else {
204 							n += wlen;
205 						}
206 					} else {
207 		/*
208 		 * *wp is not printable, and shares 1 column.
209 		 */
210 						n++;
211 					}
212 					*copy++ = *wp++;
213 				}
214 			}
215 		} else {
216 			if (iswprint(*wp) && iswprint(*(wp + 1))) {
217 		/*
218 		 * Only if both *wp and *(wp + 1) are printable,
219 		 * tries to check the binding weight between them.
220 		 */
221 				wlen = wcwidth(*wp);
222 				if (n + wlen > width) {
223 		/*
224 		 * if (n + wlen) is larger than width, *wp will be
225 		 * put to the next line.
226 		 */
227 					*copy++ = *wp++;
228 					n = width + 1;
229 					goto fold;
230 				}
231 				n += wlen;
232 				bdg = wdbindf(*wp, *(wp + 1), 1);
233 				*copy++ = *wp++;
234 				if (bdg < 5) {
235 		/*
236 		 * binding weight between *wp and *(wp + 1) is
237 		 * enough small to fold the line there.
238 		 */
239 					lastword = wp;
240 					lastend = copy;
241 					wordcnt++;
242 				}
243 			} else {
244 				wlen = wcwidth(*wp);
245 				if (wlen > 0) {
246 		/*
247 		 * *wp is printable
248 		 */
249 					if (n + wlen > width) {
250 		/*
251 		 * if (n + wlen) is larger than width, *wp will
252 		 * be put to the next line.
253 		 */
254 						*copy++ = *wp++;
255 						n = width + 1;
256 						goto fold;
257 					} else {
258 						n += wlen;
259 					}
260 				} else {
261 		/*
262 		 * *wp is not printable, and shares 1 column.
263 		 */
264 					n++;
265 				}
266 				*copy++ = *wp++;
267 			}
268 		}
269 
270 fold:
271 		if (n >= width) {
272 			if (lastend)
273 				*lastend = L'\0';
274 			else
275 				*copy = L'\0';
276 			for (i = 0; i < lmarg; i++)
277 				(void) putc(' ', fp);
278 			mlen = wcstombs(mbtemp, temp, MWIDTH+1);
279 			for (i = 0; i < mlen; i++)
280 				(void) putc(mbtemp[i], fp);
281 			(void) putc('\n', fp);
282 
283 			lastend = NULL;
284 			copy = temp;
285 			if (wordcnt)
286 				wp = lastword;
287 
288 			wordcnt = 0;
289 			n = 0;
290 			if (!force) {
291 				while (iswspace(*wp))
292 					wp++;
293 			}
294 		}
295 	} while (*wp != L'\0');
296 	if (!force) {
297 		*copy = L'\0';
298 		for (i = 0; i < lmarg; i++)
299 			(void) putc(' ', fp);
300 		mlen = wcstombs(mbtemp, temp, MWIDTH+1);
301 		for (i = 0; i < mlen; i++)
302 			(void) putc(mbtemp[i], fp);
303 	}
304 	free(wstr);
305 	return (width - n - !force);
306 }
307