NetBSD Problem Report #48092

From www@NetBSD.org  Sun Jul 28 13:39:50 2013
Return-Path: <www@NetBSD.org>
Received: from mail.netbsd.org (mail.netbsd.org [149.20.53.66])
	(using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))
	(Client CN "mail.NetBSD.org", Issuer "Postmaster NetBSD.org" (verified OK))
	by mollari.NetBSD.org (Postfix) with ESMTPS id CF007705DA
	for <gnats-bugs@gnats.NetBSD.org>; Sun, 28 Jul 2013 13:39:50 +0000 (UTC)
Message-Id: <20130728133948.90B4D70D0A@mollari.NetBSD.org>
Date: Sun, 28 Jul 2013 13:39:48 +0000 (UTC)
From: nathanialsloss@yahoo.com.au
Reply-To: nathanialsloss@yahoo.com.au
To: gnats-bugs@NetBSD.org
Subject: ansi2text a utility for formatting text with ANSI sequences for display with less.
X-Send-Pr-Version: www-1.0

>Number:         48092
>Category:       bin
>Synopsis:       ansi2text a utility for formatting text with ANSI sequences for display with less.
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    bin-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Sun Jul 28 13:40:00 +0000 2013
>Originator:     Nat Sloss
>Release:        NetBSD 5.0.1
>Organization:
>Environment:
NetBSD beast 5.0.1 NetBSD 5.0.1 (LOCKDEBUG) #4: Wed Jul 17 22:24:41 EST 2013  build@beast:/home/build/NetBSD-5.0.1_source_tree/usr/src/sys/arch/i386/compile/obj/LOCKDEBUG i386
>Description:
Consider the following:

tip myconnection | tee myconnection.log
Assume that my connection sends ANSI cursor and colour sequences.

Try to display myconnection.log with less or open with a text editor
less myconnection.log

You will find the display illegible ansi2text is my attempt at rectifying this.
>How-To-Repeat:
Refer to Description.
>Fix:
To use:

cat myconnection.log | ansi2text | less

