// filebuf.c RHS 9/1/90 file-buffering and handling routines #ifdef WINDOWS #include #include"wsmooth.h" #include"wsmooth2.h" #endif #include #include #include #include #include #include #include #include #include #include"filebuf.h" #define TRUE 1 #define FALSE 0 #define MAXSCREEN 25 #define CTRL_Z 0x1A #define CR '\r' #define LF '\n' #define MYEOF 0xffff #define PAGEINDEXSIZE 50 #define HALFBUFFER (30*1024) #define MAXFILESIZE ((long)PAGEINDEXSIZE*(long)HALFBUFFER) #define BUFFERSIZE (HALFBUFFER*2) #if defined(WINDOWS) HANDLE indexbufhdl = NULL, bufferhdl = NULL; extern HWND WinSmooth; #endif unsigned far *lineptr; // pointer to line index in index segment unsigned far *pageptr; // pointer to page index in index segment unsigned far *indexstart = NULL; // pointer to start of line index unsigned far *indexbuf = NULL; // pointer to index buffer char far *fileptr = NULL; // pointer to curr line char far *bufstart = NULL; // pointer to start of filebuffer char far *bufend; // point to end of buffer int fh = -1; int stripbits = FALSE; void filebuf_updateindex(int endndx); void filebuf_read(void); void filebuf_nextbuffer(void); void filebuf_prevbuffer(void); void filebuf_reset(void); void filebuf_stripbits(char far *buf, unsigned len); void error_exit(int err, char *msg); BOOL filebuf_fileisopen(void) { if(fh != -1) return TRUE; return FALSE; } // toggles bit stripping flag void filebuf_strip(int strip) { stripbits = strip; } // strips high bit from bytes read void filebuf_stripbits(char far *buf, unsigned len) { if(!stripbits) return; for( ; len; len--) buf[len-1] &= 0x7f; } // initializes file buffers int filebuf_init(void) { if(indexbufhdl && bufferhdl) { filebuf_reset(); return TRUE; } // allocate index buffer and file buffer #ifdef WINDOWS { unsigned u = BUFFERSIZE+1; // use an unsigned to get over compiler bug // allocate index and file buffers if(indexbufhdl = GlobalAlloc(GMEM_MOVEABLE,u)) indexbuf = (unsigned far *)GlobalWire(indexbufhdl); if(bufferhdl = GlobalAlloc(GMEM_MOVEABLE,u)) bufstart = (char far *)GlobalWire(bufferhdl); if(!indexbufhdl || !bufferhdl) { Message(WinSmooth,"Unable to allocate memory buffers: hdls=%u,%u ptrs=%lp,%lp size=%ld", indexbufhdl,bufferhdl,indexbuf,bufstart,GlobalSize(indexbufhdl)); return FALSE; } } if(!indexbuf || !bufstart) Message(WinSmooth,"Unable to re-locate memory buffers: ptrs=%lp,%lp", indexbuf,bufstart); #else indexbuf = (unsigned far *)_fmalloc(BUFFERSIZE+1); bufstart = (char far *)_fmalloc(BUFFERSIZE+1); if(!indexbuf || !bufstart) error_exit(0,"No buffer allocation"); #endif // set line pointer to beginning of line index indexstart = lineptr = &indexbuf[PAGEINDEXSIZE]; // set page pointer to beginning of page index pageptr = indexbuf; // initialize indexes _fmemset(indexbuf,0x0000,BUFFERSIZE); // set last word of line index to MYEOF indexbuf[BUFFERSIZE/(sizeof(unsigned))] = MYEOF; return TRUE; } // resets variables for new file without re-allocating buffers void filebuf_reset(void) { lineptr = indexstart; pageptr = indexbuf; fileptr = NULL; _fmemset(indexbuf,0x0000,BUFFERSIZE); } int filebuf_open(char *filename, int strip) { if(!(*filename)) return FALSE; #ifdef WINDOWS { OFSTRUCT of; // open the file for read only, and allow others to read it (but not write) if((fh = OpenFile(filename,&of, OF_CANCEL | OF_PROMPT | OF_READ | OF_SHARE_DENY_WRITE)) == -1) { Message(WinSmooth,"Error %d opening %s",of.nErrCode,filename); return FALSE; } } #else if((fh = open(filename,O_BINARY | O_RDONLY)) == -1) error_exit(0,"Unable to open file"); #endif { struct stat s; if(fstat(fh,&s)) // get file size return FALSE; if(s.st_size > MAXFILESIZE) // check file size #ifdef WINDOWS { Message(WinSmooth,"%s is too large to be handled by WinSmooth", filename); return FALSE; } #else error_exit(0,"File too large"); #endif } filebuf_read(); // get first buffer from file { unsigned len; // read entire file for(len = 1; filebuf_nextline(&len) && len; ); } stripbits = (strip ? TRUE : FALSE); return TRUE; } // de-allocates filebuffers and cleans up void filebuf_destruct(void) { #if defined(WINDOWS) if(indexbufhdl) { GlobalUnWire(indexbufhdl); GlobalFree(indexbufhdl); } if(bufferhdl) { GlobalUnWire(bufferhdl); GlobalFree(bufferhdl); } indexbufhdl = bufferhdl = NULL; #else if(indexbuf) _ffree(indexbuf); if(bufstart) _ffree(bufstart); bufstart = NULL; indexbuf = NULL; #endif if(fh != -1) close(fh); fh = -1; } // reads buffer from file void filebuf_read(void) { unsigned bytes; lseek(fh,(HALFBUFFER*(pageptr-indexbuf)),SEEK_SET); _dos_read(fh,&bufstart[HALFBUFFER],HALFBUFFER,&bytes); filebuf_stripbits(&bufstart[HALFBUFFER],HALFBUFFER); fileptr = (!fileptr ? &bufstart[HALFBUFFER] : (fileptr -= HALFBUFFER)); bufend = &bufstart[HALFBUFFER+bytes]; // set to end of buffer *bufend = CTRL_Z; filebuf_updateindex((bytes != HALFBUFFER) ? TRUE : FALSE); } // updates line index for buffer void filebuf_updateindex(int endndx) { char far *buf = fileptr; char far *old = fileptr; unsigned far *ndx = lineptr; unsigned numlines = ((pageptr == indexbuf) ? 0 : pageptr[-1]); while(buf <= bufend) // until end of buffer switch(*buf) { case CR: // if CR or LF found case LF: buf++; // check next character if(*buf == LF) // if CR followed by line feed buf++; *ndx++ = (buf-old); // use ptr arithmetic to get length numlines++; old = buf; // set old to next line if(buf == bufend) buf++; break; // break so as not to fall thru case CTRL_Z: bufend = buf; // CTRL_Z found, set new buf end if(buf > old) // if line ends with CTRL_Z *ndx++ = 0; buf++; break; default: buf++; break; } *pageptr = numlines; // set to accumulated line count if(endndx) // if end of file reached *ndx = MYEOF; // set end of file marker in index } // read next buffer from file void filebuf_nextbuffer(void) { // copy the 2nd half of buffer to the 1st half _fmemcpy(bufstart,&bufstart[HALFBUFFER],HALFBUFFER); pageptr++; // set for next page // read HALFBUFFER bytes into 2nd half filebuf_read(); } // read previous buffer from file void filebuf_prevbuffer(void) { if(pageptr == indexbuf) // insure we're not on 1st page return; // copy 1st buffer to 2nd _fmemcpy(&bufstart[HALFBUFFER],bufstart,HALFBUFFER); fileptr += HALFBUFFER; // adjust pointer to its position in 2nd page pageptr--; // seek to page lseek(fh,(HALFBUFFER*(pageptr-indexbuf)),SEEK_SET); { unsigned bytes; _dos_read(fh,bufstart,HALFBUFFER,&bytes); // read the file into the 1st buffer filebuf_stripbits(bufstart,HALFBUFFER); } bufend = &bufstart[BUFFERSIZE]; // set to end of buffer *bufend = CTRL_Z; } // seek to a particular line void filebuf_seekline(unsigned line) { unsigned far *page = indexbuf, far *buf1, far *buf2, far *ndx; // find a page with a higher accum. line count for( ; line > *page && *page; page++); if(page == indexbuf) // if we're on the first page (no prev. page) { buf1 = page; // set for first 2 buffers buf2 = page+1; } else if(*(page+1) == 0) // or not on 1st page and next page is empty { buf1 = page-1; // set for this and prev buffer buf2 = page; } else // or we are between two real pages { if(line > (*(page-1) + ((*(page+1) - *(page-1)) / 2))) { buf1 = page; buf2 = page+1; } else { buf1 = page-1; buf2 = page; } } lineptr = indexstart+line; // set lineptr to the line pageptr = page; // set pageptr to the page if(*buf2) // if no 2nd page, nothing to read { unsigned bytes; // read 1st buffer lseek(fh,(HALFBUFFER*(buf1-indexbuf)),SEEK_SET); _dos_read(fh,bufstart,HALFBUFFER,&bytes); lseek(fh,(HALFBUFFER*(buf2-indexbuf)),SEEK_SET); _dos_read(fh,&bufstart[HALFBUFFER],HALFBUFFER,&bytes); filebuf_stripbits(bufstart,HALFBUFFER*2); bufend = &bufstart[HALFBUFFER+bytes]; // set to end of buffer *bufend = CTRL_Z; fileptr = (page == buf1) ? bufstart : &bufstart[HALFBUFFER]; } else fileptr = &bufstart[HALFBUFFER]; ndx = indexstart; ndx += ((pageptr == indexbuf) ? 0 : *(pageptr-1)); if(*buf2) if(fileptr != bufstart) { while(fileptr[-1] != CR && fileptr[-1] != LF) fileptr--; } else if(lineptr != indexstart) { ndx++; while(fileptr[1] != CR && fileptr[1] != LF) fileptr++; fileptr++; while(*fileptr == CR || *fileptr == LF) fileptr++; } for(; ndx < lineptr; fileptr += *ndx, ndx++) ; } // get next line from file char far *filebuf_nextline(unsigned *len) { char far *linestart; char far *bufptr; // local pointer *len = 0; if(*lineptr == MYEOF) return NULL; reset: linestart = fileptr; // set linestart to fileptr // read until buffer end encountered, or until CR found for( bufptr = fileptr; bufptr != bufend; bufptr++) if((*bufptr == CR) || (*bufptr == LF)) break; if(bufptr == bufend) // end of buffer, line carries over { filebuf_nextbuffer(); // read more of file goto reset; } *len = (bufptr-fileptr); // remove CR from buf fileptr += *lineptr++; return linestart; } // get previous line from file char far *filebuf_prevline(unsigned *len) { char far *bufptr; *len = 0; if(lineptr == indexstart) // if at beginning of file return NULL; --lineptr; // set to previous line bufptr = bufstart+HALFBUFFER; if((fileptr > bufptr) && ((fileptr - *lineptr) <= bufptr)) pageptr--; // set to prev page if((bufstart + *lineptr) > fileptr) // if line length extends from previous page filebuf_prevbuffer(); fileptr -= (*lineptr); for(bufptr = fileptr; bufptr != bufend; bufptr++) if((*bufptr == CR) || (*bufptr == LF)) break; *len = (bufptr - fileptr); return fileptr; } // returns number of lines line file unsigned filebuf_numlines(void) { unsigned far *page; for(page = indexbuf; *(page+1); page++) ; return *page; } // returns length of longest line in file unsigned filebuf_longestline(void) { unsigned far *ndx = indexstart; unsigned longest = 0; for( ; *ndx != MYEOF; ndx++) if(*ndx > longest) longest = *ndx; return longest; } // exit routine on fatal error void error_exit(int err, char *msg) { #if defined(WINDOWS) extern HWND WinSmooth; Message(WinSmooth,msg); #else printf("%s\n",msg); #endif exit(err); }