/* * 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; }