/*
* $Id: output.c,v 1.5 2002/08/23 13:38:14 howardjp Exp $
*
* Copyright (c) 1990
* Jan Wolter. 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Jan Wolter
* and his contributors.
* 4. Neither the name of Jan Wolter nor the names of his contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JAN WOLTER 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 JAN WOLTER 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.
*/
/* PARTY PROGRAM -- OUTPUT FUNCTIONS - from file to screen - Jan Wolter */
/* This simulates a series of output filters in a pipeline. Each of the
* "internal filters" reads from one buffer and writes to another. The
* idea is to make it possible to write internal filter procedures like
* expand(), eatline() and wrap() almost as if they were separate external
* filter programs. This makes it a lot simpler to add internal filter
* procedures and simplifies the switching on and off of different
* combinations of them.
*/
#include "party.h"
#include "opt.h"
#include <errno.h>
#include <sys/ioctl.h>
char *bgetl(), *linecpy(), *lineindex();
int linecmp(),linelen();
extern int errno;
/* This is a list of the internal filters numbered from 1 in the order in
* which they will be applied if they are all turned on.
*/
#define BUF_FILE 0 /* Party file reader -- must be first */
#define BUF_EATLINE 1 /* Line eater */
#define BUF_EXPAND 2 /* Control character expander */
#define BUF_WRAP 3 /* Line wrapper */
#define MAXFIL 4
/* This is an array of output buffers that sit between internal filters.
* Output buffer zero will always be used. The rest may or may not be.
*/
struct outbuf {
char *bf; /* Pointer to buffer */
int n; /* Number of characters written to buffer */
int i; /* Number of characters read from buffer */
} ob[MAXFIL];
#define OUTBUFSZ 1280
/* OUTPUT: reads from the end of the party file. If it finds anything there,
* it prints it out through the filter chain. It returns 1 if we are
* at end of file.
*/
output()
{
int n;
/* Read enough to fill the rest of the buffer */
if ((n= read(rst, ob[BUF_FILE].bf + ob[BUF_FILE].n,
OUTBUFSZ - ob[BUF_FILE].n)) > 0)
{
ob[BUF_FILE].n += n;
/* Write the Buffer to the next filter in the chain */
bufwrite(BUF_FILE);
return(0);
}
else
return(1);
}
/* INITOUTPUT: should be called once by the main program during the
* initialization stage. It allocates memory for the first buffer in the
* chain, and sets the rest too NULL. These others will be allocated as
* needed.
*/
initoutput()
{
int i;
ob[BUF_FILE].bf= malloc((mtype)OUTBUFSZ+1);
ob[BUF_FILE].bf[OUTBUFSZ]= '\0';
ob[BUF_FILE].n= 0;
for (i=1; i< MAXFIL; i++)
ob[i].bf= NULL;
}
/* BUFWRITE: passes the contents of the buffer "in" to the next filter in the
* chain, or prints it out if it hits the end.
*/
bufwrite(in)
int in;
{
ob[in].i= 0;
switch (in+1) /* Cases must be coded in sequential order! */
{
case BUF_EATLINE:
if (!opt[OPT_RAW].yes ||
!opt[OPT_SHOWNOISE].yes ||
ignoring != NULL ||
!opt[OPT_SHOWREAD].yes ||
!opt[OPT_SHOWEVENT].yes ||
!opt[OPT_REPEAT].yes)
{
bufopen(BUF_EATLINE);
eatline(in);
bufflush(BUF_EATLINE);
break;
}
case BUF_EXPAND:
if (opt[OPT_BS].yes != 1 || opt[OPT_CONTROL].yes != 1)
{
bufopen(BUF_EXPAND);
expand(in);
bufflush(BUF_EXPAND);
break;
}
case BUF_WRAP:
if (opt[OPT_WRAP].yes)
{
bufopen(BUF_WRAP);
wrap(in);
bufflush(BUF_WRAP);
break;
}
default:
if (write(out_fd, ob[in].bf, ob[in].n) < 0 &&
errno == EPIPE && out_fd != 1)
{
/* Redo write if the filter died */
err("Filter Exited\n");
stop_filter();
write(out_fd, ob[in].bf, ob[in].n);
}
ob[in].n= 0;
}
}
/* BPUTC: Does the equivalent of a putchar() to the named buffer, passing
* it to the next filter if it gets full.
*/
bputc(out,ch)
int out;
char ch;
{
ob[out].bf[ob[out].n++]= ch;
if (ob[out].n == OUTBUFSZ)
{
bufwrite(out);
}
}
/* BPUTN: Writes n characters from str to the named buffer, passing it to
* the next filter if it gets full.
*/
bputn(out,str,n)
int out;
char *str;
int n;
{
int space= OUTBUFSZ - ob[out].n;
while (n >= space)
{
strncpy(ob[out].bf + ob[out].n, str, space);
ob[out].n= OUTBUFSZ;
bufwrite(out);
str += space;
n -= space;
space= OUTBUFSZ - ob[out].n;
}
if (n > 0)
{
strncpy(ob[out].bf + ob[out].n, str, n);
ob[out].n += n;
}
}
/* BUFOPEN: makes sure the named buffer is ready to write to.
*/
bufopen(out)
int out;
{
if (ob[out].bf == NULL)
{
ob[out].bf= malloc((mtype)OUTBUFSZ+1);
ob[out].bf[OUTBUFSZ]= '\0';
ob[out].n= 0;
}
}
/* BUFFLUSH: flushes out anything that may be in the named buffer.
*/
bufflush(out)
int out;
{
if (ob[out].n > 0)
bufwrite(out);
}
/* BGETC: Return the next character in the named buffer. Return EOF if there
* isn't anything left in it.
*/
int bgetc(in)
int in;
{
if (ob[in].i == ob[in].n)
{
ob[in].n= 0;
return(EOF);
}
return((unsigned char)ob[in].bf[ob[in].i++]);
}
/* BGETS: Return the pointer to the begining of a line terminated by a newline,
* or to a full buffer containing no newlines. If there isn't a newline
* in the buffer, and the buffer is not full, return NULL, making space in
* the buffer so we can read in the rest of the line later. The net effect
* of this is kind of like gets().
*/
char *bgets(in)
int in;
{
register int j;
int k;
/* Check if input pointer is already at end */
if (ob[in].i == ob[in].n)
{
ob[in].n= 0;
return(NULL);
}
/* Scan for end of line */
for (j= ob[in].i; j < ob[in].n; j++)
{
if (ob[in].bf[j] == '\n')
{
k= ob[in].i;
ob[in].i= j+1;
return(ob[in].bf + k);
}
}
if (ob[in].i > 0)
{
/* Move the useless remnants to the begining */
strncpy(ob[in].bf,
ob[in].bf + ob[in].i,
(k= ob[in].n - ob[in].i));
ob[in].n= k;
ob[in].i= 0;
return(NULL);
}
if (ob[in].n == OUTBUFSZ)
{
ob[in].i= OUTBUFSZ;
return(ob[in].bf);
}
else
return(NULL);
}
/* EXPAND: Expand or eat control characters.
*/
expand(in)
int in;
{
int out= BUF_EXPAND;
int ch;
int wasmeta;
while((ch= bgetc(in)) != EOF)
{
if (ch == '\b')
{
if (opt[OPT_BS].yes==1)
bputc(out,ch);
else if (opt[OPT_BS].yes==2)
{
bputc(out,'^');
bputc(out,'H');
}
continue;
}
if (!isascii(ch))
{
if (opt[OPT_CONTROL].yes==0)
continue;
if (opt[OPT_CONTROL].yes==1)
bputc(out,ch);
else
{
bputc(out,'M');
bputc(out,'-');
ch= toascii(ch);
}
wasmeta= 1;
}
else
wasmeta= 0;
if (iscntrl(ch) && (wasmeta || (ch != '\t' && ch != '\n')))
{
if (opt[OPT_CONTROL].yes==1)
bputc(out,ch);
else if (opt[OPT_CONTROL].yes==2)
{
bputc(out, '^');
bputc(out, (ch==0177) ? '?' : (ch + '@') );
}
}
else
bputc(out,ch);
}
}
/* WRAP: Do line wrapping */
wrap(in)
int in;
{
int out= BUF_WRAP;
int cols= convert(opt[OPT_COLS].str);
int wrapindent= convert(opt[OPT_WRAP].str);
char *c; /* Pointer to current character */
static char *line; /* Pointer to beginning of current line */
static char *space; /* Pointer to last space character scanned */
int col; /* Number of columns in current line */
int ind; /* Number of unwrappable chars at head of line */
int n; /* Number of characters read from current line */
int i;
if (cols < 12) cols= 12;
while((c= bgets(in)) != NULL)
{
n= col= 0;
ind= wrapindent;
line= space= c;
for (; *c != '\n' && n < OUTBUFSZ; c++,n++)
{
if (isprint(*c))
{
col++;
if (*c == ' ')
space= c;
}
else if (*c == '\t')
{
col= col + 8 - (col % 8);
space= c;
}
else if (*c == '\b')
if (col > 0) col--;
if (col >= cols)
{
if (space - line > ind)
{
/* Break between words */
bputn(out,line,space-line);
c= space++;
line= space;
}
else
{
/* Break inside word */
bputn(out,line,c-line);
line= space= c--;
}
bputc(out,'\n');
for (i=0;i<wrapindent;i++)
bputc(out,' ');
col= wrapindent;
ind= 0;
}
}
bputn(out,line,c-line+1);
}
}
/* EATLINE: Strip out lines the user doesn't want to see */
eatline(in)
int in;
{
int out= BUF_EATLINE;
static char *line;
static char oldline[BFSZ+INDENT+3]= "\n";
char *colon;
while((line= bgets(in)) != NULL)
if ((opt[OPT_SHOWNOISE].yes || line[0] != '<') &&
(opt[OPT_SHOWREAD].yes || line[0] != ' ') &&
(opt[OPT_SHOWEVENT].yes || line[0] != '-') &&
(opt[OPT_REPEAT].yes || linecmp(line,oldline)) &&
(ignoring == NULL || !ignore_line(line)))
{
if (line[0] != '<' || opt[OPT_RAW].yes ||
(colon= lineindex(line+1,':')) == NULL)
/* Pass line through unchanged */
bputn(out,line,linelen(line));
else
{
/* Delete name tag from noise */
colon++;
bputc(out,'<');
bputn(out,colon,linelen(colon));
}
bputc(out,'\n');
if (!opt[OPT_REPEAT].yes) linecpy(oldline,line);
}
}
/* LINECMP, LINECPY, LINELEN, LINEINDEX - just like strcmp, strcpy, strlen, and
* index except lines are terminated by newlines instead of nulls.
*/
int linecmp(l1,l2)
register char *l1, *l2;
{
for (;;)
{
if (*l1 == '\n' || *l1 == '\0')
return(*l2 == *l1 ? 0 : -1);
else if (*l1 != *l2)
return((*l2=='\n' || *l2=='\0' || *l1 > *l2)? 1 : -1);
l1++;
l2++;
}
}
char *linecpy(to, from)
register char *to, *from;
{
register char *t1= to;
for (;;)
{
*to= *from;
if (*to == '\n' || *to == '\0') return(t1);
to++;
from++;
}
}
int linelen(l)
register char *l;
{
return(firstin(l,"\n")-l);
}
char *lineindex(ln,ch)
char *ln;
char ch;
{
for ( ; ; )
{
if (*ln == ch) return(ln);
if (*ln == '\n' || *ln == '\0') return(NULL);
ln++;
}
}
/* GETCOLS: This sets the number of columns from the termcap, if possible.
* If not it leaves it unchanged. If the windows variable is set, it asks
* the kernel instead. This is only used if we don't have the termcap
* library.
*/
#ifdef WINDOWS
setcols()
{
struct winsize x;
ioctl(2,TIOCGWINSZ,&x);
if (x.ws_col > 11)
setnum(OPT_COLS,x.ws_col);
}
#else
setcols()
{
char *term;
int cols;
char bf[1024];
if ((term= getenv("TERM")) == NULL || tgetent(bf,term) < 1)
return;
if ((cols= tgetnum("co")) != -1 && cols > 11)
setnum(OPT_COLS,cols);
}
#endif /*WINDOWS*/
syntax highlighted by Code2HTML, v. 0.9.1