ansi2text.c
/*-
 * Copyright (c) 2012, 2013 Nathanial Sloss <nathanialsloss@yahoo.com.au>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int lines = 25;
int cols = 80;

char *screen;
char *bgcolor;
char *fgcolor;
char *attributes;
int screensize;

char ansibuffer[256];
char *leftover;

int curpos, newpos, cury, newy, curx, newx;
int ansiattr, ansiattr_old;
int bg_color, fg_color, oldbg, oldfg;

int ansipos;
char ansicode;
int ansinum;
bool csistart;

bool colorflag;

void
writebuffer(void *buf, int count) {
	int fdout = fileno(stdout);

	write(fdout, buf, count);

	return;
}

void
writescreen(void) {
	int newbg, newfg, newattr, m, n, o, p;
	int colorcode_len;
	char colorcode[20];
	char nl = 0x0a;
	char buf[2048];
	int buffercount = 0;
	int buffersize = 2048;

	for (m = 0; m < screensize; m += cols) {
		for (n = 0; n < cols; n++) {
			o = n + m;

			if (colorflag && (attributes[o] != ansiattr_old)) {

				snprintf(colorcode, sizeof(colorcode),
				    "\033[m");

				colorcode_len = strlen(colorcode);
				for (p = 0; p < colorcode_len; p++) {
					if (buffercount >= buffersize) {
						writebuffer(buf, buffercount);
						buffercount = 0;
					}
					buf[buffercount] = colorcode[p];
					buffercount++;
				}
			}

			if (colorflag && (bgcolor[o] != oldbg ||
			    fgcolor[o] != oldfg || attributes[o]
			    != ansiattr_old || n == 0)) {
				if (attributes[o] == -1)
					newattr = 0;
				else
					newattr = attributes[o];

				if (bgcolor[o] == -1)
					newbg = 49;
				else
					newbg = bgcolor[o];

				if (fgcolor[o] == -1)
					newfg = 39;
				else
					newfg = fgcolor[o];

				snprintf(colorcode, sizeof(colorcode),
				    "\033[%d;%d;%dm", newfg, newbg, newattr);

				ansiattr_old = attributes[o];
				oldbg = bgcolor[o];
				oldfg = fgcolor[o];

				colorcode_len = strlen(colorcode);
				for (p = 0; p < colorcode_len; p++) {
					if (buffercount >= buffersize) {
						writebuffer(buf, buffercount);
						buffercount = 0;
					}
					buf[buffercount] = colorcode[p];
					buffercount++;
				}
			}

			if (buffercount >= buffersize) {
				writebuffer(buf, buffercount);
				buffercount = 0;
			}
			buf[buffercount] = screen[o];
			buffercount++;
		}
		if (buffercount >= buffersize) {
			writebuffer(buf, buffercount);
			buffercount = 0;
		}
		buf[buffercount] = nl;
		buffercount++;
	}
	writebuffer(buf, buffercount);

	return;
}
void parseansi(void) {
	unsigned int j, k, l;
	/* Strip first two characters esc[ */
	for (j = 0; j < sizeof(ansibuffer); j++) {
		if (j + 2 >= (sizeof(ansibuffer)))
			k = 0;
		else
			k = ansibuffer[j + 2];
		ansibuffer[j] = k;
	}
	ansinum = strtoimax(ansibuffer, &leftover, 10);
	strcpy(ansibuffer, leftover);

	if (ansicode == 'J') {
		writescreen();

		ansiattr = 0;
		bg_color = 49;
		fg_color = 39;

		if (ansinum == 2)
			l = 0;
		else
			l = (cury * cols) + curx - 1;
		for (j = l; j < (unsigned int)screensize; j++) {
			screen[j] = ' ';
			attributes[j] = ansiattr;
			bgcolor[j] = bg_color;
			fgcolor[j] = fg_color;
		}
	}
	if (ansicode == 'A') {
		writescreen();
		if (ansinum == 0)
			cury--;
		else if (ansinum > 0)
			cury -= ansinum;
		if (cury < 1)
			cury = 1;
	}
	if (ansicode == 'B') {
		if (ansinum == 0)
			cury++;
		else if (ansinum > 0)
			cury += ansinum;
		if (cury > lines) {
			writescreen();
			cury = lines;
		}
	}
	if (ansicode == 'C') {
		if (ansinum == 0)
			curx++;
		else if (ansinum > 0)
			curx += ansinum;
		if (curx > cols) {
			writescreen();
			curx = cols;
		}
	}
	if (ansicode == 'D') {
		writescreen();
		if (ansinum == 0)
			curx--;
		else if (ansinum > 0)
			curx -= ansinum;
		if (curx < 1)
			curx = 1;
	}
	if (ansicode == 'E') {
		if (ansinum == 0)
			cury++;
		if (ansinum > 0)
			cury += ansinum;
		curx = 1;
		if (cury > lines) {
			writescreen();
			cury = lines;
		}
	}
	if (ansicode == 'F') {
		writescreen();
		if (ansinum == 0)
			cury--;
		if (ansinum > 0)
			cury -= ansinum;
		curx = 1;
		if (cury < 1)
			cury = 1;
	}
	if (ansicode == 'G') {
		if (ansinum < curx)
			writescreen();
		if (ansinum > 0)
			curx = ansinum;
		if (curx > cols) {
			writescreen();
			curx = cols;
		}
	}
	if (ansicode == 'H' || ansicode == 'f') {
		if (ansinum < 1)
			newy = 1;
		else
			newy = ansinum;

		if (ansibuffer[0] == ';') {
			for (j = 0; j < sizeof(ansibuffer); j++) {
				if (j + 1 >= (sizeof(ansibuffer)))
					k = 0;
				else
					k = ansibuffer[j + 1];
				ansibuffer[j] = k;
			}
			newx = strtoimax(ansibuffer, &leftover, 10);
			if (newx < 1)
				newx = 1;
		}

		if (newx > cols)
			newx = cols;
		if (newy > lines)
			newy = lines;

		if ((newy < cury)) 
			writescreen();
		curx = newx;
		cury = newy;
	}
	if (ansicode == 'm') {
		if (ansinum >= 0 && ansinum < 30)
			ansiattr = ansinum;
		else if (ansinum >= 30 && ansinum <= 39)
			fg_color = ansinum;
		else if (ansinum >= 40 && ansinum < 49)
			bg_color = ansinum;

		while (ansibuffer[0] == ';') {
			for (j = 0; j < sizeof(ansibuffer); j++) {
				if (j + 1 >= (sizeof(ansibuffer)))
					k = 0;
				else
					k = ansibuffer[j + 1];
				ansibuffer[j] = k;
			}
			ansinum = strtoimax(ansibuffer, &leftover, 10);
			strcpy(ansibuffer, leftover);

			if (ansinum >= 0 && ansinum < 30)
				ansiattr = ansinum;
			else if (ansinum >= 30 && ansinum <= 39)
				fg_color = ansinum;
			else if (ansinum >= 40 && ansinum < 49)
				bg_color = ansinum;
		}
	}

	return;
}

