ohta@xxxxxxxxxxxxxxxxxxxxxxxxx $B$5$s(B:
(B> nakano@xxxxxxxxxxxxxxxx$B$5$s(B:
(B>>$B!!$G!"1Q8lHG$G$9$H$3$l$r$H$k(B col $B$H$$$&%3%^%s%I$,$"$k$s$G$9$,!"$3$l$O(B
(B>>$B;DG0$J$,$iF|K\8l$K$OBP1~$7$F$$$^$;$s!#!!1Q8lHG$N%^%K%e%"%k$J$i(B
(B>>
(B>> groff -Tascii -mandoc filename | col | lpr
(B>$B$3$l$J$s$G$9$,!"(BSolaris2.4$B$N(Bcol$B$OF|K\8lBP1~$7$F$$$k$N$G$9!#(B
(B>#$B$A$g$C$T$j2y$7$$!#(B
(B>$B0lEY$I$3$+$KF|K\8l%Q%C%A$,$J$$$+C5$7$?$N$G$9$,8+$D$+$j$^$;$s$G$7$?!#(B
(B>$B$=$&$$$($P(Bperl$B$G(Bcol$B$HF1$8F0:n$r$5$;$kJ}K!$,$"$C$?$h$&$J!D!#(B
(B
$B8E$$OCBj$G?=$7Lu$"$j$^$;$s!#(B
$B?92uBV$G$9!#(B
(B
$B%=!<%9$r8+$?$i$9$0$KD>$;$=$&$J$N$GD>$7$^$7$?!#E:IU$7$^$9!#(B
(B
$B$?$@!"(B-b $B0J30$N;H$$J}$G2?$,@5>o$J$N$+$,J,$+$i$J$$$N$G$"$^$j(B
$B<+?.$,$"$j$^$;$s!#(B
$B$h$m$7$1$l$P$*;H$$$/$@$5$$!#(B
/*-
(B * Copyright (c) 1990 The Regents of the University of California.
(B * All rights reserved.
(B *
(B * This code is derived from software contributed to Berkeley by
(B * Michael Rendell of the Memorial University of Newfoundland.
(B *
(B * Redistribution and use in source and binary forms, with or without
(B * modification, are permitted provided that the following conditions
(B * are met:
(B * 1. Redistributions of source code must retain the above copyright
(B * notice, this list of conditions and the following disclaimer.
(B * 2. Redistributions in binary form must reproduce the above copyright
(B * notice, this list of conditions and the following disclaimer in the
(B * documentation and/or other materials provided with the distribution.
(B * 3. All advertising materials mentioning features or use of this software
(B * must display the following acknowledgement:
(B * This product includes software developed by the University of
(B * California, Berkeley and its contributors.
(B * 4. Neither the name of the University nor the names of its contributors
(B * may be used to endorse or promote products derived from this software
(B * without specific prior written permission.
(B *
(B * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
(B * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
(B * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
(B * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
(B * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
(B * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
(B * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
(B * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
(B * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
(B * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
(B * SUCH DAMAGE.
(B *
(B * Wed Jun 22 22:15:41 1994, faith@xxxxxxxxxx: Added internationalization
(B * patches from Andries.Brouwer@xxxxxx
(B * Wed Sep 14 22:31:17 1994: patches from Carl Christofferson
(B * (cchris@xxxxxxxxxxxxx)
(B * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@xxxxxxxxxx>
(B * added Native Language Support
(B * 1999-09-19 Bruno Haible <haible@xxxxxxxxxxxxxx>
(B * modified to work correctly in multi-byte locales
(B * 2003-10-20 Kimihiro Morishita <morisita@xxxxxxxxxxxxxx>
(B * modified to work correctly in widecharacter string
(B *
(B */
(B
(B#include <stdlib.h>
(B#include <errno.h>
(B#include <ctype.h>
(B#include <string.h>
(B#include <stdio.h>
(B#include <unistd.h>
(B#include "nls.h"
(B
(B#include "widechar.h"
(B
(B#define BS '\b' /* backspace */
(B#define TAB '\t' /* tab */
(B#define SPACE ' ' /* space */
(B#define NL '\n' /* newline */
(B#define CR '\r' /* carriage return */
(B#define ESC '\033' /* escape */
(B#define SI '\017' /* shift in to normal character set */
(B#define SO '\016' /* shift out to alternate character set */
(B#define VT '\013' /* vertical tab (aka reverse line feed) */
(B#define RLF '\007' /* ESC-07 reverse line feed */
(B#define RHLF '\010' /* ESC-010 reverse half-line feed */
(B#define FHLF '\011' /* ESC-011 forward half-line feed */
(B
(B/* build up at least this many lines before flushing them out */
(B#define BUFFER_MARGIN 32
(B
(Btypedef char CSET;
(B
(Btypedef struct char_str {
(B#define CS_NORMAL 1
(B#define CS_ALTERNATE 2
(B short c_column; /* column character is in */
(B CSET c_set; /* character set (currently only 2) */
(B wchar_t c_char; /* character in question */
(B int c_width; /* character width */
(B} CHAR;
(B
(Btypedef struct line_str LINE;
(Bstruct line_str {
(B CHAR *l_line; /* characters on the line */
(B LINE *l_prev; /* previous line */
(B LINE *l_next; /* next line */
(B int l_lsize; /* allocated sizeof l_line */
(B int l_line_len; /* strlen(l_line) */
(B int l_needs_sort; /* set if chars went in out of order */
(B int l_max_col; /* max column in the line */
(B};
(B
(Bvoid usage(void);
(Bvoid wrerr(void);
(Bvoid warn(int);
(Bvoid free_line(LINE *l);
(Bvoid flush_line(LINE *l);
(Bvoid flush_lines(int);
(Bvoid flush_blanks(void);
(Bvoid *xmalloc(void *p, size_t size);
(BLINE *alloc_line(void);
(B
(BCSET last_set; /* char_set of last char printed */
(BLINE *lines;
(Bint compress_spaces; /* if doing space -> tab conversion */
(Bint fine; /* if `fine' resolution (half lines) */
(Bint max_bufd_lines; /* max # lines to keep in memory */
(Bint nblank_lines; /* # blanks after last flushed line */
(Bint no_backspaces; /* if not to output any backspaces */
(Bint pass_unknown_seqs; /* whether to pass unknown control sequences */
(B
(B#define PUTC(ch) \
(B if (putwchar(ch) == WEOF) \
(B wrerr();
(B
(Bint main(int argc, char **argv)
(B{
(B register wint_t ch;
(B register wint_t ch0;
(B CHAR *c;
(B CSET cur_set; /* current character set */
(B LINE *l; /* current line */
(B int extra_lines; /* # of lines above first line */
(B int cur_col; /* current column */
(B int cur_line; /* line number of current position */
(B int max_line; /* max value of cur_line */
(B int this_line; /* line l points to */
(B int nflushd_lines; /* number of lines that were flushed */
(B int adjust, opt, warned;
(B
(B setlocale(LC_ALL, "");
(B bindtextdomain(PACKAGE, LOCALEDIR);
(B textdomain(PACKAGE);
(B
(B max_bufd_lines = 128;
(B compress_spaces = 1; /* compress spaces into tabs */
(B pass_unknown_seqs = 0; /* remove unknown escape sequences */
(B while ((opt = getopt(argc, argv, "bfhl:px")) != -1)
(B switch (opt) {
(B case 'b': /* do not output backspaces */
(B no_backspaces = 1;
(B break;
(B case 'f': /* allow half forward line feeds */
(B fine = 1;
(B break;
(B case 'h': /* compress spaces into tabs */
(B compress_spaces = 1;
(B break;
(B case 'l': /* buffered line count */
(B if ((max_bufd_lines = atoi(optarg)) <= 0) {
(B (void)fprintf(stderr,
(B _("col: bad -l argument %s.\n"), optarg);
(B exit(1);
(B }
(B break;
(B case 'p':
(B pass_unknown_seqs = 1;
(B break;
(B case 'x': /* do not compress spaces into tabs */
(B compress_spaces = 0;
(B break;
(B case '?':
(B default:
(B usage();
(B }
(B
(B if (optind != argc)
(B usage();
(B
(B /* this value is in half lines */
(B max_bufd_lines *= 2;
(B
(B adjust = cur_col = extra_lines = warned = 0;
(B cur_line = max_line = nflushd_lines = this_line = 0;
(B cur_set = last_set = CS_NORMAL;
(B lines = l = alloc_line();
(B
(B ch0='\0';
(B while ((ch = getwchar()) != WEOF) {
(B if (!iswgraph(ch)) {
(B switch (ch) {
(B case BS: /* can't go back further */
(B if (cur_col == 0){ /* BS at head of line is ignored */
(B continue;
(B }
(B if(iswgraph(ch0)){
(B cur_col -= wcwidth(ch0);
(B }else{
(B --cur_col;
(B }
(B continue;
(B case CR:
(B cur_col = 0;
(B ch0 = '\0';
(B continue;
(B case ESC: /* just ignore EOF */
(B ch0 = ch;
(B ch = getwchar();
(B switch(ch) {
(B case RLF:
(B cur_line -= 2;
(B break;
(B case RHLF:
(B cur_line--;
(B break;
(B case FHLF:
(B cur_line++;
(B if (cur_line > max_line)
(B max_line = cur_line;
(B }
(B ch0 = ch;
(B continue;
(B case NL:
(B cur_line += 2;
(B if (cur_line > max_line)
(B max_line = cur_line;
(B cur_col = 0;
(B ch0 = '\0';
(B continue;
(B case SPACE:
(B ++cur_col;
(B ch0 = ch;
(B continue;
(B case SI:
(B cur_set = CS_NORMAL;
(B ch0 = ch;
(B continue;
(B case SO:
(B cur_set = CS_ALTERNATE;
(B ch0 = ch;
(B continue;
(B case TAB: /* adjust column */
(B cur_col |= 7;
(B ++cur_col;
(B ch0 = ch;
(B continue;
(B case VT:
(B cur_line -= 2;
(B ch0 = ch;
(B continue;
(B }
(B if (iswspace(ch)) {
(B if (wcwidth(ch) > 0)
(B cur_col += wcwidth(ch);
(B ch0 = ch;
(B continue;
(B }
(B if (!pass_unknown_seqs){
(B ch0 = ch;
(B continue;
(B }
(B }
(B
(B /* Must stuff ch in a line - are we at the right one? */
(B if (cur_line != this_line - adjust) {
(B LINE *lnew;
(B int nmove;
(B
(B adjust = 0;
(B nmove = cur_line - this_line;
(B if (!fine) {
(B /* round up to next line */
(B if (cur_line & 1) {
(B adjust = 1;
(B nmove++;
(B }
(B }
(B if (nmove < 0) {
(B for (; nmove < 0 && l->l_prev; nmove++)
(B l = l->l_prev;
(B if (nmove) {
(B if (nflushd_lines == 0) {
(B /*
(B * Allow backup past first
(B * line if nothing has been
(B * flushed yet.
(B */
(B for (; nmove < 0; nmove++) {
(B lnew = alloc_line();
(B l->l_prev = lnew;
(B lnew->l_next = l;
(B l = lines = lnew;
(B extra_lines++;
(B }
(B } else {
(B if (!warned++)
(B warn(cur_line);
(B cur_line -= nmove;
(B }
(B }
(B } else {
(B /* may need to allocate here */
(B for (; nmove > 0 && l->l_next; nmove--)
(B l = l->l_next;
(B for (; nmove > 0; nmove--) {
(B lnew = alloc_line();
(B lnew->l_prev = l;
(B l->l_next = lnew;
(B l = lnew;
(B }
(B }
(B this_line = cur_line + adjust;
(B nmove = this_line - nflushd_lines;
(B if (nmove >= max_bufd_lines + BUFFER_MARGIN) {
(B nflushd_lines += nmove - max_bufd_lines;
(B flush_lines(nmove - max_bufd_lines);
(B }
(B }
(B /* grow line's buffer? */
(B if (l->l_line_len + 1 >= l->l_lsize) {
(B int need;
(B
(B need = l->l_lsize ? l->l_lsize * 2 : 90;
(B l->l_line = (CHAR *)xmalloc((void *) l->l_line,
(B (unsigned) need * sizeof(CHAR));
(B l->l_lsize = need;
(B }
(B c = &l->l_line[l->l_line_len++];
(B c->c_char = ch;
(B c->c_set = cur_set;
(B c->c_column = cur_col;
(B c->c_width = wcwidth(ch);
(B /*
(B * If things are put in out of order, they will need sorting
(B * when it is flushed.
(B */
(B if (cur_col < l->l_max_col)
(B l->l_needs_sort = 1;
(B else
(B l->l_max_col = cur_col;
(B if (c->c_width > 0)
(B cur_col += c->c_width;
(B
(B ch0 = ch;
(B }
(B /* goto the last line that had a character on it */
(B for (; l->l_next; l = l->l_next)
(B this_line++;
(B flush_lines(this_line - nflushd_lines + extra_lines + 1);
(B
(B /* make sure we leave things in a sane state */
(B if (last_set != CS_NORMAL)
(B PUTC('\017');
(B
(B /* flush out the last few blank lines */
(B nblank_lines = max_line - this_line;
(B if (max_line & 1)
(B nblank_lines++;
(B else if (!nblank_lines)
(B /* missing a \n on the last line? */
(B nblank_lines = 2;
(B flush_blanks();
(B if (ferror(stdout) || fclose(stdout))
(B return 1;
(B return 0;
(B}
(B
(Bvoid flush_lines(int nflush)
(B{
(B LINE *l;
(B
(B while (--nflush >= 0) {
(B l = lines;
(B lines = l->l_next;
(B if (l->l_line) {
(B flush_blanks();
(B flush_line(l);
(B }
(B nblank_lines++;
(B if (l->l_line)
(B (void)free((void *)l->l_line);
(B free_line(l);
(B }
(B if (lines)
(B lines->l_prev = NULL;
(B}
(B
(B/*
(B * Print a number of newline/half newlines. If fine flag is set, nblank_lines
(B * is the number of half line feeds, otherwise it is the number of whole line
(B * feeds.
(B */
(Bvoid flush_blanks()
(B{
(B int half, i, nb;
(B
(B half = 0;
(B nb = nblank_lines;
(B if (nb & 1) {
(B if (fine)
(B half = 1;
(B else
(B nb++;
(B }
(B nb /= 2;
(B for (i = nb; --i >= 0;)
(B PUTC('\n');
(B if (half) {
(B PUTC('\033');
(B PUTC('9');
(B if (!nb)
(B PUTC('\r');
(B }
(B nblank_lines = 0;
(B}
(B
(B/*
(B * Write a line to stdout taking care of space to tab conversion (-h flag)
(B * and character set shifts.
(B */
(Bvoid flush_line(LINE *l)
(B{
(B CHAR *c, *endc;
(B int nchars, last_col, this_col;
(B
(B last_col = 0;
(B nchars = l->l_line_len;
(B
(B if (l->l_needs_sort) {
(B static CHAR *sorted;
(B static int count_size, *count, i, save, sorted_size, tot;
(B
(B /*
(B * Do an O(n) sort on l->l_line by column being careful to
(B * preserve the order of characters in the same column.
(B */
(B if (l->l_lsize > sorted_size) {
(B sorted_size = l->l_lsize;
(B sorted = (CHAR *)xmalloc((void *)sorted,
(B (unsigned)sizeof(CHAR) * sorted_size);
(B }
(B if (l->l_max_col >= count_size) {
(B count_size = l->l_max_col + 1;
(B count = (int *)xmalloc((void *)count,
(B (unsigned)sizeof(int) * count_size);
(B }
(B memset(count, 0, sizeof(int) * l->l_max_col + 1);
(B for (i = nchars, c = l->l_line; --i >= 0; c++)
(B count[c->c_column]++;
(B
(B /*
(B * calculate running total (shifted down by 1) to use as
(B * indices into new line.
(B */
(B for (tot = 0, i = 0; i <= l->l_max_col; i++) {
(B save = count[i];
(B count[i] = tot;
(B tot += save;
(B }
(B
(B for (i = nchars, c = l->l_line; --i >= 0; c++)
(B sorted[count[c->c_column]++] = *c;
(B c = sorted;
(B } else
(B c = l->l_line;
(B while (nchars > 0) {
(B this_col = c->c_column;
(B endc = c;
(B do {
(B ++endc;
(B } while (--nchars > 0 && this_col == endc->c_column);
(B
(B /* if -b only print last character */
(B if (no_backspaces) {
(B c = endc - 1;
(B if (nchars > 0 &&
(B this_col + c->c_width > endc->c_column)
(B continue;
(B }
(B
(B if (this_col > last_col) {
(B int nspace = this_col - last_col;
(B
(B if (compress_spaces && nspace > 1) {
(B int ntabs;
(B
(B ntabs = this_col / 8 - last_col / 8;
(B if (ntabs > 0) {
(B nspace = this_col & 7;
(B while (--ntabs >= 0)
(B PUTC('\t');
(B }
(B }
(B while (--nspace >= 0)
(B PUTC(' ');
(B last_col = this_col;
(B }
(B
(B for (;;) {
(B if (c->c_set != last_set) {
(B switch (c->c_set) {
(B case CS_NORMAL:
(B PUTC('\017');
(B break;
(B case CS_ALTERNATE:
(B PUTC('\016');
(B }
(B last_set = c->c_set;
(B }
(B PUTC(c->c_char);
(B if ((c+1) < endc) {
(B int i;
(B for (i=0; i < c->c_width; i++)
(B PUTC('\b');
(B }
(B if (++c >= endc)
(B break;
(B }
(B last_col += (c-1)->c_width;
(B }
(B}
(B
(B#define NALLOC 64
(B
(Bstatic LINE *line_freelist;
(B
(BLINE *
(Balloc_line()
(B{
(B LINE *l;
(B int i;
(B
(B if (!line_freelist) {
(B l = (LINE *)xmalloc((void *)NULL, sizeof(LINE) * NALLOC);
(B line_freelist = l;
(B for (i = 1; i < NALLOC; i++, l++)
(B l->l_next = l + 1;
(B l->l_next = NULL;
(B }
(B l = line_freelist;
(B line_freelist = l->l_next;
(B
(B memset(l, 0, sizeof(LINE));
(B return(l);
(B}
(B
(Bvoid free_line(LINE *l)
(B{
(B l->l_next = line_freelist;
(B line_freelist = l;
(B}
(B
(Bvoid *
(Bxmalloc(void *p, size_t size)
(B{
(B if (!(p = (void *)realloc(p, size))) {
(B (void)fprintf(stderr, "col: %s.\n", strerror(ENOMEM));
(B exit(1);
(B }
(B return(p);
(B}
(B
(Bvoid usage()
(B{
(B (void)fprintf(stderr, _("usage: col [-bfpx] [-l nline]\n"));
(B exit(1);
(B}
(B
(Bvoid wrerr()
(B{
(B (void)fprintf(stderr, _("col: write error.\n"));
(B exit(1);
(B}
(B
(Bvoid warn(int line)
(B{
(B (void)fprintf(stderr,
(B _("col: warning: can't back up %s.\n"), line < 0 ?
(B _("past first line") : _("-- line already flushed"));
(B}