/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

/*
 * Copyright (c) 1997-1999 by Sun Microsystems, Inc.
 * All rights reserved.
 */

/*LINTLIBRARY*/
#pragma	ident	"@(#)puttext.c	1.10	99/11/18 SMI"	/* SVr4.0 1.2 */

#include <sys/types.h>
#include <stdio.h>
#include <ctype.h>
#include <wchar.h>
#include <libintl.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "libadm.h"

#define	MWIDTH	256
#define	WIDTH	60

int
puttext(FILE *fp, char *str, int lmarg, int rmarg)
{
	wchar_t	*wstr, *wp;
	wchar_t	*copy, *lastword, *lastend, temp[MWIDTH+1];
	size_t	len, ret;
	int	width, i, n, force, wordcnt;
	int	wlen, mlen, bdg;
	char	mbs[MB_LEN_MAX];
	char	mbtemp[(MWIDTH+1) * MB_LEN_MAX];

	width = rmarg ? (rmarg - lmarg) : (WIDTH - lmarg);
	if (width > MWIDTH)
		width = MWIDTH;

	if (!str || !*str)
		return (width);

	len = strlen(str);
	wstr = (wchar_t *)malloc(sizeof (wchar_t) * (len + 1));
	if (wstr == NULL)
		return (width);

	ret = mbstowcs(wstr, (const char *)str, len + 1);
	if (ret == (size_t)-1) {
		free(wstr);
		return (width);
	}

	wp = wstr;

	if (*wp == L'!') {
		wp++;
		force = 1;
		for (i = 0; i < lmarg; i++)
			(void) putc(' ', fp);
	} else {
		while (iswspace(*wp))
			++wp;	/* eat leading white space */
		force = 0;
	}

	wordcnt = 0;
	n = 0;
	copy = temp;
	lastword = wp;
	lastend = NULL;
	do {
		if (force) {
			if (*wp == L'\n') {
				(void) putc('\n', fp);
				for (i = 0; i < lmarg; i++)
					(void) putc(' ', fp);
				wp++;
				n = 0;
			} else {
				wlen = wcwidth(*wp);
		/*
		 * Using putc instead of fputwc here to avoid
		 * mixing up the byte stream and the wide stream
		 * for fp.
		 */
				mlen = wctomb(mbs, *wp);
				if (mlen == -1) {
		/*
		 * wctomb failed
		 * nothing will be outputted
		 */
					wp++;
				} else {
					for (i = 0; i < mlen; i++)
						(void) putc(mbs[i], fp);
					wp++;
		/*
		 * if wlen is a negative value (*wp is not printable),
		 * add 1 to n. (non-printable char shares 1 column.
		 */
					if (wlen >= 0)
						n += wlen;
					else
						n++;
				}
			}
			continue;
		}
		if (iswspace(*wp)) {
			/* eat multiple tabs/nl after whitespace */
			while ((*++wp == L'\t') || (*wp == '\n'));
			wordcnt++;
			lastword = wp;
			lastend = copy;
			*copy++ = L' ';
			n++;
		} else if (*wp == L'\\') {
			if (*(wp + 1) == L'n') {
				wordcnt++;
				n = width + 1;
				wp += 2;
				lastword = wp;
				lastend = copy;
			} else if (*(wp + 1) == L't') {
				wordcnt++;
				do {
					*copy++ = L' ';
				} while (++n % 8);
				n++;
				wp += 2;
				lastword = wp;
				lastend = copy;
			} else if (*(wp + 1) == L' ') {
				*copy++ = L' ';
				wp += 2;
				n++;
			} else {
				if (iswprint(*wp) && iswprint(*(wp + 1))) {
		/*
		 * Only if both *wp and *(wp +1) are printable,
		 * tries to check the binding weight between them.
		 */
					wlen = wcwidth(*wp);
					if (n + wlen > width) {
		/*
		 * if (n + wlen) is larger than width, *wp will be
		 * put to the next line.
		 */
						*copy++ = *wp++;
						n = width + 1;
						goto fold;
					} else {
						n += wlen;
						bdg = wdbindf(*wp,
							*(wp + 1), 1);
						*copy++ = *wp++;
						if (bdg < 5) {
		/*
		 * binding weight between *wp and *(wp + 1) is
		 * enough small to fold the line there.
		 */
							lastword = wp;
							lastend = copy;
							wordcnt++;
						}
					}
				} else {
					wlen = wcwidth(*wp);
					if (wlen > 0) {
		/*
		 * *wp is printable
		 */
						if (n + wlen > width) {
		/*
		 * if (n + wlen) is larger than width, *wp will
		 * be put to the next line.
		 */
							*copy++ = *wp++;
							n = width + 1;
							goto fold;
						} else {
							n += wlen;
						}
					} else {
		/*
		 * *wp is not printable, and shares 1 column.
		 */
						n++;
					}
					*copy++ = *wp++;
				}
			}
		} else {
			if (iswprint(*wp) && iswprint(*(wp + 1))) {
		/*
		 * Only if both *wp and *(wp + 1) are printable,
		 * tries to check the binding weight between them.
		 */
				wlen = wcwidth(*wp);
				if (n + wlen > width) {
		/*
		 * if (n + wlen) is larger than width, *wp will be
		 * put to the next line.
		 */
					*copy++ = *wp++;
					n = width + 1;
					goto fold;
				}
				n += wlen;
				bdg = wdbindf(*wp, *(wp + 1), 1);
				*copy++ = *wp++;
				if (bdg < 5) {
		/*
		 * binding weight between *wp and *(wp + 1) is
		 * enough small to fold the line there.
		 */
					lastword = wp;
					lastend = copy;
					wordcnt++;
				}
			} else {
				wlen = wcwidth(*wp);
				if (wlen > 0) {
		/*
		 * *wp is printable
		 */
					if (n + wlen > width) {
		/*
		 * if (n + wlen) is larger than width, *wp will
		 * be put to the next line.
		 */
						*copy++ = *wp++;
						n = width + 1;
						goto fold;
					} else {
						n += wlen;
					}
				} else {
		/*
		 * *wp is not printable, and shares 1 column.
		 */
					n++;
				}
				*copy++ = *wp++;
			}
		}

fold:
		if (n >= width) {
			if (lastend)
				*lastend = L'\0';
			else
				*copy = L'\0';
			for (i = 0; i < lmarg; i++)
				(void) putc(' ', fp);
			mlen = wcstombs(mbtemp, temp, MWIDTH+1);
			for (i = 0; i < mlen; i++)
				(void) putc(mbtemp[i], fp);
			(void) putc('\n', fp);

			lastend = NULL;
			copy = temp;
			if (wordcnt)
				wp = lastword;

			wordcnt = 0;
			n = 0;
			if (!force) {
				while (iswspace(*wp))
					wp++;
			}
		}
	} while (*wp != L'\0');
	if (!force) {
		*copy = L'\0';
		for (i = 0; i < lmarg; i++)
			(void) putc(' ', fp);
		mlen = wcstombs(mbtemp, temp, MWIDTH+1);
		for (i = 0; i < mlen; i++)
			(void) putc(mbtemp[i], fp);
	}
	free(wstr);
	return (width - n - !force);
}
