19b50d902SRodney W. Grimes /*- 2*8a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 3*8a16b7a1SPedro F. Giffuni * 49b50d902SRodney W. Grimes * Copyright (c) 1990, 1993, 1994 59b50d902SRodney W. Grimes * The Regents of the University of California. All rights reserved. 69b50d902SRodney W. Grimes * 79b50d902SRodney W. Grimes * This code is derived from software contributed to Berkeley by 89b50d902SRodney W. Grimes * Michael Rendell of the Memorial University of Newfoundland. 99b50d902SRodney W. Grimes * 109b50d902SRodney W. Grimes * Redistribution and use in source and binary forms, with or without 119b50d902SRodney W. Grimes * modification, are permitted provided that the following conditions 129b50d902SRodney W. Grimes * are met: 139b50d902SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 149b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer. 159b50d902SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 169b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 179b50d902SRodney W. Grimes * documentation and/or other materials provided with the distribution. 18fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors 199b50d902SRodney W. Grimes * may be used to endorse or promote products derived from this software 209b50d902SRodney W. Grimes * without specific prior written permission. 219b50d902SRodney W. Grimes * 229b50d902SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 239b50d902SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 249b50d902SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 259b50d902SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 269b50d902SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 279b50d902SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 289b50d902SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 299b50d902SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 309b50d902SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 319b50d902SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 329b50d902SRodney W. Grimes * SUCH DAMAGE. 339b50d902SRodney W. Grimes */ 349b50d902SRodney W. Grimes 359b50d902SRodney W. Grimes #ifndef lint 36fa146c53SArchie Cobbs static const char copyright[] = 379b50d902SRodney W. Grimes "@(#) Copyright (c) 1990, 1993, 1994\n\ 389b50d902SRodney W. Grimes The Regents of the University of California. All rights reserved.\n"; 3984c0ff22SMark Murray #endif 409b50d902SRodney W. Grimes 419f5b04e9SDavid Malone #if 0 429b50d902SRodney W. Grimes #ifndef lint 439f5b04e9SDavid Malone static char sccsid[] = "@(#)col.c 8.5 (Berkeley) 5/4/95"; 44a292105aSPhilippe Charnier #endif 459f5b04e9SDavid Malone #endif 469f5b04e9SDavid Malone 479f5b04e9SDavid Malone #include <sys/cdefs.h> 489f5b04e9SDavid Malone __FBSDID("$FreeBSD$"); 499b50d902SRodney W. Grimes 50e5ea68e7SBaptiste Daroussin #include <sys/capsicum.h> 51e5ea68e7SBaptiste Daroussin 52a4e3fc54SMariusz Zaborski #include <capsicum_helpers.h> 539b50d902SRodney W. Grimes #include <err.h> 54e5ea68e7SBaptiste Daroussin #include <errno.h> 55c3fae744STim J. Robbins #include <locale.h> 569b50d902SRodney W. Grimes #include <stdio.h> 579b50d902SRodney W. Grimes #include <stdlib.h> 58919480b7STim J. Robbins #include <string.h> 59e5ea68e7SBaptiste Daroussin #include <termios.h> 60df3f5d9dSPeter Wemm #include <unistd.h> 61c3fae744STim J. Robbins #include <wchar.h> 62c3fae744STim J. Robbins #include <wctype.h> 639b50d902SRodney W. Grimes 649b50d902SRodney W. Grimes #define BS '\b' /* backspace */ 659b50d902SRodney W. Grimes #define TAB '\t' /* tab */ 669b50d902SRodney W. Grimes #define SPACE ' ' /* space */ 679b50d902SRodney W. Grimes #define NL '\n' /* newline */ 689b50d902SRodney W. Grimes #define CR '\r' /* carriage return */ 699b50d902SRodney W. Grimes #define ESC '\033' /* escape */ 709b50d902SRodney W. Grimes #define SI '\017' /* shift in to normal character set */ 719b50d902SRodney W. Grimes #define SO '\016' /* shift out to alternate character set */ 729b50d902SRodney W. Grimes #define VT '\013' /* vertical tab (aka reverse line feed) */ 73f6240da9SBaptiste Daroussin #define RLF '7' /* ESC-7 reverse line feed */ 74f6240da9SBaptiste Daroussin #define RHLF '8' /* ESC-8 reverse half-line feed */ 75f6240da9SBaptiste Daroussin #define FHLF '9' /* ESC-9 forward half-line feed */ 769b50d902SRodney W. Grimes 779b50d902SRodney W. Grimes /* build up at least this many lines before flushing them out */ 789b50d902SRodney W. Grimes #define BUFFER_MARGIN 32 799b50d902SRodney W. Grimes 809b50d902SRodney W. Grimes typedef char CSET; 819b50d902SRodney W. Grimes 829b50d902SRodney W. Grimes typedef struct char_str { 839b50d902SRodney W. Grimes #define CS_NORMAL 1 849b50d902SRodney W. Grimes #define CS_ALTERNATE 2 859b50d902SRodney W. Grimes short c_column; /* column character is in */ 869b50d902SRodney W. Grimes CSET c_set; /* character set (currently only 2) */ 87c3fae744STim J. Robbins wchar_t c_char; /* character in question */ 88c3fae744STim J. Robbins int c_width; /* character width */ 899b50d902SRodney W. Grimes } CHAR; 909b50d902SRodney W. Grimes 919b50d902SRodney W. Grimes typedef struct line_str LINE; 929b50d902SRodney W. Grimes struct line_str { 939b50d902SRodney W. Grimes CHAR *l_line; /* characters on the line */ 949b50d902SRodney W. Grimes LINE *l_prev; /* previous line */ 959b50d902SRodney W. Grimes LINE *l_next; /* next line */ 969b50d902SRodney W. Grimes int l_lsize; /* allocated sizeof l_line */ 979b50d902SRodney W. Grimes int l_line_len; /* strlen(l_line) */ 989b50d902SRodney W. Grimes int l_needs_sort; /* set if chars went in out of order */ 999b50d902SRodney W. Grimes int l_max_col; /* max column in the line */ 1009b50d902SRodney W. Grimes }; 1019b50d902SRodney W. Grimes 102f23ed79bSBaptiste Daroussin static void addto_lineno(int *, int); 103ab8d971cSEd Schouten static LINE *alloc_line(void); 104ab8d971cSEd Schouten static void dowarn(int); 105ab8d971cSEd Schouten static void flush_line(LINE *); 106ab8d971cSEd Schouten static void flush_lines(int); 107ab8d971cSEd Schouten static void flush_blanks(void); 108ab8d971cSEd Schouten static void free_line(LINE *); 109ab8d971cSEd Schouten static void usage(void); 1109b50d902SRodney W. Grimes 111ab8d971cSEd Schouten static CSET last_set; /* char_set of last char printed */ 112ab8d971cSEd Schouten static LINE *lines; 113ab8d971cSEd Schouten static int compress_spaces; /* if doing space -> tab conversion */ 114ab8d971cSEd Schouten static int fine; /* if `fine' resolution (half lines) */ 115f23ed79bSBaptiste Daroussin static int max_bufd_lines; /* max # of half lines to keep in memory */ 116ab8d971cSEd Schouten static int nblank_lines; /* # blanks after last flushed line */ 117ab8d971cSEd Schouten static int no_backspaces; /* if not to output any backspaces */ 118ab8d971cSEd Schouten static int pass_unknown_seqs; /* pass unknown control sequences */ 1199b50d902SRodney W. Grimes 1209b50d902SRodney W. Grimes #define PUTC(ch) \ 121f746e67cSBruce Evans do { \ 122c3fae744STim J. Robbins if (putwchar(ch) == WEOF) \ 123f34d0e74SMike Heffner errx(1, "write error"); \ 124f746e67cSBruce Evans } while (0) 1259b50d902SRodney W. Grimes 1269b50d902SRodney W. Grimes int 127f4ac32deSDavid Malone main(int argc, char **argv) 1289b50d902SRodney W. Grimes { 129c3fae744STim J. Robbins wint_t ch; 1309b50d902SRodney W. Grimes CHAR *c; 1319b50d902SRodney W. Grimes CSET cur_set; /* current character set */ 1329b50d902SRodney W. Grimes LINE *l; /* current line */ 1339b50d902SRodney W. Grimes int extra_lines; /* # of lines above first line */ 1349b50d902SRodney W. Grimes int cur_col; /* current column */ 1359b50d902SRodney W. Grimes int cur_line; /* line number of current position */ 1369b50d902SRodney W. Grimes int max_line; /* max value of cur_line */ 1379b50d902SRodney W. Grimes int this_line; /* line l points to */ 1389b50d902SRodney W. Grimes int nflushd_lines; /* number of lines that were flushed */ 139c3fae744STim J. Robbins int adjust, opt, warned, width; 140f23ed79bSBaptiste Daroussin const char *errstr; 1419b50d902SRodney W. Grimes 1428bbd9072SAndrey A. Chernov (void)setlocale(LC_CTYPE, ""); 1438bbd9072SAndrey A. Chernov 144a4e3fc54SMariusz Zaborski if (caph_limit_stdio() == -1) 145a4e3fc54SMariusz Zaborski err(1, "unable to limit stdio"); 146e5ea68e7SBaptiste Daroussin 147e5ea68e7SBaptiste Daroussin if (cap_enter() < 0 && errno != ENOSYS) 148e5ea68e7SBaptiste Daroussin err(1, "unable to enter capability mode"); 149e5ea68e7SBaptiste Daroussin 150f23ed79bSBaptiste Daroussin max_bufd_lines = 256; 1519b50d902SRodney W. Grimes compress_spaces = 1; /* compress spaces into tabs */ 152844518ffSMike Heffner while ((opt = getopt(argc, argv, "bfhl:px")) != -1) 1539b50d902SRodney W. Grimes switch (opt) { 1549b50d902SRodney W. Grimes case 'b': /* do not output backspaces */ 1559b50d902SRodney W. Grimes no_backspaces = 1; 1569b50d902SRodney W. Grimes break; 1579b50d902SRodney W. Grimes case 'f': /* allow half forward line feeds */ 1589b50d902SRodney W. Grimes fine = 1; 1599b50d902SRodney W. Grimes break; 1609b50d902SRodney W. Grimes case 'h': /* compress spaces into tabs */ 1619b50d902SRodney W. Grimes compress_spaces = 1; 1629b50d902SRodney W. Grimes break; 1639b50d902SRodney W. Grimes case 'l': /* buffered line count */ 164f23ed79bSBaptiste Daroussin max_bufd_lines = strtonum(optarg, 1, 165f23ed79bSBaptiste Daroussin (INT_MAX - BUFFER_MARGIN) / 2, &errstr) * 2; 166f23ed79bSBaptiste Daroussin if (errstr != NULL) 167f23ed79bSBaptiste Daroussin errx(1, "bad -l argument, %s: %s", errstr, 168f23ed79bSBaptiste Daroussin optarg); 1699b50d902SRodney W. Grimes break; 170844518ffSMike Heffner case 'p': /* pass unknown control sequences */ 171844518ffSMike Heffner pass_unknown_seqs = 1; 172844518ffSMike Heffner break; 1739b50d902SRodney W. Grimes case 'x': /* do not compress spaces into tabs */ 1749b50d902SRodney W. Grimes compress_spaces = 0; 1759b50d902SRodney W. Grimes break; 1769b50d902SRodney W. Grimes case '?': 1779b50d902SRodney W. Grimes default: 1789b50d902SRodney W. Grimes usage(); 1799b50d902SRodney W. Grimes } 1809b50d902SRodney W. Grimes 1819b50d902SRodney W. Grimes if (optind != argc) 1829b50d902SRodney W. Grimes usage(); 1839b50d902SRodney W. Grimes 1849b50d902SRodney W. Grimes adjust = cur_col = extra_lines = warned = 0; 1859b50d902SRodney W. Grimes cur_line = max_line = nflushd_lines = this_line = 0; 1869b50d902SRodney W. Grimes cur_set = last_set = CS_NORMAL; 1879b50d902SRodney W. Grimes lines = l = alloc_line(); 1889b50d902SRodney W. Grimes 189c3fae744STim J. Robbins while ((ch = getwchar()) != WEOF) { 190c3fae744STim J. Robbins if (!iswgraph(ch)) { 1919b50d902SRodney W. Grimes switch (ch) { 1929b50d902SRodney W. Grimes case BS: /* can't go back further */ 1939b50d902SRodney W. Grimes if (cur_col == 0) 1949b50d902SRodney W. Grimes continue; 1959b50d902SRodney W. Grimes --cur_col; 1969b50d902SRodney W. Grimes continue; 1979b50d902SRodney W. Grimes case CR: 1989b50d902SRodney W. Grimes cur_col = 0; 1999b50d902SRodney W. Grimes continue; 2009b50d902SRodney W. Grimes case ESC: /* just ignore EOF */ 201c3fae744STim J. Robbins switch(getwchar()) { 2027bc60a16SBaptiste Daroussin /* 2037bc60a16SBaptiste Daroussin * In the input stream, accept both the 2047bc60a16SBaptiste Daroussin * XPG5 sequences ESC-digit and the 2057bc60a16SBaptiste Daroussin * traditional BSD sequences ESC-ctrl. 2067bc60a16SBaptiste Daroussin */ 2077bc60a16SBaptiste Daroussin case '\007': 2087bc60a16SBaptiste Daroussin /* FALLTHROUGH */ 2099b50d902SRodney W. Grimes case RLF: 210f23ed79bSBaptiste Daroussin addto_lineno(&cur_line, -2); 2119b50d902SRodney W. Grimes break; 2127bc60a16SBaptiste Daroussin case '\010': 2137bc60a16SBaptiste Daroussin /* FALLTHROUGH */ 2149b50d902SRodney W. Grimes case RHLF: 215f23ed79bSBaptiste Daroussin addto_lineno(&cur_line, -1); 2169b50d902SRodney W. Grimes break; 2177bc60a16SBaptiste Daroussin case '\011': 2187bc60a16SBaptiste Daroussin /* FALLTHROUGH */ 2199b50d902SRodney W. Grimes case FHLF: 220f23ed79bSBaptiste Daroussin addto_lineno(&cur_line, 1); 2219b50d902SRodney W. Grimes if (cur_line > max_line) 2229b50d902SRodney W. Grimes max_line = cur_line; 2239b50d902SRodney W. Grimes } 2249b50d902SRodney W. Grimes continue; 2259b50d902SRodney W. Grimes case NL: 226f23ed79bSBaptiste Daroussin addto_lineno(&cur_line, 2); 2279b50d902SRodney W. Grimes if (cur_line > max_line) 2289b50d902SRodney W. Grimes max_line = cur_line; 2299b50d902SRodney W. Grimes cur_col = 0; 2309b50d902SRodney W. Grimes continue; 2319b50d902SRodney W. Grimes case SPACE: 2329b50d902SRodney W. Grimes ++cur_col; 2339b50d902SRodney W. Grimes continue; 2349b50d902SRodney W. Grimes case SI: 2359b50d902SRodney W. Grimes cur_set = CS_NORMAL; 2369b50d902SRodney W. Grimes continue; 2379b50d902SRodney W. Grimes case SO: 2389b50d902SRodney W. Grimes cur_set = CS_ALTERNATE; 2399b50d902SRodney W. Grimes continue; 2409b50d902SRodney W. Grimes case TAB: /* adjust column */ 2419b50d902SRodney W. Grimes cur_col |= 7; 2429b50d902SRodney W. Grimes ++cur_col; 2439b50d902SRodney W. Grimes continue; 2449b50d902SRodney W. Grimes case VT: 245f23ed79bSBaptiste Daroussin addto_lineno(&cur_line, -2); 2469b50d902SRodney W. Grimes continue; 2479b50d902SRodney W. Grimes } 248c3fae744STim J. Robbins if (iswspace(ch)) { 249c3fae744STim J. Robbins if ((width = wcwidth(ch)) > 0) 250c3fae744STim J. Robbins cur_col += width; 251c3fae744STim J. Robbins continue; 252c3fae744STim J. Robbins } 253844518ffSMike Heffner if (!pass_unknown_seqs) 2549b50d902SRodney W. Grimes continue; 2559b50d902SRodney W. Grimes } 2569b50d902SRodney W. Grimes 2579b50d902SRodney W. Grimes /* Must stuff ch in a line - are we at the right one? */ 258f23ed79bSBaptiste Daroussin if (cur_line + adjust != this_line) { 2599b50d902SRodney W. Grimes LINE *lnew; 2609b50d902SRodney W. Grimes 2619b50d902SRodney W. Grimes /* round up to next line */ 262f23ed79bSBaptiste Daroussin adjust = !fine && (cur_line & 1); 263f23ed79bSBaptiste Daroussin 264f23ed79bSBaptiste Daroussin if (cur_line + adjust < this_line) { 265f23ed79bSBaptiste Daroussin while (cur_line + adjust < this_line && 266f23ed79bSBaptiste Daroussin l->l_prev != NULL) { 2679b50d902SRodney W. Grimes l = l->l_prev; 268f23ed79bSBaptiste Daroussin this_line--; 269f23ed79bSBaptiste Daroussin } 270f23ed79bSBaptiste Daroussin if (cur_line + adjust < this_line) { 2719b50d902SRodney W. Grimes if (nflushd_lines == 0) { 2729b50d902SRodney W. Grimes /* 2739b50d902SRodney W. Grimes * Allow backup past first 2749b50d902SRodney W. Grimes * line if nothing has been 2759b50d902SRodney W. Grimes * flushed yet. 2769b50d902SRodney W. Grimes */ 277f23ed79bSBaptiste Daroussin while (cur_line + adjust 278f23ed79bSBaptiste Daroussin < this_line) { 2799b50d902SRodney W. Grimes lnew = alloc_line(); 2809b50d902SRodney W. Grimes l->l_prev = lnew; 2819b50d902SRodney W. Grimes lnew->l_next = l; 2829b50d902SRodney W. Grimes l = lines = lnew; 2839b50d902SRodney W. Grimes extra_lines++; 284f23ed79bSBaptiste Daroussin this_line--; 2859b50d902SRodney W. Grimes } 2869b50d902SRodney W. Grimes } else { 2879b50d902SRodney W. Grimes if (!warned++) 2889b50d902SRodney W. Grimes dowarn(cur_line); 289f23ed79bSBaptiste Daroussin cur_line = this_line - adjust; 2909b50d902SRodney W. Grimes } 2919b50d902SRodney W. Grimes } 2929b50d902SRodney W. Grimes } else { 2939b50d902SRodney W. Grimes /* may need to allocate here */ 294f23ed79bSBaptiste Daroussin while (cur_line + adjust > this_line) { 295f23ed79bSBaptiste Daroussin if (l->l_next == NULL) { 296f23ed79bSBaptiste Daroussin l->l_next = alloc_line(); 297f23ed79bSBaptiste Daroussin l->l_next->l_prev = l; 298f23ed79bSBaptiste Daroussin } 2999b50d902SRodney W. Grimes l = l->l_next; 300f23ed79bSBaptiste Daroussin this_line++; 3019b50d902SRodney W. Grimes } 3029b50d902SRodney W. Grimes } 303f23ed79bSBaptiste Daroussin if (this_line > nflushd_lines && 304f23ed79bSBaptiste Daroussin this_line - nflushd_lines >= 305f23ed79bSBaptiste Daroussin max_bufd_lines + BUFFER_MARGIN) { 306f23ed79bSBaptiste Daroussin if (extra_lines) { 307f23ed79bSBaptiste Daroussin flush_lines(extra_lines); 308f23ed79bSBaptiste Daroussin extra_lines = 0; 309f23ed79bSBaptiste Daroussin } 310f23ed79bSBaptiste Daroussin flush_lines(this_line - nflushd_lines - 311f23ed79bSBaptiste Daroussin max_bufd_lines); 312f23ed79bSBaptiste Daroussin nflushd_lines = this_line - max_bufd_lines; 3139b50d902SRodney W. Grimes } 3149b50d902SRodney W. Grimes } 3159b50d902SRodney W. Grimes /* grow line's buffer? */ 3169b50d902SRodney W. Grimes if (l->l_line_len + 1 >= l->l_lsize) { 3179b50d902SRodney W. Grimes int need; 3189b50d902SRodney W. Grimes 3199b50d902SRodney W. Grimes need = l->l_lsize ? l->l_lsize * 2 : 90; 32044974a7fSDavid E. O'Brien if ((l->l_line = realloc(l->l_line, 32144974a7fSDavid E. O'Brien (unsigned)need * sizeof(CHAR))) == NULL) 32218463c77SKevin Lo err(1, NULL); 3239b50d902SRodney W. Grimes l->l_lsize = need; 3249b50d902SRodney W. Grimes } 3259b50d902SRodney W. Grimes c = &l->l_line[l->l_line_len++]; 3269b50d902SRodney W. Grimes c->c_char = ch; 3279b50d902SRodney W. Grimes c->c_set = cur_set; 3289b50d902SRodney W. Grimes c->c_column = cur_col; 329c3fae744STim J. Robbins c->c_width = wcwidth(ch); 3309b50d902SRodney W. Grimes /* 3319b50d902SRodney W. Grimes * If things are put in out of order, they will need sorting 3329b50d902SRodney W. Grimes * when it is flushed. 3339b50d902SRodney W. Grimes */ 3349b50d902SRodney W. Grimes if (cur_col < l->l_max_col) 3359b50d902SRodney W. Grimes l->l_needs_sort = 1; 3369b50d902SRodney W. Grimes else 3379b50d902SRodney W. Grimes l->l_max_col = cur_col; 338c3fae744STim J. Robbins if (c->c_width > 0) 339c3fae744STim J. Robbins cur_col += c->c_width; 3409b50d902SRodney W. Grimes } 341c3fae744STim J. Robbins if (ferror(stdin)) 342c3fae744STim J. Robbins err(1, NULL); 343f23ed79bSBaptiste Daroussin if (extra_lines) 344f23ed79bSBaptiste Daroussin flush_lines(extra_lines); 345df3f5d9dSPeter Wemm 3469b50d902SRodney W. Grimes /* goto the last line that had a character on it */ 3479b50d902SRodney W. Grimes for (; l->l_next; l = l->l_next) 3489b50d902SRodney W. Grimes this_line++; 349f23ed79bSBaptiste Daroussin flush_lines(this_line - nflushd_lines + 1); 3509b50d902SRodney W. Grimes 3519b50d902SRodney W. Grimes /* make sure we leave things in a sane state */ 3529b50d902SRodney W. Grimes if (last_set != CS_NORMAL) 353a9da03efSBaptiste Daroussin PUTC(SI); 3549b50d902SRodney W. Grimes 3559b50d902SRodney W. Grimes /* flush out the last few blank lines */ 356f23ed79bSBaptiste Daroussin if (max_line > this_line) 3579b50d902SRodney W. Grimes nblank_lines = max_line - this_line; 3589b50d902SRodney W. Grimes if (max_line & 1) 3599b50d902SRodney W. Grimes nblank_lines++; 3609b50d902SRodney W. Grimes flush_blanks(); 3619b50d902SRodney W. Grimes exit(0); 3629b50d902SRodney W. Grimes } 3639b50d902SRodney W. Grimes 364ab8d971cSEd Schouten static void 365f4ac32deSDavid Malone flush_lines(int nflush) 3669b50d902SRodney W. Grimes { 3679b50d902SRodney W. Grimes LINE *l; 3689b50d902SRodney W. Grimes 3699b50d902SRodney W. Grimes while (--nflush >= 0) { 3709b50d902SRodney W. Grimes l = lines; 3719b50d902SRodney W. Grimes lines = l->l_next; 3729b50d902SRodney W. Grimes if (l->l_line) { 3739b50d902SRodney W. Grimes flush_blanks(); 3749b50d902SRodney W. Grimes flush_line(l); 3759b50d902SRodney W. Grimes } 376f23ed79bSBaptiste Daroussin if (l->l_line || l->l_next) 3779b50d902SRodney W. Grimes nblank_lines++; 3789b50d902SRodney W. Grimes if (l->l_line) 379f34d0e74SMike Heffner (void)free(l->l_line); 3809b50d902SRodney W. Grimes free_line(l); 3819b50d902SRodney W. Grimes } 3829b50d902SRodney W. Grimes if (lines) 3839b50d902SRodney W. Grimes lines->l_prev = NULL; 3849b50d902SRodney W. Grimes } 3859b50d902SRodney W. Grimes 3869b50d902SRodney W. Grimes /* 3879b50d902SRodney W. Grimes * Print a number of newline/half newlines. If fine flag is set, nblank_lines 3889b50d902SRodney W. Grimes * is the number of half line feeds, otherwise it is the number of whole line 3899b50d902SRodney W. Grimes * feeds. 3909b50d902SRodney W. Grimes */ 391ab8d971cSEd Schouten static void 392f4ac32deSDavid Malone flush_blanks(void) 3939b50d902SRodney W. Grimes { 3949b50d902SRodney W. Grimes int half, i, nb; 3959b50d902SRodney W. Grimes 3969b50d902SRodney W. Grimes half = 0; 3979b50d902SRodney W. Grimes nb = nblank_lines; 3989b50d902SRodney W. Grimes if (nb & 1) { 3999b50d902SRodney W. Grimes if (fine) 4009b50d902SRodney W. Grimes half = 1; 4019b50d902SRodney W. Grimes else 4029b50d902SRodney W. Grimes nb++; 4039b50d902SRodney W. Grimes } 4049b50d902SRodney W. Grimes nb /= 2; 4059b50d902SRodney W. Grimes for (i = nb; --i >= 0;) 4069b50d902SRodney W. Grimes PUTC('\n'); 4079b50d902SRodney W. Grimes if (half) { 408a9da03efSBaptiste Daroussin PUTC(ESC); 409a9da03efSBaptiste Daroussin PUTC(FHLF); 4109b50d902SRodney W. Grimes if (!nb) 4119b50d902SRodney W. Grimes PUTC('\r'); 4129b50d902SRodney W. Grimes } 4139b50d902SRodney W. Grimes nblank_lines = 0; 4149b50d902SRodney W. Grimes } 4159b50d902SRodney W. Grimes 4169b50d902SRodney W. Grimes /* 4179b50d902SRodney W. Grimes * Write a line to stdout taking care of space to tab conversion (-h flag) 4189b50d902SRodney W. Grimes * and character set shifts. 4199b50d902SRodney W. Grimes */ 420ab8d971cSEd Schouten static void 421f4ac32deSDavid Malone flush_line(LINE *l) 4229b50d902SRodney W. Grimes { 4239b50d902SRodney W. Grimes CHAR *c, *endc; 424ae66acacSStefan Farfeleder int i, j, nchars, last_col, save, this_col, tot; 4259b50d902SRodney W. Grimes 4269b50d902SRodney W. Grimes last_col = 0; 4279b50d902SRodney W. Grimes nchars = l->l_line_len; 4289b50d902SRodney W. Grimes 4299b50d902SRodney W. Grimes if (l->l_needs_sort) { 4309b50d902SRodney W. Grimes static CHAR *sorted; 431ae66acacSStefan Farfeleder static int count_size, *count, sorted_size; 4329b50d902SRodney W. Grimes 4339b50d902SRodney W. Grimes /* 4349b50d902SRodney W. Grimes * Do an O(n) sort on l->l_line by column being careful to 4359b50d902SRodney W. Grimes * preserve the order of characters in the same column. 4369b50d902SRodney W. Grimes */ 4379b50d902SRodney W. Grimes if (l->l_lsize > sorted_size) { 4389b50d902SRodney W. Grimes sorted_size = l->l_lsize; 43944974a7fSDavid E. O'Brien if ((sorted = realloc(sorted, 44044974a7fSDavid E. O'Brien (unsigned)sizeof(CHAR) * sorted_size)) == NULL) 44118463c77SKevin Lo err(1, NULL); 4429b50d902SRodney W. Grimes } 4439b50d902SRodney W. Grimes if (l->l_max_col >= count_size) { 4449b50d902SRodney W. Grimes count_size = l->l_max_col + 1; 44544974a7fSDavid E. O'Brien if ((count = realloc(count, 44644974a7fSDavid E. O'Brien (unsigned)sizeof(int) * count_size)) == NULL) 44718463c77SKevin Lo err(1, NULL); 4489b50d902SRodney W. Grimes } 449f34d0e74SMike Heffner memset(count, 0, sizeof(int) * l->l_max_col + 1); 4509b50d902SRodney W. Grimes for (i = nchars, c = l->l_line; --i >= 0; c++) 4519b50d902SRodney W. Grimes count[c->c_column]++; 4529b50d902SRodney W. Grimes 4539b50d902SRodney W. Grimes /* 4549b50d902SRodney W. Grimes * calculate running total (shifted down by 1) to use as 4559b50d902SRodney W. Grimes * indices into new line. 4569b50d902SRodney W. Grimes */ 4579b50d902SRodney W. Grimes for (tot = 0, i = 0; i <= l->l_max_col; i++) { 4589b50d902SRodney W. Grimes save = count[i]; 4599b50d902SRodney W. Grimes count[i] = tot; 4609b50d902SRodney W. Grimes tot += save; 4619b50d902SRodney W. Grimes } 4629b50d902SRodney W. Grimes 4639b50d902SRodney W. Grimes for (i = nchars, c = l->l_line; --i >= 0; c++) 4649b50d902SRodney W. Grimes sorted[count[c->c_column]++] = *c; 4659b50d902SRodney W. Grimes c = sorted; 4669b50d902SRodney W. Grimes } else 4679b50d902SRodney W. Grimes c = l->l_line; 4689b50d902SRodney W. Grimes while (nchars > 0) { 4699b50d902SRodney W. Grimes this_col = c->c_column; 4709b50d902SRodney W. Grimes endc = c; 4719b50d902SRodney W. Grimes do { 4729b50d902SRodney W. Grimes ++endc; 4739b50d902SRodney W. Grimes } while (--nchars > 0 && this_col == endc->c_column); 4749b50d902SRodney W. Grimes 4759b50d902SRodney W. Grimes /* if -b only print last character */ 476c3fae744STim J. Robbins if (no_backspaces) { 4779b50d902SRodney W. Grimes c = endc - 1; 478c3fae744STim J. Robbins if (nchars > 0 && 479c3fae744STim J. Robbins this_col + c->c_width > endc->c_column) 480c3fae744STim J. Robbins continue; 481c3fae744STim J. Robbins } 4829b50d902SRodney W. Grimes 4839b50d902SRodney W. Grimes if (this_col > last_col) { 4849b50d902SRodney W. Grimes int nspace = this_col - last_col; 4859b50d902SRodney W. Grimes 4869b50d902SRodney W. Grimes if (compress_spaces && nspace > 1) { 487f746e67cSBruce Evans while (1) { 4885eb2aa45SEd Maste int tab_col, tab_size; 4899b50d902SRodney W. Grimes 490f746e67cSBruce Evans tab_col = (last_col + 8) & ~7; 491f746e67cSBruce Evans if (tab_col > this_col) 492f746e67cSBruce Evans break; 493f746e67cSBruce Evans tab_size = tab_col - last_col; 494f746e67cSBruce Evans if (tab_size == 1) 495f746e67cSBruce Evans PUTC(' '); 496f746e67cSBruce Evans else 4979b50d902SRodney W. Grimes PUTC('\t'); 498f746e67cSBruce Evans nspace -= tab_size; 499f746e67cSBruce Evans last_col = tab_col; 500f746e67cSBruce Evans } 5019b50d902SRodney W. Grimes } 5029b50d902SRodney W. Grimes while (--nspace >= 0) 5039b50d902SRodney W. Grimes PUTC(' '); 5049b50d902SRodney W. Grimes last_col = this_col; 5059b50d902SRodney W. Grimes } 5069b50d902SRodney W. Grimes 5079b50d902SRodney W. Grimes for (;;) { 5089b50d902SRodney W. Grimes if (c->c_set != last_set) { 5099b50d902SRodney W. Grimes switch (c->c_set) { 5109b50d902SRodney W. Grimes case CS_NORMAL: 511a9da03efSBaptiste Daroussin PUTC(SI); 5129b50d902SRodney W. Grimes break; 5139b50d902SRodney W. Grimes case CS_ALTERNATE: 514a9da03efSBaptiste Daroussin PUTC(SO); 5159b50d902SRodney W. Grimes } 5169b50d902SRodney W. Grimes last_set = c->c_set; 5179b50d902SRodney W. Grimes } 5189b50d902SRodney W. Grimes PUTC(c->c_char); 519c3fae744STim J. Robbins if ((c + 1) < endc) 52070708375SDavid Malone for (j = 0; j < c->c_width; j++) 521c3fae744STim J. Robbins PUTC('\b'); 5229b50d902SRodney W. Grimes if (++c >= endc) 5239b50d902SRodney W. Grimes break; 5249b50d902SRodney W. Grimes } 525c3fae744STim J. Robbins last_col += (c - 1)->c_width; 5269b50d902SRodney W. Grimes } 5279b50d902SRodney W. Grimes } 5289b50d902SRodney W. Grimes 529f23ed79bSBaptiste Daroussin /* 530f23ed79bSBaptiste Daroussin * Increment or decrement a line number, checking for overflow. 531f23ed79bSBaptiste Daroussin * Stop one below INT_MAX such that the adjust variable is safe. 532f23ed79bSBaptiste Daroussin */ 533f23ed79bSBaptiste Daroussin void 534f23ed79bSBaptiste Daroussin addto_lineno(int *lno, int offset) 535f23ed79bSBaptiste Daroussin { 536f23ed79bSBaptiste Daroussin if (offset > 0) { 537f23ed79bSBaptiste Daroussin if (*lno >= INT_MAX - offset) 538f23ed79bSBaptiste Daroussin errx(1, "too many lines"); 539f23ed79bSBaptiste Daroussin } else { 540f23ed79bSBaptiste Daroussin if (*lno < INT_MIN - offset) 541f23ed79bSBaptiste Daroussin errx(1, "too many reverse line feeds"); 542f23ed79bSBaptiste Daroussin } 543f23ed79bSBaptiste Daroussin *lno += offset; 544f23ed79bSBaptiste Daroussin } 545f23ed79bSBaptiste Daroussin 5469b50d902SRodney W. Grimes #define NALLOC 64 5479b50d902SRodney W. Grimes 5489b50d902SRodney W. Grimes static LINE *line_freelist; 5499b50d902SRodney W. Grimes 550ab8d971cSEd Schouten static LINE * 551f4ac32deSDavid Malone alloc_line(void) 5529b50d902SRodney W. Grimes { 5539b50d902SRodney W. Grimes LINE *l; 5549b50d902SRodney W. Grimes int i; 5559b50d902SRodney W. Grimes 5569b50d902SRodney W. Grimes if (!line_freelist) { 55744974a7fSDavid E. O'Brien if ((l = realloc(NULL, sizeof(LINE) * NALLOC)) == NULL) 55818463c77SKevin Lo err(1, NULL); 5599b50d902SRodney W. Grimes line_freelist = l; 5609b50d902SRodney W. Grimes for (i = 1; i < NALLOC; i++, l++) 5619b50d902SRodney W. Grimes l->l_next = l + 1; 5629b50d902SRodney W. Grimes l->l_next = NULL; 5639b50d902SRodney W. Grimes } 5649b50d902SRodney W. Grimes l = line_freelist; 5659b50d902SRodney W. Grimes line_freelist = l->l_next; 5669b50d902SRodney W. Grimes 5679b50d902SRodney W. Grimes memset(l, 0, sizeof(LINE)); 5689b50d902SRodney W. Grimes return (l); 5699b50d902SRodney W. Grimes } 5709b50d902SRodney W. Grimes 571ab8d971cSEd Schouten static void 572f4ac32deSDavid Malone free_line(LINE *l) 5739b50d902SRodney W. Grimes { 5749b50d902SRodney W. Grimes 5759b50d902SRodney W. Grimes l->l_next = line_freelist; 5769b50d902SRodney W. Grimes line_freelist = l; 5779b50d902SRodney W. Grimes } 5789b50d902SRodney W. Grimes 579ab8d971cSEd Schouten static void 580f4ac32deSDavid Malone usage(void) 5819b50d902SRodney W. Grimes { 5829b50d902SRodney W. Grimes 583844518ffSMike Heffner (void)fprintf(stderr, "usage: col [-bfhpx] [-l nline]\n"); 5849b50d902SRodney W. Grimes exit(1); 5859b50d902SRodney W. Grimes } 5869b50d902SRodney W. Grimes 587ab8d971cSEd Schouten static void 588f4ac32deSDavid Malone dowarn(int line) 5899b50d902SRodney W. Grimes { 5909b50d902SRodney W. Grimes 5919b50d902SRodney W. Grimes warnx("warning: can't back up %s", 5929b50d902SRodney W. Grimes line < 0 ? "past first line" : "-- line already flushed"); 5939b50d902SRodney W. Grimes } 594