Mail Archives: djgpp/2001/02/20/14:26:36
From: | "Rafael García" <rafael AT geninfor DOT com>
|
Newsgroups: | comp.os.msdos.djgpp
|
Subject: | continuous file reading
|
Date: | Tue, 20 Feb 2001 20:12:30 +0100
|
Organization: | BT Tel. Netnews service (readers)
|
Lines: | 561
|
Message-ID: | <3a92c201@filemon.telecable.es>
|
NNTP-Posting-Host: | filemon.telecable.es
|
Mime-Version: | 1.0
|
X-Trace: | titan.bt.es 982696375 5567 212.89.0.4 (20 Feb 2001 19:12:55 GMT)
|
X-Complaints-To: | abuse AT bt DOT es
|
NNTP-Posting-Date: | 20 Feb 2001 19:12:55 GMT
|
X-Priority: | 3
|
X-MSMail-Priority: | Normal
|
X-Newsreader: | Microsoft Outlook Express 5.00.2615.200
|
X-MimeOLE: | Produced By Microsoft MimeOLE V5.00.2615.200
|
X-Original-NNTP-Posting-Host: | cm05118.telecable.es
|
To: | djgpp AT delorie DOT com
|
DJ-Gateway: | from newsgroup comp.os.msdos.djgpp
|
Reply-To: | djgpp AT delorie DOT com
|
I have programs that writes LOG files. I can try a process and then I can
revise that LOG to debug, p.e.
But I wrote this program to view the LOG _while_ in execution.
Now, my question is about the main loop, where I try to read all the time,
because I don't know if it is possible to use some kind of signal to read
only where file had grown.
Any suggestion?
Thanks
char description[]=
"View indented LOG file while it is being written.\n"
"You can select detail level based on indentation with +/- keys.\n"
"Press ENTER to pause/continue end tracking.\n"
"\n"
"Syntax: vilog [-fN] [-cN] [-dN] file\n"
"fN N=Number of fixed columns, not indented, pe. date/time (def=guess)\n"
"cN N=Number of columns by indentation level (def=2)\n"
"dN N=Initial detail level (def=4)\n"
"\n"
"Version 1.0 beta 1\n"
"By Rafael García González (rafael AT geninfor DOT com)\n"
"You can copy it freely\n";
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <assert.h>
#include <pc.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLIN 4096
#define TRUE 1
#define FALSE 0
#define BOOL int
#define MAXSHOWCOLS 256
#define MAINATTRIB ((BLUE <<4)|LIGHTGRAY) // main data colors // could
be parameters
#define CURSORATTRIB ((CYAN <<4)|WHITE) // cursor line colors
#define STLINEATTRIB ((LIGHTGRAY<<4)|BLUE) // status line colors
#define TABINDENTVAL 8 // nr of spaces for a TAB //
could be parameter
#define ESC 27
#define ENTER '\r'
#define LEFT (75 <<8)
#define RIGHT (77 <<8)
#define UP (72 <<8)
#define DOWN (80 <<8)
#define PGUP (73 <<8)
#define PGDN (81 <<8)
#define HOME (71 <<8)
#define END (79 <<8)
#define CTLHOME (119<<8)
#define CTLEND (117<<8)
#define CTLLEFT (115<<8)
#define CTLRIGHT (116<<8)
int mainloop(int fd);
ssize_t load(int fd, BOOL bigfirst);
int useraction(void);
void show();
void showline(char *line,int row,int attrib);
void showstatusline(int row);
int procbuf(char *buf,ssize_t tbuf);
int indentlevel(char *line);
void strncpyno0(char *dest, char *source, int max);
int getchext(void);
void countspaces(char *newline);
int defstaticzone(void);
char *filename=NULL;
char **text=NULL; /* this will hold the content of file in memory
to speed up detail level changes and scrolling */
long nlines=0; /* loaded */
long nlinesalloc=0; /* allocated */
int hposition=0; /* horizontal scroll */
int vposition=0; /* vertical scroll */
int detlevel=4; // detail show level
int detstep=2; // number of columns for each detail level
int logstaticzone=-1; /* number of columns without indentation
significance in LOG file at line start.
For example, I have this kind of file:
16/02/01 18:27:30.890 * ***********************************************
16/02/01 18:27:30.890 * Sesión 16/02/01 18:27 Gestión comercial V3.2
16/02/01 18:27:31.050 f faopen(Configuración)
16/02/01 18:27:39.900 o Menú principal: Albaranes
16/02/01 18:27:37.430 e memlibre: limiteinicial=-49152
16/02/01 18:27:37.480 e memlibre: limite=-49152
16/02/01 18:27:37.480 a [arriba]
16/02/01 18:27:37.700 f faclose(Configuración)
16/02/01 18:27:39.900 a a
16/02/01 18:27:39.950 f faopen(Terminales)
..............................................................
this is 26 static columns */
BOOL tracking=TRUE; /* if program show new lines logged "online" */
int screenrows,screencols;
int datarows;
int cspacc[MAXLIN]; // number of spaces in each column
/*****************************************************************/
int main(int argc,char **argv) {
int fd;
int ret;
int n;
for (n=1;n<argc;n++) {
char *parm=argv[n];
if (parm[0]=='/' || parm[0]=='-') { // switches
switch(parm[1]) {
case 'f':
logstaticzone=atoi(parm+2);
assert(logstaticzone>=0);
break;
case 'c':
detstep=atoi(parm+2);
assert(detstep>0);
break;
case 'd':
detlevel=atoi(parm+2);
assert(detlevel>=0);
break;
default:
fprintf(stderr,"Syntax error: %s\n\n",parm);
fprintf(stderr,description);
return 1;
}
}
else { // filename
if (filename) {
fprintf(stderr,"Sorry, can only open one file at a time\n");
fprintf(stderr,description);
return 1;
}
filename=parm;
}
}
if (!filename) {
fprintf(stderr,description);
return 1;
}
screencols=ScreenCols();
screenrows=ScreenRows();
assert(screencols!=0);
assert(screenrows!=0);
assert(screencols<=MAXSHOWCOLS);
datarows=screenrows-1; // 1 line reserved for status line
if ((fd=open(filename,O_RDONLY))<0) {
perror(filename);
return 2; }
ret=mainloop(fd);
close(fd);
clrscr();
return ret;
}
int mainloop(int fd) {
BOOL bigfirst=TRUE;
ssize_t newdata=0;
/* I don't know how to detect when file has changed so I try
to read all the time */
while ((newdata=load(fd,bigfirst))>=0) {
if (bigfirst) {
bigfirst=FALSE;
vposition=nlines-1; // begin showing end of file
if (logstaticzone<0) { // if not given by caller
logstaticzone=defstaticzone(); // guess it
if (logstaticzone)
printf("\nFixed zone could be %d\n",logstaticzone);
}
}
if (newdata) { // I don't want to be writing to screen
continuosly if file don't change
if (tracking) vposition=nlines-1;
show();
}
if (useraction()) break; // let the user move it
if (!kbhit()) { /* give some rest to the system */
#ifdef DELAY
delay(50); // y have found sometimes this to work better than
pause()
#else
pause();
#endif
}
}
return 0;
}
// read data available from logfile
// returns number of bytes read, 0 if nothing done, or <0 on error
ssize_t load(int fd, BOOL bigfirst) {
ssize_t bytesread;
ssize_t totalread=0;
char buf[8192];
static off_t filepos=0;
// read all data available now
while ((bytesread=read(fd,buf,sizeof(buf)))>0) {
filepos+=bytesread;
totalread+=bytesread;
if (bigfirst) { // show the user that program is working (file
could be large)
printf("Reading %s %ld...\r",filename,(long)filepos);
if (kbhit() && getch()==ESC) return -2;
}
procbuf(buf,bytesread);
}
if (bytesread<0) {
perror(filename);
return -1;
}
return totalread;
}
// let user change variables
// returns !0 to terminate program
int useraction(void) {
if (kbhit()) {
int c=getchext();
switch (c) {
case '+':
detlevel++;
show();
break;
case '-':
if (detlevel>0) {
detlevel--;
show();
}
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
detlevel=c-'0';
show();
break;
case LEFT:
if (hposition>0) {
hposition--;
show();
}
break;
case RIGHT:
hposition++;
show();
break;
case UP:
// go to previous visible line
if (vposition>0) {
for (vposition--;
vposition>0 && indentlevel(text[vposition])>detlevel ;
vposition-- );
}
tracking=FALSE; // and stop following tail of file
show();
break;
case DOWN:
// go to next visible line
if (vposition<nlines-1) {
for (vposition++;
vposition<nlines-1 &&
indentlevel(text[vposition])>detlevel ;
vposition++ );
}
tracking=FALSE;
show();
break;
case PGUP:
if (vposition>0) {
int skipvisiblelines;
for (skipvisiblelines=0,vposition--; vposition>0;
vposition--) {
if (indentlevel(text[vposition])<=detlevel) {
skipvisiblelines++;
if (skipvisiblelines==datarows) break;
}
}
tracking=FALSE;
show();
}
break;
case PGDN:
if (vposition<nlines-1) {
int skipvisiblelines;
for (skipvisiblelines=0,vposition++; vposition<nlines-1;
vposition++) {
if (indentlevel(text[vposition])<=detlevel) {
skipvisiblelines++;
if (skipvisiblelines==datarows) break;
}
}
tracking=FALSE;
show();
}
break;
case HOME:
hposition=0;
show();
break;
case END:
hposition=strlen(text[vposition])-screencols;
hposition+=screencols/6;
if (hposition<0) hposition=0;
show();
break;
case CTLHOME:
vposition=0;
show();
break;
case CTLEND:
vposition=nlines-1;
show();
break;
case CTLLEFT:
hposition-=screencols/4;
if (hposition<0) hposition=0;
show();
break;
case CTLRIGHT:
hposition+=screencols/4;
show();
break;
case ENTER: // start/stop tracking
if (tracking)
tracking=FALSE;
else { // goto "now" (growing tail of
logfile)
vposition=nlines-1;
tracking=TRUE;
}
show();
break;
case ESC:
return 1;
break;
default: break;
}
}
return 0;
}
/* I have read a new chunk of data and have to load it in my ram image of
the file, line by line */
int procbuf(char *filebuf,ssize_t bufsize) {
char *p;
ssize_t n;
static char *linebuf=NULL;
static char *plinebuf;
static int ncharsline;
if (linebuf==NULL) {
linebuf=(char*)malloc(MAXLIN); // this will hold chars of one line
assert(linebuf);
plinebuf=linebuf;
ncharsline=0;
}
if (!text) {
nlinesalloc=1000;
text=(char**)malloc(nlinesalloc*sizeof(char*));
assert(text);
}
for (p=filebuf,n=0; n<bufsize; p++,n++) {
if (*p=='\n') { // now I have got a complete line
newline:
*plinebuf=0;
countspaces(linebuf);
text[nlines]=strdup(linebuf);
if (!text[nlines]) {
fprintf(stderr,"Sorry, not enough memory (line
%ld)\n",(long)nlines);
exit(4);
}
plinebuf=linebuf;
ncharsline=0;
nlines++;
if (nlines>=nlinesalloc) {
nlinesalloc*=2;
if
((text=(char**)realloc(text,nlinesalloc*sizeof(char*)))==NULL) {
fprintf(stderr,"Sorry, not enough memory (line
%ld)\n",(long)nlines);
exit(4);
}
}
}
else { // add a char to the line buffer
*plinebuf++=*p;
if (++ncharsline>=MAXLIN) { // I hope this never occurs
fprintf(stderr,"Line too long (%ld)\n",(long)nlines);
goto newline; // ...but if it happens, I don't want program
to stop
}
}
}
return 0;
}
void show() {
int n,linesfound,row;
assert(vposition<nlines);
// search first line to show, giving a margin of 1/4 at bottom
for (n=vposition,linesfound=0; n>0 && linesfound<datarows/4*3; n--) {
if (indentlevel(text[n])<=detlevel) {
linesfound++;
}
}
// now I know where to start printing
for (row=0;n<nlines && row<datarows; n++) {
if (indentlevel(text[n])<=detlevel) {
showline(text[n],row, n==vposition?CURSORATTRIB:MAINATTRIB);
row++;
}
}
while (row<datarows) { // clear to end of screen
showline("",row,MAINATTRIB);
row++;
}
showstatusline(datarows);
}
/* show part of a line visible in available width */
void showline(char *line,int row,int attrib) {
static char buf[MAXSHOWCOLS+1];
memset(buf,' ',screencols); // to let empty to right margin
if (strlen(line)>hposition)
strncpyno0(buf,line+hposition,screencols);
ScreenPutString(buf, attrib, 0, row);
}
/* copy string with length limit and not copying terminator */
void strncpyno0(char *dest, char *source, int max) {
while (max--) {
if (!*source) break;
*dest++=*source++;
}
}
// get number of indentation levels of a line
int indentlevel(char *line) {
int spaces=9999; // this makes a line not significant
if (strlen(line)>logstaticzone) {
char *p;
for (spaces=0,p=line+logstaticzone;*p;p++) {
if (*p==' ') spaces++;
else if (*p=='\t') spaces+=TABINDENTVAL;
else break;
}
}
return spaces/detstep;
}
void showstatusline(int row) {
static char buf[500];
sprintf(buf," %s ¦ pos %ld / %ld ¦ detlevel=%d ¦ tracking=%s",
filename,1+vposition,nlines,detlevel,
tracking?"YES":"NO");
showline(buf,row,STLINEATTRIB);
}
/* get key pressed code, shifting extended codes 8 bits left */
int getchext(void) {
int c=getch();
if (c) return c;
else return getch()<<8;
}
/* Count spaces in each column to assign logstaticzone automatically */
void countspaces(char *newline) {
char *p;
int coln;
for (coln=0,p=newline; *p && coln<MAXLIN; p++,coln++) {
if (*p==' ') cspacc[coln]++;
else if (*p=='\t') {
int n;
for (n=0;n<TABINDENTVAL;n++) {
cspacc[coln]++;
coln++;
}
coln--; // discount char '\t' position incremented by loop too
}
}
}
/* get apparent left fixed zone */
int defstaticzone(void) {
int n;
if (nlines<10) return 0;
for (n=0; n<MAXLIN; n++) {
if (cspacc[n]==0) continue; // this column was never space, it
can be fixed zone
if (cspacc[n]==nlines) continue; // this column was always space, it
can be fixed zone
break; // this column was sometimes space,
so it is not fixed zone
}
if (n==MAXLIN) return 0;
return n;
}
- Raw text -