Subversion Repositories planix.SVN

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/* col - eliminate reverse line feeds */
#include <u.h>
#include <libc.h>
#include <ctype.h>
#include <bio.h>

enum {
        ESC     = '\033',
        RLF     = '\013',

        PL      = 256,
        LINELN  = 800,

        Tabstop = 8,            /* must be power of 2 */
};

static int bflag, xflag, fflag;
static int cp, lp;
static int half;
static int ll, llh, mustwr;
static int pcp = 0;

static char *page[PL];
static char *line;
static char lbuff[LINELN];
static Biobuf bin, bout;

void    emit(char *s, int lineno);
void    incr(void), decr(void);
void    outc(Rune);

static void
usage(void)
{
        fprint(2, "usage: %s [-bfx]\n", argv0);
        exits("usage");
}

void
main(int argc, char **argv)
{
        int i, lno;
        long ch;
        Rune c;

        ARGBEGIN{
        case 'b':
                bflag++;
                break;
        case 'f':
                fflag++;
                break;
        case 'x':
                xflag++;
                break;
        default:
                usage();
        }ARGEND;

        for (ll=0; ll < PL; ll++)
                page[ll] = nil;

        cp = 0;
        ll = 0;
        mustwr = PL;
        line = lbuff;

        Binit(&bin, 0, OREAD);
        Binit(&bout, 1, OWRITE);
        while ((ch = Bgetrune(&bin)) != Beof) {
                c = ch;
                switch (c) {
                case '\n':
                        incr();
                        incr();
                        cp = 0;
                        break;

                case '\0':
                        break;

                case ESC:
                        c = Bgetrune(&bin);
                        switch (c) {
                        case '7':       /* reverse full line feed */
                                decr();
                                decr();
                                break;

                        case '8':       /* reverse half line feed */
                                if (fflag)
                                        decr();
                                else
                                        if (--half < -1) {
                                                decr();
                                                decr();
                                                half += 2;
                                        }
                                break;

                        case '9':       /* forward half line feed */
                                if (fflag)
                                        incr();
                                else
                                        if (++half > 0) {
                                                incr();
                                                incr();
                                                half -= 2;
                                        }
                                break;
                        }
                        break;

                case RLF:
                        decr();
                        decr();
                        break;

                case '\r':
                        cp = 0;
                        break;

                case '\t':
                        cp = (cp + Tabstop) & -Tabstop;
                        break;

                case '\b':
                        if (cp > 0)
                                cp--;
                        break;

                case ' ':
                        cp++;
                        break;

                default:
                        if (!isascii(c) || isprint(c)) {
                                outc(c);
                                cp++;
                        }
                        break;
                }
        }

        for (i=0; i < PL; i++) {
                lno = (mustwr+i) % PL;
                if (page[lno] != 0)
                        emit(page[lno], mustwr+i-PL);
        }
        emit(" ", (llh + 1) & -2);
        exits(0);
}

void
outc(Rune c)
{
        if (lp > cp) {
                line = lbuff;
                lp = 0;
        }

        while (lp < cp) {
                switch (*line) {
                case '\0':
                        *line = ' ';
                        lp++;
                        break;
                case '\b':
                        lp--;
                        break;
                default:
                        lp++;
                        break;
                }
                line++;
        }
        while (*line == '\b')
                line += 2;
        if (bflag || *line == '\0' || *line == ' ')
                cp += runetochar(line, &c) - 1;
        else {
                char c1, c2, c3;

                c1 = *++line;
                *line++ = '\b';
                c2 = *line;
                *line++ = c;
                while (c1) {
                        c3 = *line;
                        *line++ = c1;
                        c1 = c2;
                        c2 = c3;
                }
                lp = 0;
                line = lbuff;
        }
}

void
store(int lno)
{
        lno %= PL;
        if (page[lno] != nil)
                free(page[lno]);
        page[lno] = malloc((unsigned)strlen(lbuff) + 2);
        if (page[lno] == nil)
                sysfatal("out of memory");
        strcpy(page[lno], lbuff);
}

void
fetch(int lno)
{
        char *p;

        lno %= PL;
        p = lbuff;
        while (*p)
                *p++ = '\0';
        line = lbuff;
        lp = 0;
        if (page[lno])
                strcpy(line, page[lno]);
}

void
emit(char *s, int lineno)
{
        int ncp;
        char *p;
        static int cline = 0;

        if (*s) {
                while (cline < lineno - 1) {
                        Bputc(&bout, '\n');
                        pcp = 0;
                        cline += 2;
                }
                if (cline != lineno) {
                        Bputc(&bout, ESC);
                        Bputc(&bout, '9');
                        cline++;
                }
                if (pcp)
                        Bputc(&bout, '\r');
                pcp = 0;
                p = s;
                while (*p) {
                        ncp = pcp;
                        while (*p++ == ' ')
                                if ((++ncp & 7) == 0 && !xflag) {
                                        pcp = ncp;
                                        Bputc(&bout, '\t');
                                }
                        if (!*--p)
                                break;
                        while (pcp < ncp) {
                                Bputc(&bout, ' ');
                                pcp++;
                        }
                        Bputc(&bout, *p);
                        if (*p++ == '\b')
                                pcp--;
                        else
                                pcp++;
                }
        }
}

void
incr(void)
{
        int lno;

        store(ll++);
        if (ll > llh)
                llh = ll;
        lno = ll % PL;
        if (ll >= mustwr && page[lno]) {
                emit(page[lno], ll - PL);
                mustwr++;
                free(page[lno]);
                page[lno] = nil;
        }
        fetch(ll);
}

void
decr(void)
{
        if (ll > mustwr - PL) {
                store(ll--);
                fetch(ll);
        }
}