xref: /illumos-gate/usr/src/lib/libadm/common/puttext.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 #pragma	ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.2 */
33 
34 #include <sys/types.h>
35 #include <stdio.h>
36 #include <ctype.h>
37 #include <wchar.h>
38 #include <libintl.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <limits.h>
42 #include "libadm.h"
43 
44 #define	MWIDTH	256
45 #define	WIDTH	60
46 
47 int
48 puttext(FILE *fp, char *str, int lmarg, int rmarg)
49 {
50 	wchar_t	*wstr, *wp;
51 	wchar_t	*copy, *lastword, *lastend, temp[MWIDTH+1];
52 	size_t	len, ret;
53 	int	width, i, n, force, wordcnt;
54 	int	wlen, mlen, bdg;
55 	char	mbs[MB_LEN_MAX];
56 	char	mbtemp[(MWIDTH+1) * MB_LEN_MAX];
57 
58 	width = rmarg ? (rmarg - lmarg) : (WIDTH - lmarg);
59 	if (width > MWIDTH)
60 		width = MWIDTH;
61 
62 	if (!str || !*str)
63 		return (width);
64 
65 	len = strlen(str);
66 	wstr = (wchar_t *)malloc(sizeof (wchar_t) * (len + 1));
67 	if (wstr == NULL)
68 		return (width);
69 
70 	ret = mbstowcs(wstr, (const char *)str, len + 1);
71 	if (ret == (size_t)-1) {
72 		free(wstr);
73 		return (width);
74 	}
75 
76 	wp = wstr;
77 
78 	if (*wp == L'!') {
79 		wp++;
80 		force = 1;
81 		for (i = 0; i < lmarg; i++)
82 			(void) putc(' ', fp);
83 	} else {
84 		while (iswspace(*wp))
85 			++wp;	/* eat leading white space */
86 		force = 0;
87 	}
88 
89 	wordcnt = 0;
90 	n = 0;
91 	copy = temp;
92 	lastword = wp;
93 	lastend = NULL;
94 	do {
95 		if (force) {
96 			if (*wp == L'\n') {
97 				(void) putc('\n', fp);
98 				for (i = 0; i < lmarg; i++)
99 					(void) putc(' ', fp);
100 				wp++;
101 				n = 0;
102 			} else {
103 				wlen = wcwidth(*wp);
104 		/*
105 		 * Using putc instead of fputwc here to avoid
106 		 * mixing up the byte stream and the wide stream
107 		 * for fp.
108 		 */
109 				mlen = wctomb(mbs, *wp);
110 				if (mlen == -1) {
111 		/*
112 		 * wctomb failed
113 		 * nothing will be outputted
114 		 */
115 					wp++;
116 				} else {
117 					for (i = 0; i < mlen; i++)
118 						(void) putc(mbs[i], fp);
119 					wp++;
120 		/*
121 		 * if wlen is a negative value (*wp is not printable),
122 		 * add 1 to n. (non-printable char shares 1 column.
123 		 */
124 					if (wlen >= 0)
125 						n += wlen;
126 					else
127 						n++;
128 				}
129 			}
130 			continue;
131 		}
132 		if (iswspace(*wp)) {
133 			/* eat multiple tabs/nl after whitespace */
134 			while ((*++wp == L'\t') || (*wp == '\n'));
135 			wordcnt++;
136 			lastword = wp;
137 			lastend = copy;
138 			*copy++ = L' ';
139 			n++;
140 		} else if (*wp == L'\\') {
141 			if (*(wp + 1) == L'n') {
142 				wordcnt++;
143 				n = width + 1;
144 				wp += 2;
145 				lastword = wp;
146 				lastend = copy;
147 			} else if (*(wp + 1) == L't') {
148 				wordcnt++;
149 				do {
150 					*copy++ = L' ';
151 				} while (++n % 8);
152 				n++;
153 				wp += 2;
154 				lastword = wp;
155 				lastend = copy;
156 			} else if (*(wp + 1) == L' ') {
157 				*copy++ = L' ';
158 				wp += 2;
159 				n++;
160 			} else {
161 				if (iswprint(*wp) && iswprint(*(wp + 1))) {
162 		/*
163 		 * Only if both *wp and *(wp +1) are printable,
164 		 * tries to check the binding weight between them.
165 		 */
166 					wlen = wcwidth(*wp);
167 					if (n + wlen > width) {
168 		/*
169 		 * if (n + wlen) is larger than width, *wp will be
170 		 * put to the next line.
171 		 */
172 						*copy++ = *wp++;
173 						n = width + 1;
174 						goto fold;
175 					} else {
176 						n += wlen;
177 						bdg = wdbindf(*wp,
178 							*(wp + 1), 1);
179 						*copy++ = *wp++;
180 						if (bdg < 5) {
181 		/*
182 		 * binding weight between *wp and *(wp + 1) is
183 		 * enough small to fold the line there.
184 		 */
185 							lastword = wp;
186 							lastend = copy;
187 							wordcnt++;
188 						}
189 					}
190 				} else {
191 					wlen = wcwidth(*wp);
192 					if (wlen > 0) {
193 		/*
194 		 * *wp is printable
195 		 */
196 						if (n + wlen > width) {
197 		/*
198 		 * if (n + wlen) is larger than width, *wp will
199 		 * be put to the next line.
200 		 */
201 							*copy++ = *wp++;
202 							n = width + 1;
203 							goto fold;
204 						} else {
205 							n += wlen;
206 						}
207 					} else {
208 		/*
209 		 * *wp is not printable, and shares 1 column.
210 		 */
211 						n++;
212 					}
213 					*copy++ = *wp++;
214 				}
215 			}
216 		} else {
217 			if (iswprint(*wp) && iswprint(*(wp + 1))) {
218 		/*
219 		 * Only if both *wp and *(wp + 1) are printable,
220 		 * tries to check the binding weight between them.
221 		 */
222 				wlen = wcwidth(*wp);
223 				if (n + wlen > width) {
224 		/*
225 		 * if (n + wlen) is larger than width, *wp will be
226 		 * put to the next line.
227 		 */
228 					*copy++ = *wp++;
229 					n = width + 1;
230 					goto fold;
231 				}
232 				n += wlen;
233 				bdg = wdbindf(*wp, *(wp + 1), 1);
234 				*copy++ = *wp++;
235 				if (bdg < 5) {
236 		/*
237 		 * binding weight between *wp and *(wp + 1) is
238 		 * enough small to fold the line there.
239 		 */
240 					lastword = wp;
241 					lastend = copy;
242 					wordcnt++;
243 				}
244 			} else {
245 				wlen = wcwidth(*wp);
246 				if (wlen > 0) {
247 		/*
248 		 * *wp is printable
249 		 */
250 					if (n + wlen > width) {
251 		/*
252 		 * if (n + wlen) is larger than width, *wp will
253 		 * be put to the next line.
254 		 */
255 						*copy++ = *wp++;
256 						n = width + 1;
257 						goto fold;
258 					} else {
259 						n += wlen;
260 					}
261 				} else {
262 		/*
263 		 * *wp is not printable, and shares 1 column.
264 		 */
265 					n++;
266 				}
267 				*copy++ = *wp++;
268 			}
269 		}
270 
271 fold:
272 		if (n >= width) {
273 			if (lastend)
274 				*lastend = L'\0';
275 			else
276 				*copy = L'\0';
277 			for (i = 0; i < lmarg; i++)
278 				(void) putc(' ', fp);
279 			mlen = wcstombs(mbtemp, temp, MWIDTH+1);
280 			for (i = 0; i < mlen; i++)
281 				(void) putc(mbtemp[i], fp);
282 			(void) putc('\n', fp);
283 
284 			lastend = NULL;
285 			copy = temp;
286 			if (wordcnt)
287 				wp = lastword;
288 
289 			wordcnt = 0;
290 			n = 0;
291 			if (!force) {
292 				while (iswspace(*wp))
293 					wp++;
294 			}
295 		}
296 	} while (*wp != L'\0');
297 	if (!force) {
298 		*copy = L'\0';
299 		for (i = 0; i < lmarg; i++)
300 			(void) putc(' ', fp);
301 		mlen = wcstombs(mbtemp, temp, MWIDTH+1);
302 		for (i = 0; i < mlen; i++)
303 			(void) putc(mbtemp[i], fp);
304 	}
305 	free(wstr);
306 	return (width - n - !force);
307 }
308