delorie.com/archives/browse.cgi   search  
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 -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019