/*
 *  Copyright (c) 1992, 1994 John E. Davis  (davis@amy.tch.harvard.edu)
 *  All Rights Reserved.
 */
#include <stdio.h>
#include <string.h>
#include <limits.h>

#ifndef sequent
# ifdef unix
#  include <unistd.h>
# endif
# include <stdlib.h>
#endif

#ifdef VMS
#include <file.h>
#endif

#ifdef unix
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#ifdef SYSV
#include <sys/fcntl.h>
#endif
#endif

#ifdef msdos
#include <fcntl.h>
#include <io.h>
#include <sys/stat.h>
#endif

#ifdef __os2__
#include <fcntl.h>
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif
 

#include "config.h"
#include "jmalloc.h"
#include "vfile.h"
#ifdef JED
#include "misc.h"
#endif

#include "mem.h"

unsigned int VFile_Mode = VFILE_TEXT;

VFILE *vopen(char *file, unsigned int size, unsigned int fmode)
{
   int fd;
   unsigned int mode;

#ifdef O_BINARY
   mode = O_BINARY;
#else 
   mode = 0;
#endif
      
   if ((fd = open(file, mode | O_RDONLY, 0)) < 0) return(NULL);
   return vstream(fd, size, fmode);
}

void vclose(VFILE *v)
{
   close(v->fd);
   if (v->buf != NULL) FREE(v->buf);
   FREE(v);
}


VFILE *vstream(int fd, unsigned int size, unsigned int mode)
{
   VFILE *v;
   
   if (NULL == (v = (VFILE *) MALLOC(sizeof(VFILE)))) return(NULL);
   v->bmax = v->bp = v->buf = NULL;
   v->fd = fd;
   v->eof = NULL;
   v->size = size;
   if (mode == 0) mode = VFile_Mode;
   v->mode = mode;
   return v;
}

/* I malloc one extra so that I can always add a null character to last line */
char *vgets(VFILE *vp, unsigned int *num)
{
   register char *bp, *bp1;
   register char *bmax, *bpmax;
   char *neew;
   int fd = vp->fd;
   unsigned int n, max, fmode = vp->mode;
   int doread = 0;
   n = vp->size;
   
   *num = 0;
   if (NULL == vp->buf)
     {
#if defined (msdos) || defined (__os2_16__)
	if (!n) n = 512;
#else
	if (!n) n = 64 * 1024;
#endif
	
	if (NULL == (neew = (char *) MALLOC(n + 1)))
	  {
#ifdef JED
	     msg_error("Unable to malloc space.");
#endif
	     return(NULL);
	  }
	
	vp->bp = vp->buf = neew;
	vp->bmax = neew + n;
	doread = 1;
     }

   bp = vp->bp;
   if ((vp->eof != NULL) && (bp >= vp->eof)) return (NULL);
   bp1 = vp->buf;
   bmax = vp->bmax;
   
   while (1)
     {	
	if (doread)
	  {
	     max = (int) (vp->bmax - bp);
	     while ((n = read(fd, bp, max)) != 0)
	       {
		  max -= n;
		  bp += n;
	       }
	     if (max) vp->eof = bp;
	     if (bp == bp1)
	       {
		  return(NULL);
	       }
	     bp = bp1;
	  }
	else bp1 = bp;
	   
	/* extract a line */
	if (vp->eof != NULL) bmax = vp->eof;
	
	n = (unsigned int) (bmax - bp);
#ifdef msdos
	if (n)
	  {
	     bpmax = bp;
	     asm  {
		mov bx, di
		mov al, 10
		mov cx, n
		les di, bpmax
		cld
		repne scasb
		inc cx
		sub n, cx
		mov di, bx
	     }
	     bp += n;
	     if (*bp != '\n') bp++;
	  }
	
	if (bp < bmax)
	  {
	     vp->bp = ++bp;
	     *num = (unsigned int) (bp - bp1);
	     
	     /* if it is text, replace the carriage return by a newline
	        and adjust the number read by 1 */
	     bp -= 2;
	     if ((fmode == VFILE_TEXT) && (*num > 1) && (*bp == '\r'))
	       {
		  *bp = '\n';
		  *num -= 1;
	       }
	     return bp1;
	  }
#else
	if (NULL != (bpmax = MEMCHR(bp, '\n', n)))
	  {
	     bpmax++;
	     vp->bp = bpmax;
	     *num = (unsigned int) (bpmax - bp1);
	     
	     if ((fmode == VFILE_TEXT) && (*num > 1))
	       {
		  bpmax -= 2;
		  if (*bpmax == '\r') 
		    {
		       *bpmax = '\n'; (*num)--;
		    }
	       }
	     return bp1;
	  }
	bp = bp + n;
#endif	/* msdos */
	if (vp->eof != NULL)
	  {
	     *num = (unsigned int) (bp - bp1);
	     vp->bp = bp;
	     
	     
#if defined(pc_system) || defined(__os2__)
	     /* kill ^Z at EOF if present */
	     if ((fmode == VFILE_TEXT) && (*num) && (26 == *(bp - 1))) 
	       {
		  *num -= 1;
		  if (!*num) bp1 = NULL;
	       }
#endif
	     return(bp1);
	  }
	
	doread = 1;

	bp = bp1;
	bp1 = vp->buf;
	if (bp != bp1)
	  {
	     /* shift to beginning */
	     while (bp < bmax) *bp1++ = *bp++;
	     bp = bp1;
	     bp1 = vp->buf;
	  }
	else 
	  {
	     bp = bmax;
	     vp->bmax += 2 * (int) (vp->bmax - vp->buf);
	     neew = (char *) REALLOC(vp->buf, vp->bmax - vp->buf + 1);
	     if (neew == NULL) 
	       {
#ifdef JED
		  msg_error("Realloc Error.");
#endif

		  return(NULL);
	       }
	     
	     bp = neew + (int) (bmax - vp->buf);
	     bmax = vp->bmax = neew + (int) (vp->bmax - vp->buf);
	     bp1 = vp->buf = neew;
	  }
     }
}