int
main(int argc, char **argv) {
	int ch, nr, readpos;
	int fdin = fileno(stdin);
	char buffer[2048];

	cols = 80;
	lines = 25;

	curpos = newpos = 0;
	cury = newy = 1;
	curx = newx = 1;
	bg_color = fg_color = oldbg = oldfg = -1;
	ansiattr = ansiattr_old = -1;

	colorflag = false;			/* Defaults to b&w */

	while ((ch = getopt(argc, argv, "cl:w:")) != -1)
		switch (ch) {
		case 'c':
			colorflag = true;
			break;
		case 'l':
			lines = atoi(optarg);	/* lines per screen */
			break;
		case 'w':
			cols = atoi(optarg);	/* coloumns per screen */
			break;
		default:
		case '?':
			(void)fprintf(stderr,
			    "usage: ansi2text [-c -w [width] -l [lines]] [file]\n");
			exit(EXIT_FAILURE);
			/* NOTREACHED */
		}
	argv += optind;

	if (*argv) {
		if ((fdin = open(*argv, O_RDONLY)) < 0)
			err("Error opening file: %s", *argv);
	}

	screensize = cols * lines;
	if (screensize < 1) {
		fprintf(stderr, "Screensize must be at least 1 x 1.\n");

		return EXIT_FAILURE;
	}

	/* Allocate virtual screen */
	screen = malloc(screensize);
	attributes = malloc(screensize);
	bgcolor = malloc(screensize);
	fgcolor = malloc(screensize);

	/* Initialise virtual screen with spaces */
	memset(screen, ' ', screensize);
	memset(bgcolor, -1, screensize);
	memset(fgcolor, -1, screensize);
	memset(attributes, -1, screensize);

	while ((nr = read(fdin, &buffer, sizeof(buffer))) != -1 
	    && nr != 0) {
		for(readpos = 0; readpos < nr; readpos++) {

			if (buffer[readpos] == 0x1b) {
				csistart = true;
				memset(ansibuffer, 0, sizeof(ansibuffer));
				ansipos = 0;
				ansinum = 0;
				ansicode = 0;
			}
			if (csistart) {
				ansibuffer[ansipos] = buffer[readpos];

				if (ansibuffer[ansipos] >= '0' &&
				    ansipos > 1 && ansibuffer[1] != '[')
					csistart = false;

				if (ansibuffer[ansipos] >= 'A' &&
				    ansipos > 1 && ansibuffer[1] == '[') {
					ansicode = ansibuffer[ansipos];
				} else
					ansicode = 0;
				if (ansicode) {
					parseansi();
					csistart = false;
					ansicode = 0;
				}
				ansipos++;
			} else {
				if (buffer[readpos] == 0x0a) {
					cury++;
					curx = 1;
				}
				if (buffer[readpos] == 0x0d)
					curx = 1;
				if (buffer[readpos] == 0x08) {
					curx--;
					if (curx < 1)
						curx = 1;
				}

				if (curx > cols) {
					cury++;
					curx = 1;
				}
				if (cury > lines) {
					writescreen();
					cury = 1;
				}

				curpos = ((cury - 1) * cols) + curx - 1;
				if (curpos == screensize) {
					cury = curx = 1;
					curpos = 0;
				}

				if (buffer[readpos] >= ' ') {
					if ((fg_color != 39 || bg_color != 49) &&
					    ansiattr == 0)
						ansiattr = 22;

					attributes[curpos] = ansiattr;
					bgcolor[curpos] = bg_color;
					fgcolor[curpos] = fg_color;
					screen[curpos] = buffer[readpos];
					curx++;
				}
			}
		}
	}
	writescreen();

	close(fdin);

	/* free virtual screen */
	free(fgcolor);
	free(bgcolor);
	free(attributes);
	free(screen);

	return EXIT_SUCCESS;
}
ansi2text.1
.\"	$NetBSD: colorbars.6,v 1.3 2012/06/09 23:15:13 njoly Exp $
.\"
.\" Copyright (c) 2012, 2013 Nathanial Sloss <nathanialsloss@yahoo.com.au>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\"    notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\"    notice, this list of conditions and the following disclaimer in the
.\"    documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd July 28, 2013
.Dt ANSI2TEXT 1
.Os
.Sh NAME
.Nm ansi2text
.Nd formats ANSI text files or standard input for display
.Sh SYNOPSIS
.Nm
.Op Fl c
.Op Fl w Ar width
.Op Fl l Ar length
.Op Ar file
.Sh DESCRIPTION
The
.Nm
command formats ANSI text for display 
.Bl -tag -width Ds
.It Fl c
Display formatted text in color suitable for display with less -R
.It Fl w Ar width
Display formatted text in with a maximum
.Ar width
in coloumns.  Defaults to 80 coloumns.
.It Fl l Ar length
Display formatted text in with a maximum
.Ar length
(height) in rows.  Defaults to 25 rows.
.It Ar file
File to display, if not specified standard input is used.
.Sh SEE ALSO
.Xr less 1
.Sh HISTORY
.Nm
appeared in
.Nx 7.0 .
.Sh AUTHORS
Nathanial Sloss


I hope this is useful.

Regards,

Nat

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2007 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.