root/usr/src/lib/libadm/common/puttext.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */


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

/*LINTLIBRARY*/

#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);
}