# # 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) 1989 by Sun Microsystems, Inc. # # Copyright 2019 Joyent, Inc. # PROG= ul include ../Makefile.cmd LDLIBS += -lcurses # false positive SMOFF += unreachable .KEEP_STATE: all: $(PROG) install: all $(ROOTPROG) clean: include ../Makefile.targ Copyright (c) 1980 Regents of the University of California. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the University of California, Berkeley and its contributors. 4. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PORTIONS OF UL COMMAND FUNCTIONALITY /* * Copyright 2000 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ /* * Copyright (c) 2018, Joyent, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #define IESC L'\033' #define SO L'\016' #define SI L'\017' #define HFWD L'9' #define HREV L'8' #define FREV L'7' #define CDUMMY -1 #define NORMAL 000 #define ALTSET 001 /* Reverse */ #define SUPERSC 002 /* Dim */ #define SUBSC 004 /* Dim | Ul */ #define UNDERL 010 /* Ul */ #define BOLD 020 /* Bold */ #define MEMFCT 16 /* * MEMFCT is a number that is likely to be large enough as a factor for * allocating more memory and to be small enough so as not wasting memory */ int must_use_uc, must_overstrike; char *CURS_UP, *CURS_RIGHT, *CURS_LEFT, *ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE, *ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES; struct CHAR { char c_mode; wchar_t c_char; }; struct CHAR obuf[LINE_MAX]; int col, maxcol; int mode; int halfpos; int upln; int iflag; eucwidth_t wp; int scrw[4]; void setmode(int newmode); void outc(wchar_t c); int outchar(char c); void initcap(void); void reverse(void); void fwd(void); void initbuf(void); void iattr(void); void overstrike(void); void flushln(void); void ul_filter(FILE *f); void ul_puts(char *str); int main(int argc, char **argv) { int c; char *termtype; FILE *f; char termcap[1024]; extern int optind; extern char *optarg; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); getwidth(&wp); scrw[0] = 1; scrw[1] = wp._scrw1; scrw[2] = wp._scrw2; scrw[3] = wp._scrw3; termtype = getenv("TERM"); if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1))) termtype = "lpr"; while ((c = getopt(argc, argv, "it:T:")) != EOF) switch (c) { case 't': case 'T': /* for nroff compatibility */ termtype = optarg; break; case 'i': iflag = 1; break; default: (void) fprintf(stderr, gettext("\ Usage: %s [ -i ] [ -t terminal ] [ filename...]\n"), argv[0]); exit(1); } switch (tgetent(termcap, termtype)) { case 1: break; default: (void) fprintf(stderr, gettext("trouble reading termcap")); /*FALLTHROUGH*/ case 0: /* No such terminal type - assume dumb */ (void) strcpy(termcap, "dumb:os:col#80:cr=^M:sf=^J:am:"); break; } initcap(); if ((tgetflag("os") && ENTER_BOLD == NULL) || (tgetflag("ul") && ENTER_UNDERLINE == NULL && UNDER_CHAR == NULL)) must_overstrike = 1; initbuf(); if (optind == argc) ul_filter(stdin); else for (; optind < argc; optind++) { f = fopen(argv[optind], "r"); if (f == NULL) { perror(argv[optind]); exit(1); } else ul_filter(f); } return (0); } void ul_filter(FILE *f) { wchar_t c; int i; while ((c = getwc(f)) != EOF) { if (maxcol >= LINE_MAX) { (void) fprintf(stderr, gettext("Input line longer than %d characters\n"), LINE_MAX); exit(1); } switch (c) { case L'\b': if (col > 0) col--; continue; case L'\t': col = (col+8) & ~07; if (col > maxcol) maxcol = col; continue; case L'\r': col = 0; continue; case SO: mode |= ALTSET; continue; case SI: mode &= ~ALTSET; continue; case IESC: switch (c = getwc(f)) { case HREV: if (halfpos == 0) { mode |= SUPERSC; halfpos--; } else if (halfpos > 0) { mode &= ~SUBSC; halfpos--; } else { halfpos = 0; reverse(); } continue; case HFWD: if (halfpos == 0) { mode |= SUBSC; halfpos++; } else if (halfpos < 0) { mode &= ~SUPERSC; halfpos++; } else { halfpos = 0; fwd(); } continue; case FREV: reverse(); continue; default: (void) fprintf(stderr, gettext("Unknown escape sequence in input: %o, %o\n"), IESC, c); exit(1); } continue; case L'_': if (obuf[col].c_char) obuf[col].c_mode |= UNDERL | mode; else obuf[col].c_char = '_'; /*FALLTHROUGH*/ case L' ': col++; if (col > maxcol) maxcol = col; continue; case L'\n': flushln(); continue; default: if (c < L' ') /* non printing */ continue; if (obuf[col].c_char == L'\0') { obuf[col].c_char = c; obuf[col].c_mode = mode; i = scrw[wcsetno(c)]; while (--i > 0) obuf[++col].c_char = CDUMMY; } else if (obuf[col].c_char == L'_') { obuf[col].c_char = c; obuf[col].c_mode |= UNDERL|mode; i = scrw[wcsetno(c)]; while (--i > 0) obuf[++col].c_char = CDUMMY; } else if (obuf[col].c_char == c) obuf[col].c_mode |= BOLD|mode; else { obuf[col].c_char = c; obuf[col].c_mode = mode; } col++; if (col > maxcol) maxcol = col; continue; } } if (maxcol) flushln(); } void flushln(void) { int lastmode; int i; int hadmodes = 0; lastmode = NORMAL; for (i = 0; i < maxcol; i++) { if (obuf[i].c_mode != lastmode) { hadmodes++; setmode(obuf[i].c_mode); lastmode = obuf[i].c_mode; } if (obuf[i].c_char == L'\0') { if (upln) { ul_puts(CURS_RIGHT); } else outc(L' '); } else outc(obuf[i].c_char); } if (lastmode != NORMAL) { setmode(0); } if (must_overstrike && hadmodes) overstrike(); (void) putwchar(L'\n'); if (iflag && hadmodes) iattr(); if (upln) upln--; initbuf(); } /* * For terminals that can overstrike, overstrike underlines and bolds. * We don't do anything with halfline ups and downs, or Greek. */ void overstrike(void) { int i, n; wchar_t *cp, *scp; size_t szbf = 256, tszbf; int hadbold = 0; scp = (wchar_t *)malloc(sizeof (wchar_t) * szbf); if (!scp) { /* this kind of message need not to be gettext'ed */ (void) fprintf(stderr, "malloc failed\n"); exit(1); } cp = scp; tszbf = szbf; #ifdef DEBUG /* * to allocate a memory after the chunk of the current scp * and to make sure the following realloc() allocates * memory from different chunks. */ (void) malloc(1024 * 1024); #endif /* Set up overstrike buffer */ for (i = 0; i < maxcol; i++) { n = scrw[wcsetno(obuf[i].c_char)]; if (tszbf <= n) { /* may not enough buffer for this char */ size_t pos; /* obtain the offset of cp */ pos = cp - scp; /* reallocate another (n * MEMFCT) * sizeof (wchar_t) */ scp = (wchar_t *)realloc(scp, sizeof (wchar_t) * (szbf + (n * MEMFCT))); if (!scp) { (void) fprintf(stderr, "malloc failed\n"); exit(1); } /* get the new address of cp */ cp = scp + pos; szbf += n * MEMFCT; tszbf += n * MEMFCT; } switch (obuf[i].c_mode) { case NORMAL: default: tszbf -= n; *cp++ = L' '; while (--n > 0) { *cp++ = L' '; i++; } break; case UNDERL: tszbf -= n; *cp++ = L'_'; while (--n > 0) { *cp++ = L'_'; i++; } break; case BOLD: tszbf--; *cp++ = obuf[i].c_char; hadbold = 1; break; } } (void) putwchar(L'\r'); for (*cp = L' '; *cp == L' '; cp--) *cp = L'\0'; for (cp = scp; *cp; cp++) (void) putwchar(*cp); if (hadbold) { (void) putwchar(L'\r'); for (cp = scp; *cp; cp++) (void) putwchar(*cp == L'_' ? L' ' : *cp); (void) putwchar(L'\r'); for (cp = scp; *cp; cp++) (void) putwchar(*cp == L'_' ? L' ' : *cp); } free(scp); } void iattr(void) { int i, n; wchar_t *cp, *scp; wchar_t cx; size_t szbf = 256, tszbf; scp = (wchar_t *)malloc(sizeof (wchar_t) * szbf); if (!scp) { /* this kind of message need not to be gettext'ed */ (void) fprintf(stderr, "malloc failed\n"); exit(1); } cp = scp; tszbf = szbf; #ifdef DEBUG /* * to allocate a memory after the chunk of the current scp * and to make sure the following realloc() allocates * memory from different chunks. */ (void) malloc(1024 * 1024); #endif for (i = 0; i < maxcol; i++) { switch (obuf[i].c_mode) { case NORMAL: cx = ' '; break; case ALTSET: cx = 'g'; break; case SUPERSC: cx = '^'; break; case SUBSC: cx = 'v'; break; case UNDERL: cx = '_'; break; case BOLD: cx = '!'; break; default: cx = 'X'; break; } n = scrw[wcsetno(obuf[i].c_char)]; if (tszbf <= n) { /* may not enough buffer for this char */ size_t pos; /* obtain the offset of cp */ pos = cp - scp; /* reallocate another (n * MEMFCT) * sizeof (wchar_t) */ scp = (wchar_t *)realloc(scp, sizeof (wchar_t) * (szbf + (n * MEMFCT))); if (!scp) { (void) fprintf(stderr, "malloc failed\n"); exit(1); } /* get the new address of cp */ cp = scp + pos; szbf += n * MEMFCT; tszbf += n * MEMFCT; } tszbf -= n; *cp++ = cx; while (--n > 0) { *cp++ = cx; i++; } } for (*cp = L' '; *cp == L' '; cp--) *cp = L'\0'; for (cp = scp; *cp; cp++) (void) putwchar(*cp); (void) putwchar(L'\n'); free(scp); } void initbuf(void) { int i; /* following depends on NORMAL == 000 */ for (i = 0; i < LINE_MAX; i++) obuf[i].c_char = obuf[i].c_mode = 0; col = 0; maxcol = 0; mode &= ALTSET; } void fwd(void) { int oldcol, oldmax; oldcol = col; oldmax = maxcol; flushln(); col = oldcol; maxcol = oldmax; } void reverse(void) { upln++; fwd(); ul_puts(CURS_UP); ul_puts(CURS_UP); upln++; } void initcap(void) { static char tcapbuf[512]; char *bp = tcapbuf; /* This nonsense attempts to work with both old and new termcap */ CURS_UP = tgetstr("up", &bp); CURS_RIGHT = tgetstr("ri", &bp); if (CURS_RIGHT == NULL) CURS_RIGHT = tgetstr("nd", &bp); CURS_LEFT = tgetstr("le", &bp); if (CURS_LEFT == NULL) CURS_LEFT = tgetstr("bc", &bp); if (CURS_LEFT == NULL && tgetflag("bs")) CURS_LEFT = "\b"; ENTER_STANDOUT = tgetstr("so", &bp); EXIT_STANDOUT = tgetstr("se", &bp); ENTER_UNDERLINE = tgetstr("us", &bp); EXIT_UNDERLINE = tgetstr("ue", &bp); ENTER_DIM = tgetstr("mh", &bp); ENTER_BOLD = tgetstr("md", &bp); ENTER_REVERSE = tgetstr("mr", &bp); EXIT_ATTRIBUTES = tgetstr("me", &bp); if (!ENTER_BOLD && ENTER_REVERSE) ENTER_BOLD = ENTER_REVERSE; if (!ENTER_BOLD && ENTER_STANDOUT) ENTER_BOLD = ENTER_STANDOUT; if (!ENTER_UNDERLINE && ENTER_STANDOUT) { ENTER_UNDERLINE = ENTER_STANDOUT; EXIT_UNDERLINE = EXIT_STANDOUT; } if (!ENTER_DIM && ENTER_STANDOUT) ENTER_DIM = ENTER_STANDOUT; if (!ENTER_REVERSE && ENTER_STANDOUT) ENTER_REVERSE = ENTER_STANDOUT; if (!EXIT_ATTRIBUTES && EXIT_STANDOUT) EXIT_ATTRIBUTES = EXIT_STANDOUT; /* * Note that we use REVERSE for the alternate character set, * not the as/ae capabilities. This is because we are modelling * the model 37 teletype (since that's what nroff outputs) and * the typical as/ae is more of a graphics set, not the greek * letters the 37 has. */ #ifdef notdef printf("so %s se %s us %s ue %s me %s\n", ENTER_STANDOUT, EXIT_STANDOUT, ENTER_UNDERLINE, EXIT_UNDERLINE, EXIT_ATTRIBUTES); #endif UNDER_CHAR = tgetstr("uc", &bp); must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE); } int outchar(char c) { (void) putchar(c&0177); return (0); } void ul_puts(char *str) { if (str) (void) tputs(str, 1, outchar); } static int curmode = 0; void outc(wchar_t c) { int m, n; if (c == CDUMMY) return; (void) putwchar(c); if (must_use_uc && (curmode & UNDERL)) { m = n = scrw[wcsetno(c)]; ul_puts(CURS_LEFT); while (--m > 0) ul_puts(CURS_LEFT); ul_puts(UNDER_CHAR); while (--n > 0) ul_puts(UNDER_CHAR); } } void setmode(int newmode) { if (!iflag) { if (curmode != NORMAL && newmode != NORMAL) setmode(NORMAL); switch (newmode) { case NORMAL: switch (curmode) { case NORMAL: break; case UNDERL: ul_puts(EXIT_UNDERLINE); break; default: /* This includes standout */ ul_puts(EXIT_ATTRIBUTES); break; } break; case ALTSET: ul_puts(ENTER_REVERSE); break; case SUPERSC: /* * This only works on a few terminals. * It should be fixed. */ ul_puts(ENTER_UNDERLINE); ul_puts(ENTER_DIM); break; case SUBSC: ul_puts(ENTER_DIM); break; case UNDERL: ul_puts(ENTER_UNDERLINE); break; case BOLD: ul_puts(ENTER_BOLD); break; default: /* * We should have some provision here for multiple modes * on at once. This will have to come later. */ ul_puts(ENTER_STANDOUT); break; } } curmode = newmode; }