1*0a6a1f1dSLionel Sambuc /* $NetBSD: col.c,v 1.18 2014/10/18 14:56:14 christos Exp $ */
27b9673cdSThomas Cort
37b9673cdSThomas Cort /*-
47b9673cdSThomas Cort * Copyright (c) 1990, 1993, 1994
57b9673cdSThomas Cort * The Regents of the University of California. All rights reserved.
67b9673cdSThomas Cort *
77b9673cdSThomas Cort * This code is derived from software contributed to Berkeley by
87b9673cdSThomas Cort * Michael Rendell of the Memorial University of Newfoundland.
97b9673cdSThomas Cort *
107b9673cdSThomas Cort * Redistribution and use in source and binary forms, with or without
117b9673cdSThomas Cort * modification, are permitted provided that the following conditions
127b9673cdSThomas Cort * are met:
137b9673cdSThomas Cort * 1. Redistributions of source code must retain the above copyright
147b9673cdSThomas Cort * notice, this list of conditions and the following disclaimer.
157b9673cdSThomas Cort * 2. Redistributions in binary form must reproduce the above copyright
167b9673cdSThomas Cort * notice, this list of conditions and the following disclaimer in the
177b9673cdSThomas Cort * documentation and/or other materials provided with the distribution.
187b9673cdSThomas Cort * 3. Neither the name of the University nor the names of its contributors
197b9673cdSThomas Cort * may be used to endorse or promote products derived from this software
207b9673cdSThomas Cort * without specific prior written permission.
217b9673cdSThomas Cort *
227b9673cdSThomas Cort * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
237b9673cdSThomas Cort * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
247b9673cdSThomas Cort * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
257b9673cdSThomas Cort * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
267b9673cdSThomas Cort * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
277b9673cdSThomas Cort * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
287b9673cdSThomas Cort * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
297b9673cdSThomas Cort * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
307b9673cdSThomas Cort * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
317b9673cdSThomas Cort * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
327b9673cdSThomas Cort * SUCH DAMAGE.
337b9673cdSThomas Cort */
347b9673cdSThomas Cort
357b9673cdSThomas Cort #include <sys/cdefs.h>
367b9673cdSThomas Cort #ifndef lint
377b9673cdSThomas Cort __COPYRIGHT("@(#) Copyright (c) 1990, 1993, 1994\
387b9673cdSThomas Cort The Regents of the University of California. All rights reserved.");
397b9673cdSThomas Cort #endif /* not lint */
407b9673cdSThomas Cort
417b9673cdSThomas Cort #ifndef lint
427b9673cdSThomas Cort #if 0
437b9673cdSThomas Cort static char sccsid[] = "@(#)col.c 8.5 (Berkeley) 5/4/95";
447b9673cdSThomas Cort #endif
45*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: col.c,v 1.18 2014/10/18 14:56:14 christos Exp $");
467b9673cdSThomas Cort #endif /* not lint */
477b9673cdSThomas Cort
487b9673cdSThomas Cort #include <ctype.h>
497b9673cdSThomas Cort #include <err.h>
507b9673cdSThomas Cort #include <string.h>
517b9673cdSThomas Cort #include <stdio.h>
527b9673cdSThomas Cort #include <stdlib.h>
537b9673cdSThomas Cort #include <unistd.h>
547b9673cdSThomas Cort
557b9673cdSThomas Cort #define BS '\b' /* backspace */
567b9673cdSThomas Cort #define TAB '\t' /* tab */
577b9673cdSThomas Cort #define SPACE ' ' /* space */
587b9673cdSThomas Cort #define NL '\n' /* newline */
597b9673cdSThomas Cort #define CR '\r' /* carriage return */
607b9673cdSThomas Cort #define ESC '\033' /* escape */
617b9673cdSThomas Cort #define SI '\017' /* shift in to normal character set */
627b9673cdSThomas Cort #define SO '\016' /* shift out to alternate character set */
637b9673cdSThomas Cort #define VT '\013' /* vertical tab (aka reverse line feed) */
647b9673cdSThomas Cort #define RLF '\007' /* ESC-07 reverse line feed */
657b9673cdSThomas Cort #define RHLF '\010' /* ESC-010 reverse half-line feed */
667b9673cdSThomas Cort #define FHLF '\011' /* ESC-011 forward half-line feed */
677b9673cdSThomas Cort
687b9673cdSThomas Cort /* build up at least this many lines before flushing them out */
697b9673cdSThomas Cort #define BUFFER_MARGIN 32
707b9673cdSThomas Cort
717b9673cdSThomas Cort typedef char CSET;
727b9673cdSThomas Cort
737b9673cdSThomas Cort typedef struct char_str {
747b9673cdSThomas Cort #define CS_NORMAL 1
757b9673cdSThomas Cort #define CS_ALTERNATE 2
767b9673cdSThomas Cort short c_column; /* column character is in */
777b9673cdSThomas Cort CSET c_set; /* character set (currently only 2) */
787b9673cdSThomas Cort char c_char; /* character in question */
797b9673cdSThomas Cort } CHAR;
807b9673cdSThomas Cort
817b9673cdSThomas Cort typedef struct line_str LINE;
827b9673cdSThomas Cort struct line_str {
837b9673cdSThomas Cort CHAR *l_line; /* characters on the line */
847b9673cdSThomas Cort LINE *l_prev; /* previous line */
857b9673cdSThomas Cort LINE *l_next; /* next line */
867b9673cdSThomas Cort int l_lsize; /* allocated sizeof l_line */
877b9673cdSThomas Cort int l_line_len; /* strlen(l_line) */
887b9673cdSThomas Cort int l_needs_sort; /* set if chars went in out of order */
897b9673cdSThomas Cort int l_max_col; /* max column in the line */
907b9673cdSThomas Cort };
917b9673cdSThomas Cort
927b9673cdSThomas Cort static LINE *alloc_line(void);
937b9673cdSThomas Cort static void dowarn(int);
947b9673cdSThomas Cort static void flush_line(LINE *);
957b9673cdSThomas Cort static void flush_lines(int);
967b9673cdSThomas Cort static void flush_blanks(void);
977b9673cdSThomas Cort static void free_line(LINE *);
987b9673cdSThomas Cort __dead static void usage(void);
997b9673cdSThomas Cort __dead static void wrerr(void);
1007b9673cdSThomas Cort static void *xmalloc(void *, size_t);
1017b9673cdSThomas Cort
1027b9673cdSThomas Cort static CSET last_set; /* char_set of last char printed */
1037b9673cdSThomas Cort static LINE *lines;
1047b9673cdSThomas Cort static int compress_spaces; /* if doing space -> tab conversion */
1057b9673cdSThomas Cort static int fine; /* if `fine' resolution (half lines) */
1067b9673cdSThomas Cort static int max_bufd_lines; /* max # lines to keep in memory */
1077b9673cdSThomas Cort static int nblank_lines; /* # blanks after last flushed line */
1087b9673cdSThomas Cort static int no_backspaces; /* if not to output any backspaces */
1097b9673cdSThomas Cort static int pass_unknown_seqs; /* whether to pass unknown control sequences */
1107b9673cdSThomas Cort
1117b9673cdSThomas Cort #define PUTC(ch) \
1127b9673cdSThomas Cort if (putchar(ch) == EOF) \
1137b9673cdSThomas Cort wrerr();
1147b9673cdSThomas Cort
1157b9673cdSThomas Cort int
main(int argc,char ** argv)1167b9673cdSThomas Cort main(int argc, char **argv)
1177b9673cdSThomas Cort {
1187b9673cdSThomas Cort int ch;
1197b9673cdSThomas Cort CHAR *c;
1207b9673cdSThomas Cort CSET cur_set; /* current character set */
1217b9673cdSThomas Cort LINE *l; /* current line */
1227b9673cdSThomas Cort int extra_lines; /* # of lines above first line */
1237b9673cdSThomas Cort int cur_col; /* current column */
1247b9673cdSThomas Cort int cur_line; /* line number of current position */
1257b9673cdSThomas Cort int max_line; /* max value of cur_line */
1267b9673cdSThomas Cort int this_line; /* line l points to */
1277b9673cdSThomas Cort int nflushd_lines; /* number of lines that were flushed */
1287b9673cdSThomas Cort int adjust, opt, warned;
1297b9673cdSThomas Cort
1307b9673cdSThomas Cort max_bufd_lines = 128;
1317b9673cdSThomas Cort compress_spaces = 1; /* compress spaces into tabs */
1327b9673cdSThomas Cort pass_unknown_seqs = 0; /* remove unknown escape sequences */
1337b9673cdSThomas Cort while ((opt = getopt(argc, argv, "bfhl:px")) != -1)
1347b9673cdSThomas Cort switch (opt) {
1357b9673cdSThomas Cort case 'b': /* do not output backspaces */
1367b9673cdSThomas Cort no_backspaces = 1;
1377b9673cdSThomas Cort break;
1387b9673cdSThomas Cort case 'f': /* allow half forward line feeds */
1397b9673cdSThomas Cort fine = 1;
1407b9673cdSThomas Cort break;
1417b9673cdSThomas Cort case 'h': /* compress spaces into tabs */
1427b9673cdSThomas Cort compress_spaces = 1;
1437b9673cdSThomas Cort break;
1447b9673cdSThomas Cort case 'l': /* buffered line count */
1457b9673cdSThomas Cort if ((max_bufd_lines = atoi(optarg)) <= 0) {
1467b9673cdSThomas Cort (void)fprintf(stderr,
1477b9673cdSThomas Cort "col: bad -l argument %s.\n", optarg);
1487b9673cdSThomas Cort exit(EXIT_FAILURE);
1497b9673cdSThomas Cort }
1507b9673cdSThomas Cort break;
1517b9673cdSThomas Cort case 'p': /* pass unknown control sequences */
1527b9673cdSThomas Cort pass_unknown_seqs = 1;
1537b9673cdSThomas Cort break;
1547b9673cdSThomas Cort case 'x': /* do not compress spaces into tabs */
1557b9673cdSThomas Cort compress_spaces = 0;
1567b9673cdSThomas Cort break;
1577b9673cdSThomas Cort case '?':
1587b9673cdSThomas Cort default:
1597b9673cdSThomas Cort usage();
1607b9673cdSThomas Cort }
1617b9673cdSThomas Cort
1627b9673cdSThomas Cort if (optind != argc)
1637b9673cdSThomas Cort usage();
1647b9673cdSThomas Cort
1657b9673cdSThomas Cort /* this value is in half lines */
1667b9673cdSThomas Cort max_bufd_lines *= 2;
1677b9673cdSThomas Cort
1687b9673cdSThomas Cort adjust = cur_col = extra_lines = warned = 0;
1697b9673cdSThomas Cort cur_line = max_line = nflushd_lines = this_line = 0;
1707b9673cdSThomas Cort cur_set = last_set = CS_NORMAL;
1717b9673cdSThomas Cort lines = l = alloc_line();
1727b9673cdSThomas Cort
1737b9673cdSThomas Cort while ((ch = getchar()) != EOF) {
1747b9673cdSThomas Cort if (!isgraph(ch)) {
1757b9673cdSThomas Cort switch (ch) {
1767b9673cdSThomas Cort case BS: /* can't go back further */
1777b9673cdSThomas Cort if (cur_col == 0)
1787b9673cdSThomas Cort continue;
1797b9673cdSThomas Cort --cur_col;
1807b9673cdSThomas Cort continue;
1817b9673cdSThomas Cort case CR:
1827b9673cdSThomas Cort cur_col = 0;
1837b9673cdSThomas Cort continue;
1847b9673cdSThomas Cort case ESC: /* just ignore EOF */
1857b9673cdSThomas Cort switch(getchar()) {
1867b9673cdSThomas Cort case RLF:
1877b9673cdSThomas Cort cur_line -= 2;
1887b9673cdSThomas Cort break;
1897b9673cdSThomas Cort case RHLF:
1907b9673cdSThomas Cort cur_line--;
1917b9673cdSThomas Cort break;
1927b9673cdSThomas Cort case FHLF:
1937b9673cdSThomas Cort cur_line++;
1947b9673cdSThomas Cort if (cur_line > max_line)
1957b9673cdSThomas Cort max_line = cur_line;
1967b9673cdSThomas Cort }
1977b9673cdSThomas Cort continue;
1987b9673cdSThomas Cort case NL:
1997b9673cdSThomas Cort cur_line += 2;
2007b9673cdSThomas Cort if (cur_line > max_line)
2017b9673cdSThomas Cort max_line = cur_line;
2027b9673cdSThomas Cort cur_col = 0;
2037b9673cdSThomas Cort continue;
2047b9673cdSThomas Cort case SPACE:
2057b9673cdSThomas Cort ++cur_col;
2067b9673cdSThomas Cort continue;
2077b9673cdSThomas Cort case SI:
2087b9673cdSThomas Cort cur_set = CS_NORMAL;
2097b9673cdSThomas Cort continue;
2107b9673cdSThomas Cort case SO:
2117b9673cdSThomas Cort cur_set = CS_ALTERNATE;
2127b9673cdSThomas Cort continue;
2137b9673cdSThomas Cort case TAB: /* adjust column */
2147b9673cdSThomas Cort cur_col |= 7;
2157b9673cdSThomas Cort ++cur_col;
2167b9673cdSThomas Cort continue;
2177b9673cdSThomas Cort case VT:
2187b9673cdSThomas Cort cur_line -= 2;
2197b9673cdSThomas Cort continue;
2207b9673cdSThomas Cort }
2217b9673cdSThomas Cort if (!pass_unknown_seqs)
2227b9673cdSThomas Cort continue;
2237b9673cdSThomas Cort }
2247b9673cdSThomas Cort
2257b9673cdSThomas Cort /* Must stuff ch in a line - are we at the right one? */
2267b9673cdSThomas Cort if (cur_line != this_line - adjust) {
2277b9673cdSThomas Cort LINE *lnew;
2287b9673cdSThomas Cort int nmove;
2297b9673cdSThomas Cort
2307b9673cdSThomas Cort adjust = 0;
2317b9673cdSThomas Cort nmove = cur_line - this_line;
2327b9673cdSThomas Cort if (!fine) {
2337b9673cdSThomas Cort /* round up to next line */
2347b9673cdSThomas Cort if (cur_line & 1) {
2357b9673cdSThomas Cort adjust = 1;
2367b9673cdSThomas Cort nmove++;
2377b9673cdSThomas Cort }
2387b9673cdSThomas Cort }
2397b9673cdSThomas Cort if (nmove < 0) {
2407b9673cdSThomas Cort for (; nmove < 0 && l->l_prev; nmove++)
2417b9673cdSThomas Cort l = l->l_prev;
2427b9673cdSThomas Cort if (nmove) {
2437b9673cdSThomas Cort if (nflushd_lines == 0) {
2447b9673cdSThomas Cort /*
2457b9673cdSThomas Cort * Allow backup past first
2467b9673cdSThomas Cort * line if nothing has been
2477b9673cdSThomas Cort * flushed yet.
2487b9673cdSThomas Cort */
2497b9673cdSThomas Cort for (; nmove < 0; nmove++) {
2507b9673cdSThomas Cort lnew = alloc_line();
2517b9673cdSThomas Cort l->l_prev = lnew;
2527b9673cdSThomas Cort lnew->l_next = l;
2537b9673cdSThomas Cort l = lines = lnew;
2547b9673cdSThomas Cort extra_lines++;
2557b9673cdSThomas Cort }
2567b9673cdSThomas Cort } else {
2577b9673cdSThomas Cort if (!warned++)
2587b9673cdSThomas Cort dowarn(cur_line);
2597b9673cdSThomas Cort cur_line -= nmove;
2607b9673cdSThomas Cort }
2617b9673cdSThomas Cort }
2627b9673cdSThomas Cort } else {
2637b9673cdSThomas Cort /* may need to allocate here */
2647b9673cdSThomas Cort for (; nmove > 0 && l->l_next; nmove--)
2657b9673cdSThomas Cort l = l->l_next;
2667b9673cdSThomas Cort for (; nmove > 0; nmove--) {
2677b9673cdSThomas Cort lnew = alloc_line();
2687b9673cdSThomas Cort lnew->l_prev = l;
2697b9673cdSThomas Cort l->l_next = lnew;
2707b9673cdSThomas Cort l = lnew;
2717b9673cdSThomas Cort }
2727b9673cdSThomas Cort }
2737b9673cdSThomas Cort this_line = cur_line + adjust;
2747b9673cdSThomas Cort nmove = this_line - nflushd_lines;
2757b9673cdSThomas Cort if (nmove >= max_bufd_lines + BUFFER_MARGIN) {
2767b9673cdSThomas Cort nflushd_lines += nmove - max_bufd_lines;
2777b9673cdSThomas Cort flush_lines(nmove - max_bufd_lines);
2787b9673cdSThomas Cort }
2797b9673cdSThomas Cort }
2807b9673cdSThomas Cort /* grow line's buffer? */
2817b9673cdSThomas Cort if (l->l_line_len + 1 >= l->l_lsize) {
2827b9673cdSThomas Cort int need;
2837b9673cdSThomas Cort
2847b9673cdSThomas Cort need = l->l_lsize ? l->l_lsize * 2 : 90;
2857b9673cdSThomas Cort l->l_line = (CHAR *)xmalloc((void *) l->l_line,
2867b9673cdSThomas Cort (unsigned) need * sizeof(CHAR));
2877b9673cdSThomas Cort l->l_lsize = need;
2887b9673cdSThomas Cort }
2897b9673cdSThomas Cort c = &l->l_line[l->l_line_len++];
2907b9673cdSThomas Cort c->c_char = ch;
2917b9673cdSThomas Cort c->c_set = cur_set;
2927b9673cdSThomas Cort c->c_column = cur_col;
2937b9673cdSThomas Cort /*
2947b9673cdSThomas Cort * If things are put in out of order, they will need sorting
2957b9673cdSThomas Cort * when it is flushed.
2967b9673cdSThomas Cort */
2977b9673cdSThomas Cort if (cur_col < l->l_max_col)
2987b9673cdSThomas Cort l->l_needs_sort = 1;
2997b9673cdSThomas Cort else
3007b9673cdSThomas Cort l->l_max_col = cur_col;
3017b9673cdSThomas Cort cur_col++;
3027b9673cdSThomas Cort }
3037b9673cdSThomas Cort if (max_line == 0)
3047b9673cdSThomas Cort exit(EXIT_SUCCESS); /* no lines, so just exit */
3057b9673cdSThomas Cort
3067b9673cdSThomas Cort /* goto the last line that had a character on it */
3077b9673cdSThomas Cort for (; l->l_next; l = l->l_next)
3087b9673cdSThomas Cort this_line++;
3097b9673cdSThomas Cort flush_lines(this_line - nflushd_lines + extra_lines + 1);
3107b9673cdSThomas Cort
3117b9673cdSThomas Cort /* make sure we leave things in a sane state */
3127b9673cdSThomas Cort if (last_set != CS_NORMAL)
3137b9673cdSThomas Cort PUTC('\017');
3147b9673cdSThomas Cort
3157b9673cdSThomas Cort /* flush out the last few blank lines */
3167b9673cdSThomas Cort nblank_lines = max_line - this_line;
3177b9673cdSThomas Cort if (max_line & 1)
3187b9673cdSThomas Cort nblank_lines++;
3197b9673cdSThomas Cort else if (!nblank_lines)
3207b9673cdSThomas Cort /* missing a \n on the last line? */
3217b9673cdSThomas Cort nblank_lines = 2;
3227b9673cdSThomas Cort flush_blanks();
3237b9673cdSThomas Cort exit(EXIT_SUCCESS);
3247b9673cdSThomas Cort /* NOTREACHED */
3257b9673cdSThomas Cort }
3267b9673cdSThomas Cort
3277b9673cdSThomas Cort static void
flush_lines(int nflush)3287b9673cdSThomas Cort flush_lines(int nflush)
3297b9673cdSThomas Cort {
3307b9673cdSThomas Cort LINE *l;
3317b9673cdSThomas Cort
3327b9673cdSThomas Cort while (--nflush >= 0) {
3337b9673cdSThomas Cort l = lines;
3347b9673cdSThomas Cort lines = l->l_next;
3357b9673cdSThomas Cort if (l->l_line) {
3367b9673cdSThomas Cort flush_blanks();
3377b9673cdSThomas Cort flush_line(l);
3387b9673cdSThomas Cort }
3397b9673cdSThomas Cort nblank_lines++;
3407b9673cdSThomas Cort if (l->l_line)
3417b9673cdSThomas Cort (void)free((void *)l->l_line);
3427b9673cdSThomas Cort free_line(l);
3437b9673cdSThomas Cort }
3447b9673cdSThomas Cort if (lines)
3457b9673cdSThomas Cort lines->l_prev = NULL;
3467b9673cdSThomas Cort }
3477b9673cdSThomas Cort
3487b9673cdSThomas Cort /*
3497b9673cdSThomas Cort * Print a number of newline/half newlines. If fine flag is set, nblank_lines
3507b9673cdSThomas Cort * is the number of half line feeds, otherwise it is the number of whole line
3517b9673cdSThomas Cort * feeds.
3527b9673cdSThomas Cort */
3537b9673cdSThomas Cort static void
flush_blanks(void)3547b9673cdSThomas Cort flush_blanks(void)
3557b9673cdSThomas Cort {
3567b9673cdSThomas Cort int half, i, nb;
3577b9673cdSThomas Cort
3587b9673cdSThomas Cort half = 0;
3597b9673cdSThomas Cort nb = nblank_lines;
3607b9673cdSThomas Cort if (nb & 1) {
3617b9673cdSThomas Cort if (fine)
3627b9673cdSThomas Cort half = 1;
3637b9673cdSThomas Cort else
3647b9673cdSThomas Cort nb++;
3657b9673cdSThomas Cort }
3667b9673cdSThomas Cort nb /= 2;
3677b9673cdSThomas Cort for (i = nb; --i >= 0;)
3687b9673cdSThomas Cort PUTC('\n');
3697b9673cdSThomas Cort if (half) {
3707b9673cdSThomas Cort PUTC('\033');
371*0a6a1f1dSLionel Sambuc PUTC('\011');
3727b9673cdSThomas Cort if (!nb)
3737b9673cdSThomas Cort PUTC('\r');
3747b9673cdSThomas Cort }
3757b9673cdSThomas Cort nblank_lines = 0;
3767b9673cdSThomas Cort }
3777b9673cdSThomas Cort
3787b9673cdSThomas Cort /*
3797b9673cdSThomas Cort * Write a line to stdout taking care of space to tab conversion (-h flag)
3807b9673cdSThomas Cort * and character set shifts.
3817b9673cdSThomas Cort */
3827b9673cdSThomas Cort static void
flush_line(LINE * l)3837b9673cdSThomas Cort flush_line(LINE *l)
3847b9673cdSThomas Cort {
3857b9673cdSThomas Cort CHAR *c, *endc;
3867b9673cdSThomas Cort int nchars, last_col, this_col;
3877b9673cdSThomas Cort
3887b9673cdSThomas Cort last_col = 0;
3897b9673cdSThomas Cort nchars = l->l_line_len;
3907b9673cdSThomas Cort
3917b9673cdSThomas Cort if (l->l_needs_sort) {
3927b9673cdSThomas Cort static CHAR *sorted;
3937b9673cdSThomas Cort static int count_size, *count, i, save, sorted_size, tot;
3947b9673cdSThomas Cort
3957b9673cdSThomas Cort /*
3967b9673cdSThomas Cort * Do an O(n) sort on l->l_line by column being careful to
3977b9673cdSThomas Cort * preserve the order of characters in the same column.
3987b9673cdSThomas Cort */
3997b9673cdSThomas Cort if (l->l_lsize > sorted_size) {
4007b9673cdSThomas Cort sorted_size = l->l_lsize;
4017b9673cdSThomas Cort sorted = (CHAR *)xmalloc((void *)sorted,
4027b9673cdSThomas Cort (unsigned)sizeof(CHAR) * sorted_size);
4037b9673cdSThomas Cort }
4047b9673cdSThomas Cort if (l->l_max_col >= count_size) {
4057b9673cdSThomas Cort count_size = l->l_max_col + 1;
4067b9673cdSThomas Cort count = (int *)xmalloc((void *)count,
4077b9673cdSThomas Cort (unsigned)sizeof(int) * count_size);
4087b9673cdSThomas Cort }
4097b9673cdSThomas Cort (void)memset(count, 0, sizeof(int) * l->l_max_col + 1);
4107b9673cdSThomas Cort for (i = nchars, c = l->l_line; --i >= 0; c++)
4117b9673cdSThomas Cort count[c->c_column]++;
4127b9673cdSThomas Cort
4137b9673cdSThomas Cort /*
4147b9673cdSThomas Cort * calculate running total (shifted down by 1) to use as
4157b9673cdSThomas Cort * indices into new line.
4167b9673cdSThomas Cort */
4177b9673cdSThomas Cort for (tot = 0, i = 0; i <= l->l_max_col; i++) {
4187b9673cdSThomas Cort save = count[i];
4197b9673cdSThomas Cort count[i] = tot;
4207b9673cdSThomas Cort tot += save;
4217b9673cdSThomas Cort }
4227b9673cdSThomas Cort
4237b9673cdSThomas Cort for (i = nchars, c = l->l_line; --i >= 0; c++)
4247b9673cdSThomas Cort sorted[count[c->c_column]++] = *c;
4257b9673cdSThomas Cort c = sorted;
4267b9673cdSThomas Cort } else
4277b9673cdSThomas Cort c = l->l_line;
4287b9673cdSThomas Cort while (nchars > 0) {
4297b9673cdSThomas Cort this_col = c->c_column;
4307b9673cdSThomas Cort endc = c;
4317b9673cdSThomas Cort do {
4327b9673cdSThomas Cort ++endc;
4337b9673cdSThomas Cort } while (--nchars > 0 && this_col == endc->c_column);
4347b9673cdSThomas Cort
4357b9673cdSThomas Cort /* if -b only print last character */
4367b9673cdSThomas Cort if (no_backspaces)
4377b9673cdSThomas Cort c = endc - 1;
4387b9673cdSThomas Cort
4397b9673cdSThomas Cort if (this_col > last_col) {
4407b9673cdSThomas Cort int nspace = this_col - last_col;
4417b9673cdSThomas Cort
4427b9673cdSThomas Cort if (compress_spaces && nspace > 1) {
4437b9673cdSThomas Cort int ntabs;
4447b9673cdSThomas Cort
4457b9673cdSThomas Cort ntabs = ((last_col % 8) + nspace) / 8;
4467b9673cdSThomas Cort if (ntabs) {
4477b9673cdSThomas Cort nspace -= (ntabs * 8) - (last_col % 8);
4487b9673cdSThomas Cort while (--ntabs >= 0)
4497b9673cdSThomas Cort PUTC('\t');
4507b9673cdSThomas Cort }
4517b9673cdSThomas Cort }
4527b9673cdSThomas Cort while (--nspace >= 0)
4537b9673cdSThomas Cort PUTC(' ');
4547b9673cdSThomas Cort last_col = this_col;
4557b9673cdSThomas Cort }
4567b9673cdSThomas Cort last_col++;
4577b9673cdSThomas Cort
4587b9673cdSThomas Cort for (;;) {
4597b9673cdSThomas Cort if (c->c_set != last_set) {
4607b9673cdSThomas Cort switch (c->c_set) {
4617b9673cdSThomas Cort case CS_NORMAL:
4627b9673cdSThomas Cort PUTC('\017');
4637b9673cdSThomas Cort break;
4647b9673cdSThomas Cort case CS_ALTERNATE:
4657b9673cdSThomas Cort PUTC('\016');
4667b9673cdSThomas Cort }
4677b9673cdSThomas Cort last_set = c->c_set;
4687b9673cdSThomas Cort }
4697b9673cdSThomas Cort PUTC(c->c_char);
4707b9673cdSThomas Cort if (++c >= endc)
4717b9673cdSThomas Cort break;
4727b9673cdSThomas Cort PUTC('\b');
4737b9673cdSThomas Cort }
4747b9673cdSThomas Cort }
4757b9673cdSThomas Cort }
4767b9673cdSThomas Cort
4777b9673cdSThomas Cort #define NALLOC 64
4787b9673cdSThomas Cort
4797b9673cdSThomas Cort static LINE *line_freelist;
4807b9673cdSThomas Cort
4817b9673cdSThomas Cort static LINE *
alloc_line(void)4827b9673cdSThomas Cort alloc_line(void)
4837b9673cdSThomas Cort {
4847b9673cdSThomas Cort LINE *l;
4857b9673cdSThomas Cort int i;
4867b9673cdSThomas Cort
4877b9673cdSThomas Cort if (!line_freelist) {
4887b9673cdSThomas Cort l = (LINE *)xmalloc(NULL, sizeof(LINE) * NALLOC);
4897b9673cdSThomas Cort line_freelist = l;
4907b9673cdSThomas Cort for (i = 1; i < NALLOC; i++, l++)
4917b9673cdSThomas Cort l->l_next = l + 1;
4927b9673cdSThomas Cort l->l_next = NULL;
4937b9673cdSThomas Cort }
4947b9673cdSThomas Cort l = line_freelist;
4957b9673cdSThomas Cort line_freelist = l->l_next;
4967b9673cdSThomas Cort
4977b9673cdSThomas Cort (void)memset(l, 0, sizeof(LINE));
4987b9673cdSThomas Cort return (l);
4997b9673cdSThomas Cort }
5007b9673cdSThomas Cort
5017b9673cdSThomas Cort static void
free_line(LINE * l)5027b9673cdSThomas Cort free_line(LINE *l)
5037b9673cdSThomas Cort {
5047b9673cdSThomas Cort
5057b9673cdSThomas Cort l->l_next = line_freelist;
5067b9673cdSThomas Cort line_freelist = l;
5077b9673cdSThomas Cort }
5087b9673cdSThomas Cort
5097b9673cdSThomas Cort static void *
xmalloc(void * p,size_t size)5107b9673cdSThomas Cort xmalloc(void *p, size_t size)
5117b9673cdSThomas Cort {
5127b9673cdSThomas Cort void *q;
5137b9673cdSThomas Cort
5147b9673cdSThomas Cort if (!(q = (void *)realloc(p, size)))
5157b9673cdSThomas Cort err(EXIT_FAILURE, "realloc");
5167b9673cdSThomas Cort p = q;
5177b9673cdSThomas Cort return (p);
5187b9673cdSThomas Cort }
5197b9673cdSThomas Cort
5207b9673cdSThomas Cort static void
usage(void)5217b9673cdSThomas Cort usage(void)
5227b9673cdSThomas Cort {
5237b9673cdSThomas Cort
5247b9673cdSThomas Cort (void)fprintf(stderr, "usage: col [-bfpx] [-l nline]\n");
5257b9673cdSThomas Cort exit(EXIT_FAILURE);
5267b9673cdSThomas Cort }
5277b9673cdSThomas Cort
5287b9673cdSThomas Cort static void
wrerr(void)5297b9673cdSThomas Cort wrerr(void)
5307b9673cdSThomas Cort {
5317b9673cdSThomas Cort
5327b9673cdSThomas Cort (void)fprintf(stderr, "col: write error.\n");
5337b9673cdSThomas Cort exit(EXIT_FAILURE);
5347b9673cdSThomas Cort }
5357b9673cdSThomas Cort
5367b9673cdSThomas Cort static void
dowarn(int line)5377b9673cdSThomas Cort dowarn(int line)
5387b9673cdSThomas Cort {
5397b9673cdSThomas Cort
5407b9673cdSThomas Cort warnx("warning: can't back up %s",
5417b9673cdSThomas Cort line < 0 ? "past first line" : "-- line already flushed");
5427b9673cdSThomas Cort }
543